Commit Graph

18 Commits

Author SHA1 Message Date
rcourtman a39935182d Parse smartctl 7.5 power_mode object on guarded probes
smartmontools 7.5 emits power_mode as an {ata_value, name} object whenever
the -n guard runs CHECK POWER MODE, which is every rotational-disk probe.
The parser declared the field as a string, so json.Unmarshal failed for the
whole document and healthy spinning disks degraded to the lossy text
fallback, surfacing as no usable SMART data while guard-free SSD probes
kept working. That is the exact rotational-only failure split in the
discussion 1690 debug log (SAS3224 HBA, smartctl 7.5). Decode both shapes
the field has used, key standby on the reported name, and never fail the
document over this field. Also broaden the text fallback standby match to
the EPC names (STANDBY_Y, STANDBY (OS), SLEEP) that the mode-suffixed
match missed.

Refs #1690

Contract-Neutral: smartctl 7.5 power_mode JSON parse fix, internal decoder only, no DiskSMART payload or subsystem contract delta (discussion #1690)
2026-08-13 10:25:28 +01:00
rcourtman 9615b5f2b0 fix(security): close the four open CodeQL findings
Resolves every open code scanning alert on the repository. Dependabot and
secret scanning were already clear.

SMART temperature truncation (alerts 312, 313). parseRawValue returns a
64-bit raw attribute value, but DiskSMART.Temperature is an int, which is
32 bits wide on the 386 and arm release builds Pulse ships. The range check
ran after the narrowing conversion, so a raw value of 4294967316 truncated
to 20 and was published as a plausible 20 degree reading.
validSMARTTemperature64 now gates the conversion.

Provider MSP restore archive names (alert 314). cleanProviderMSPArchiveName
rejected a leading "../" but not a bare "..", which path.Clean produces from
entries such as ".." and "a/../..". pathIsInside caught the escape
downstream, so this was not exploitable, but the sanitizer now rejects it
outright instead of depending on a second gate.

TrueNAS device paths (alert 315). vdev.Device is supplied by the appliance,
concatenated into a path and published verbatim on ZFSDevice.Path, so values
like "//evil.example.com/share" and "/\evil.example.com" passed straight
through. devicePath now drops traversal segments and backslashes and
collapses a leading double slash. The alert's open-redirect framing does not
apply here, there is no redirect sink on this path, but the value is
untrusted input rendered as a path and is worth normalising.

Patrol readiness cache key (alert 311). The key is persisted to
ai_patrol_model_readiness.json and embedded an unkeyed SHA-256 of the Ollama
Basic Auth username and password. That password is chosen by a human, so
anyone holding the evidence file could recover it offline at two SHA-256
operations per guess. The fingerprint is now HMAC-SHA256 keyed with a
32-byte per-install salt stored beside the evidence at mode 600. Credential
rotation still invalidates the cache and the key still survives a restart.

Each fix carries a regression test confirmed to fail against the previous
implementation.

monitoring.md carries the one warranted contract refinement. It already
required SMART temperature selection to accept only plausible readings, and
that rule now states the width at which plausibility is decided.

Contract-Neutral: CodeQL security fixes with no public-contract delta and no payload change. monitoring.md carries the one warranted refinement (SMART plausibility decided at 64-bit width). Residual demands are inapplicable: ai-runtime readiness prose documents interruption semantics, not cache-key derivation, and the credential-invalidation contract is unchanged; cloud-paid and deployment-installability contracts never name archive-entry sanitisation; agent-lifecycle owns smartctl.go but its SMART temperature prose lives in the staged monitoring.md.
2026-08-06 18:11:51 +01:00
courtmanr@gmail.com ad054defaf fix outstanding integration and agent regressions 2026-07-29 22:10:04 +01:00
rcourtman 25b2df07a0 Classify SATA disks behind a SAS HBA as SATA
linuxBlockDeviceTransportEvidence tested sas_address before vendor. A SATA
disk attached to an LSI/mpt3sas HBA, which is the common Unraid and TrueNAS
layout, exposes sas_address on its scsi_device while reporting vendor ATA,
so it classified as SAS. linuxInferredSmartctlDeviceTypes then returned only
"scsi" and smartctlDeviceTypeMatchesTransport discarded the correct "-d sat"
hint, steering a direct-ATA disk back to the "-d scsi" probe that 6e93cb3b5
exists to avoid.

Vendor ATA is the SCSI layer's marker for a device reached through a SAT
translation layer, so it is the more specific signal and is now tested
first. A genuine SAS disk reports its own vendor and never ATA, so the SAS
arm is unaffected.

Regression coverage pins all three shapes (SATA behind an HBA, a genuine SAS
disk, and a direct SATA disk) plus the resulting probe hint, and was
verified against the old ordering: it reports transport "sas" and inferred
types [scsi] for the HBA case.
2026-07-24 23:01:02 +01:00
rcourtman 6e93cb3b5c Fix Unraid SMART probing and disk authority 2026-07-24 09:38:39 +01:00
rcourtman 6a527ba9f2 Fix Proxmox physical disk inventory continuity 2026-07-23 22:05:09 +01:00
rcourtman 59e6f25a65 Detect SAS transport and parse SCSI attributes in SMART collection
smartctl reports SAS drives with device protocol SCSI, so detectDiskType
fell through to its blanket sata default, and that non-empty type also
masked the text-output transport evidence the fallback parser had
already extracted. The wrong sata label then blocked the merge layer
from promoting the smartctl serial over the SAS transport address
Proxmox reports.

Classify SCSI-protocol devices via the scsi_transport_protocol
descriptor, let the text and sysfs refinements upgrade a generic scsi
label, and apply the legacy sata default only after all evidence is
exhausted. Parse the SCSI log-page fields (power-on hours, grown defect
count, endurance used) that SCSI drives report instead of an ATA
attribute table, and let agent-reported sas replace coarse hdd/ssd/sata
types during the disk merge.

Refs #1595

Contract-Neutral: behavioral fix: SAS transport detection and SCSI attribute parsing in host agent SMART collection; no public contract delta (#1595)
2026-07-21 17:36:14 +01:00
rcourtman b6a74576bc Integrate trust-gate reliability fixes 2026-07-20 16:03:29 +01:00
rcourtman 52ed884640 Prepare v6.1.0-rc.4 release 2026-07-19 15:52:42 +01:00
rcourtman f2b732721e Fix physical disks vanishing on wide nodes and standby-misreporting SSDs
Two root causes behind #1516's remaining reports:

- A node whose Proxmox disks/list query fails (PVE probes SMART per disk
  inside that call, so dozens of disks can exceed the API window) now
  falls back to the linked host agent's smartctl inventory instead of
  leaving the Physical Disks view empty. Each node also gets its own
  attempt window so one slow node no longer starves the rest of the
  cluster, and a poll that runs out of budget saves partial results.

- The -n standby probe guard is dropped for positively confirmed
  non-rotational devices in both the host agent and the node sensor
  wrapper. The guard exists to avoid spinning up sleeping HDDs; an SSD
  has nothing to spin up, and some SATA SSDs answer CHECK POWER MODE
  with a bogus standby state that permanently hid their temperature,
  attributes and history.

Refs #1516
2026-07-11 10:24:45 +01:00
rcourtman 92524e1c27 Harden CodeQL storage and integer boundaries 2026-07-09 17:37:08 +01:00
rcourtman 4b669ad9b5 Fix SAT SMART temperature collection
Refs #1471
2026-07-06 23:42:00 +01:00
rcourtman c511c935b3 fix(disks): ground SMART disk discovery in /sys/block so no physical disk vanishes
On the #1483 reporter's Proxmox node a SATA SSD (INTEL SSDSC2BW240A4) never
appeared in Pulse even though smartctl reads it fine, because the agent's
discovery and probing both had single points of failure:

- Discovery trusted smartctl --scan-open exclusively whenever it returned at
  least one target. The scan silently omits any device it fails to open at
  scan time (the failure is only a #-comment in its output), so one bad open
  hid a real disk while its neighbours were listed.
- A scan-provided -d type got exactly one probe attempt on Linux. A type whose
  full query (-i -A -H) fails or returns nothing dropped the disk silently.
- A probe yielding no usable SMART data dropped the disk entirely instead of
  reporting the identity the kernel can prove.

Fixes, all in the agent collector:

- Union discovery: the kernel block device list (/sys/block, lsblk fallback)
  is the ground truth for which disks exist; smartctl --scan-open only
  contributes device-type hints. Any physical disk the scan misses gets an
  untyped probe target.
- Untyped retry: a typed Linux probe that errors, yields no usable data, or
  fails to open (exit bit 2, distinct from -n standby,3's exit 3) retries with
  smartctl auto-detection before giving up. Multiplexed controller members
  (megaraid, cciss, ...) are exempt since dropping -d would probe the array.
- Identity-only fallback: when every probe fails but the disk exists in
  /sys/block with nonzero capacity, report device/model/serial/size with
  health UNKNOWN instead of hiding it. No SMART data is fabricated;
  multiplexed array paths and zero-capacity media are excluded.
- Exclusion follow-through: --disk-exclude now also matches the canonical
  post-refine name (nvme0n1), not just the scan label (nvme0).

Regression tests use smartctl/lsblk fixtures captured from PVE 9.1.9 hosts
and the issue report. Verified live on two PVE nodes: NVMe keyed by
namespace with byte-exact pvesh sizes, SATA disk reported with full identity.

Completes the #1483 fix started in bd20069c6 (namespace devpath, authoritative
sizes, merge downgrade guard). Closes #1483.
2026-06-10 15:14:34 +01:00
rcourtman 9fe769ecdb Parse OPNsense 'Current Drive Temperature' SMART output
Back-port the smartctl regex half of v5 fix 0c2de2938 to v6 (the
prerelease-aware CompareVersions half is already present). smartctl text
fallback now matches 'Current Drive Temperature:' in addition to
'Current Temperature:', so OPNsense/pfSense disk temperatures are read
instead of reported as missing. Adds a regression test.
2026-06-04 09:50:37 +01:00
rcourtman bd20069c60 fix(disks): report authoritative disk size and namespace devpath from the host agent
On a Proxmox node, physical disks collected by the host agent were keyed by
the NVMe controller (e.g. "nvme0 [nvme]") instead of the namespace, reported
sizeBytes 0 (or a stale filesystem-usage value), and flickered as the agent
reading intermittently replaced the authoritative Proxmox disks/list reading.

Root causes:
- smartctl --scan-open reports NVMe disks by their controller char device
  (/dev/nvme0), and that scan label became the reported devPath.
- DiskSMART carried no capacity, so the server backfilled size by matching the
  SMART device against host filesystem-usage entries, which never match a whole
  partitioned/LVM/ZFS disk, leaving size 0.
- The unified-resource merge let the agent's controller label overwrite the
  canonical Proxmox /dev/... devPath.

Fixes:
- The agent now reports the canonical block device (an NVMe controller resolves
  to its namespace) and the authoritative capacity from /sys/block, with the
  smartctl user_capacity / nvme_total_capacity as a cross-platform fallback.
  Disks behind multiplexing controllers (megaraid, cciss, areca) keep their
  disambiguating label and smartctl-reported size.
- SizeBytes flows through the agent report, host model, and adapter; the
  filesystem-usage match is demoted to a legacy fallback.
- The merge keeps a canonical /dev/<device> devPath and never downgrades it to
  a scan label, so an un-updated agent can no longer corrupt Proxmox data.

Refs #1483.
2026-05-29 19:55:53 +01:00
rcourtman 60d7db6ef9 Harden agentexec token binding and disk filtering 2026-04-23 15:54:48 +01:00
rcourtman 24fc9a019b Forward-port SMART collector hardening 2026-04-01 15:15:59 +01:00
rcourtman 778a2577b6 feat: Pulse v6 release 2026-03-18 16:06:30 +00:00