Skip to content

Windows port — Phase 3: disk health & SMART#9

Merged
ronylicha merged 8 commits into
masterfrom
feat/win-phase-3
Jun 26, 2026
Merged

Windows port — Phase 3: disk health & SMART#9
ronylicha merged 8 commits into
masterfrom
feat/win-phase-3

Conversation

@ronylicha

Copy link
Copy Markdown
Member

Phase 3 of the Windows port. Disk-health tab on Windows: disk list + uptime via sysinfo; SMART via smartctl read by the Phase-2 UAC-elevated executor (--smart mode); one-click winget install smartmontools when missing. Throughput stays 0 (PDH deferred, like macOS).

Security hardening (from adversarial review): trusted-absolute smartctl only (no PATH-planting EoP); CSPRNG random-nonce tokens for all elevated-IPC temp paths (closes TOCTOU + forged-report injection at the root; also hardens Phase-2 apply). Linux/macOS unchanged (cfg-gated). SMART runtime is manual-smoke (no smartctl/disk on CI).

rony added 6 commits June 26, 2026 21:03
…OCTOU + report-injection)

The elevated apply/smart IPC used the PID in predictable %TEMP% file names
(fyd-apply-<pid>-*, fyd-smart-<pid>-*). A local same-user attacker could
pre-create a junction at the predictable path to redirect the admin write
(CWE-377 TOCTOU) or substitute a forged SmartInfo report (spoof/XSS into the
frontend). Replace the PID with a CSPRNG nonce (getrandom, 20 digits, digit-only
to satisfy the child's token guard) so the path is unguessable — closing both
vectors at the root. Also: read_smart_elevated surfaces a report-write failure
(exit 1) instead of masking it; doc comments updated (pid -> token).

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: ab74c308f3

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src-tauri/src/headless.rs
let Some(name) = dev.get("name").and_then(|n| n.as_str()) else {
continue;
};
results.push(read_one_smart(&smartctl, name));

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Return SMART keys that match disk cards

On Windows this returns each SmartInfo keyed by the raw smartctl --scan-open name, but the UI builds smart[info.device] and renders only smart[disk.device], while the Windows disk list keys cards from sysinfo volume names/mount points (health.rs:208-213). When those identifiers differ, as with normal Windows drive-letter volumes versus smartctl device tokens, a successful SMART read is never attached to any disk card and the health tab hides the SMART data.

Useful? React with 👍 / 👎.

dirs.iter().any(|d| d.join(bin).is_file())
// On Windows, executables carry a .exe suffix.
#[cfg(target_os = "windows")]
let found = dirs.iter().any(|d| d.join(format!("{bin}.exe")).is_file());

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Align smartctl detection with the elevated resolver

For bin == "smartctl", this still searches every PATH directory, but the elevated reader intentionally refuses PATH and only accepts the two Program Files smartmontools locations (headless.rs:191-199). If a user has a portable/custom/stale smartctl.exe on PATH but not in those trusted locations, smart_deps_status() reports it installed and leaves missing empty, so the install prompt disappears while disk_smart() can only return no SMART data.

Useful? React with 👍 / 👎.

Comment thread src-tauri/src/headless.rs
let Some(name) = dev.get("name").and_then(|n| n.as_str()) else {
continue;
};
results.push(read_one_smart(&smartctl, name));

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve smartctl scan device types

When --scan-open finds a device that needs an explicit type, this drops the reported type and later calls only smartctl -a -j <name>. The smartctl man page says --scan reports device type/protocol, --scan-open may change that type through autodetection, and -d TYPE is how callers specify it, so USB/SAT/RAID/NVMe devices that scan successfully can still fail the subsequent read and show no Windows SMART data.

Useful? React with 👍 / 👎.

Comment thread src-tauri/src/execute.rs
// The elevated child self-discovers devices via `smartctl --scan-open`, so
// the caller's device list is unused. One UAC prompt; report read from file.
let token = elevation_token();
let report_path = std::env::temp_dir().join(format!("fyd-smart-{token}-report.json"));

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Use a report path shared across UAC accounts

This report path is derived from the unelevated user's %TEMP%, but read_smart_elevated derives its own path with std::env::temp_dir() after Start-Process -Verb RunAs. When UAC prompts for different admin credentials, the child runs under that admin account and writes to the admin temp directory, so the parent never sees the report and returns empty SMART despite a successful read; this affects standard Windows accounts.

Useful? React with 👍 / 👎.

rony added 2 commits June 26, 2026 22:17
…, disks comment

Doc/comment-only follow-ups from the Phase 3 final review (no logic change):
- correct stale 'parent PID' comments (token is now a random nonce) in
  execute.rs/headless.rs (the value is security-relevant — avoid misleading readers)
- add a SUPERSEDED security banner to the plan's Task 3 so its pre-hardening
  snippet (PATH fallback + PID token) is never copied verbatim (would reintroduce the EoP)
- health.rs: 'volumes (not physical disks)' instead of 'physical-ish'
The Windows clippy gate (-D warnings) failed: since Task 2 routes Windows SMART
installs through winget (not pkexec_install_deps), HELPER_PATH, resolve_helper_path
and pkexec_install_deps became dead code on Windows. Gate the first two to
cfg(not(windows)) (Linux+macOS still use them) and pkexec_install_deps to
cfg(linux) (macOS=brew, Windows=winget). Cross-platform execute_plan/user_zones
keep Path/PathBuf imports alive on Windows. (Local clippy runs on Linux where
these ARE used — only the Windows CI catches this, which is why it exists.)
@ronylicha ronylicha merged commit 9b2d476 into master Jun 26, 2026
6 checks passed
@ronylicha ronylicha deleted the feat/win-phase-3 branch June 26, 2026 20:26

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 70c1151d4c

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src-tauri/src/health.rs
device,
model: None,
size_bytes: disk.total_space(),
rotational: false,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Do not hard-code Windows disks as SSD

On Windows machines with an HDD, this forces rotational to false, and the health UI renders false as SSD, so every Windows HDD is mislabeled. sysinfo exposes disk kind information on Windows; otherwise this needs an unknown state instead of mapping unavailable data to SSD.

Useful? React with 👍 / 👎.

Comment thread src-tauri/src/execute.rs
let mut buf = [0u8; 8];
match getrandom::getrandom(&mut buf) {
Ok(()) => format!("{:020}", u64::from_le_bytes(buf)),
Err(_) => std::process::id().to_string(),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Fail closed instead of reusing predictable PID tokens

If the OS RNG returns an error, this falls back to the process ID for the temp-file token used by the Windows elevated --apply/--smart IPC. In that environment the path is predictable again, so a same-user attacker can pre-create the report path/junction or forge a report before the elevated process writes it; avoid launching elevation or return an error instead of continuing with a PID token.

Useful? React with 👍 / 👎.

Comment thread src-tauri/src/health.rs
Comment on lines +208 to +212
let name = disk.name().to_string_lossy().into_owned();
let device = if name.is_empty() {
disk.mount_point().to_string_lossy().into_owned()
} else {
name

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Keep Windows volume identifiers unique

When two Windows volumes have the same non-empty label, this uses that label from disk.name() as the device; the seen set below then treats them as the same disk and drops later volumes, so health_overview and SMART dependency status omit real drives. Use the mount point or volume GUID as the stable key and keep the label separate for display.

Useful? React with 👍 / 👎.

Comment thread src-tauri/src/execute.rs
Comment on lines +354 to +362
let out = Command::new("winget")
.args([
"install",
"--id",
"smartmontools.smartmontools",
"--accept-source-agreements",
"--accept-package-agreements",
"--silent",
])

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Require an exact winget package ID

Microsoft's winget install docs say --id only restricts which field is searched and --exact is needed to require an exact match. On systems with an added source or another ID containing this string, the one-click install can become ambiguous or select the wrong package despite the intended allowlist; add --exact/-e and preferably pin the source.

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant