diff options
| author | Luke Hoersten <[email protected]> | 2026-07-28 17:38:06 -0500 |
|---|---|---|
| committer | Luke Hoersten <[email protected]> | 2026-07-28 17:38:06 -0500 |
| commit | 29943e536f2be420a9d4042cd26d7b7eddf71438 (patch) | |
| tree | 3083eafc75877243777a8257dd4a46e88ee8a3cf /src/main.rs | |
| parent | 28a660e8bacac84523601f67547b2671058d9b20 (diff) | |
Address remaining audit findings: input hardening and cleanup
Security:
- SNTP replies must echo a random nonce in their originate timestamp, and
unsynchronized (LI=3) or invalid-stratum servers are rejected, so an
off-path spoofer cannot defeat the clock-trust gate
- The storage directory is created 0700 but an existing one is tightened
only when it is provably ours (holds our artifacts or is empty); a
misconfigured path like /tmp under root is left alone with a warning
- Device-supplied vendor/product names are stripped of control characters
at ingestion so a device cannot inject terminal escapes
Cleanup:
- ensure_fabric splits into decision logic plus bootstrap_fabric
- read_device_names merges the two name readers; a From impl replaces the
hand-mapped IdentityReport; run_sync uses map/transpose with an extracted
manual_instant; pick_interface runs once; print_json escapes its fallback;
device_name dedups the vendor/product join; identity_status ignores
leftover .tmp blobs; dead parameters and repeated random-id code removed
36 unit tests, clippy clean.
Diffstat (limited to 'src/main.rs')
| -rw-r--r-- | src/main.rs | 52 |
1 files changed, 19 insertions, 33 deletions
diff --git a/src/main.rs b/src/main.rs index e36ad21..16fe2df 100644 --- a/src/main.rs +++ b/src/main.rs @@ -48,9 +48,7 @@ use clap::{Parser, Subcommand}; use jiff::Timestamp; use crate::config::{Config, DEFAULT_CONFIG_PATH}; -use crate::output::{ - ConfigReport, DstTransition, IdentityReport, NodeListing, Output, StatusReport, SyncReport, -}; +use crate::output::{ConfigReport, DstTransition, NodeListing, Output, StatusReport, SyncReport}; use crate::state::{NodeRegistry, ServiceState}; use crate::time::MatterMicros; @@ -260,20 +258,7 @@ fn run_status(config: &Config, filter: Filter) -> anyhow::Result<Output> { }) .collect(); - let identity = match controller::identity_status(&config.storage_path) { - controller::IdentityStatus::NotCreated => IdentityReport::NotCreated, - controller::IdentityStatus::Created { - fabric_id, - controller_node_id, - } => IdentityReport::Created { - fabric_id: fabric_id.to_string(), - controller_node_id: controller_node_id.to_string(), - }, - controller::IdentityStatus::Unreadable => IdentityReport::Unreadable, - controller::IdentityStatus::Inconsistent(reason) => IdentityReport::Inconsistent { - reason: reason.to_string(), - }, - }; + let identity = controller::identity_status(&config.storage_path).into(); let offset_seconds = tz.to_offset(now).seconds(); Ok(Output::Status(Box::new(StatusReport { config: ConfigReport { @@ -310,23 +295,24 @@ fn run_inspect(config: &Config, target: Target) -> anyhow::Result<Output> { Ok(Output::Inspection { nodes }) } +/// The requested wall-clock time as a concrete instant: today's date in the +/// configured zone at that time. The operator is asserting the wall clock, so +/// the result feeds `--time` directly. +fn manual_instant(config: &Config, raw: &str) -> anyhow::Result<MatterMicros> { + let wall = crate::time::parse_wall_clock(raw).map_err(|e| anyhow::anyhow!(e))?; + let tz = config.time_zone(); + let today = Timestamp::now().to_zoned(tz.clone()).date(); + let instant = today + .at(wall.hour(), wall.minute(), wall.second(), 0) + .to_zoned(tz) + .map_err(|e| anyhow::anyhow!("cannot place {raw:?} in {}: {e}", config.timezone))? + .timestamp(); + Ok(MatterMicros::from_timestamp(instant)) +} + fn run_sync(config: &Config, target: Target, time: Option<&str>) -> anyhow::Result<Output> { - // --time makes the operator the time source: compute today's date in - // the configured zone at the requested wall-clock time. - let manual_time = match time { - None => None, - Some(raw) => { - let wall = crate::time::parse_wall_clock(raw).map_err(|e| anyhow::anyhow!(e))?; - let tz = config.time_zone(); - let today = Timestamp::now().to_zoned(tz.clone()).date(); - let instant = today - .at(wall.hour(), wall.minute(), wall.second(), 0) - .to_zoned(tz) - .map_err(|e| anyhow::anyhow!("cannot place {raw:?} in {}: {e}", config.timezone))? - .timestamp(); - Some(MatterMicros::from_timestamp(instant)) - } - }; + // --time makes the operator the time source. + let manual_time = time.map(|raw| manual_instant(config, raw)).transpose()?; // Fail safe: never push time from a clock that is not NTP-disciplined. // Not enforced with --time: the operator deliberately chose the value. |
