Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion crates/uffs-cli/src/commands/system_status.rs
Original file line number Diff line number Diff line change
Expand Up @@ -384,6 +384,9 @@ fn find_mcp_stdio_processes() -> Vec<StdioSession> {
else {
return Vec::new();
};
// AUDIT-OK(bytes): per-line PID scan of process-list output; each line's
// PID parses-or-skips (fail-safe). Whole-buffer strict decode would drop
// the whole list on one bad byte. (WI-4.3 follow-up)
let text = String::from_utf8_lossy(&raw_output.stdout);
let my_pid = std::process::id();

Expand Down Expand Up @@ -475,7 +478,10 @@ fn resolve_parent_name(ppid: u32) -> Option<String> {
.stderr(std::process::Stdio::null())
.output()
.ok()?;
let name = String::from_utf8_lossy(&output.stdout).trim().to_owned();
// Strict decode: this process name is returned and used for a
// comparison/targeting decision, so invalid UTF-8 fails closed (None)
// rather than yielding a U+FFFD-mangled name. (WI-4.3 follow-up)
let name = core::str::from_utf8(&output.stdout).ok()?.trim().to_owned();
if name.is_empty() {
return None;
}
Expand Down Expand Up @@ -504,6 +510,8 @@ fn http_get_json(bind: &str, port: u16, path: &str) -> Result<serde_json::Value>
let mut response = Vec::new();
stream.read_to_end(&mut response)?;

// AUDIT-OK(bytes): HTTP probe response body split for display only, not a
// trust/targeting decision. (WI-4.3 follow-up)
let text = String::from_utf8_lossy(&response);
let body = text
.split_once("\r\n\r\n")
Expand Down
6 changes: 6 additions & 0 deletions crates/uffs-client/src/connect_sync_autostart.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,9 @@ fn is_process_alive(pid: u32) -> bool {
.stderr(std::process::Stdio::null())
.output()
.is_ok_and(|output| {
// AUDIT-OK(bytes): daemon-identity probe via substring match; a lossy
// decode can only FAIL the match → treat as 'not the daemon' (fail-safe
// reconnect), never a false positive. (WI-4.3 follow-up)
let text = String::from_utf8_lossy(&output.stdout);
// tasklist prints "uffsd.exe <PID> ..." when the process matches.
// Verify both the PID and the executable name.
Expand Down Expand Up @@ -113,6 +116,9 @@ fn is_daemon_process(pid: u32) -> bool {
.stderr(std::process::Stdio::null())
.output()
.is_ok_and(|output| {
// AUDIT-OK(bytes): daemon-identity probe via substring match; a lossy
// decode can only FAIL the match → treat as 'not the daemon' (fail-safe
// reconnect), never a false positive. (WI-4.3 follow-up)
let comm = String::from_utf8_lossy(&output.stdout);
// `ps -o comm=` prints the executable path or basename.
// Match if any path component is "uffsd".
Expand Down
2 changes: 2 additions & 0 deletions crates/uffs-client/src/mcp_pid.rs
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,8 @@ fn is_process_alive(pid: u32) -> bool {
std::process::Command::new("tasklist")
.args(["/FI", &format!("PID eq {pid}"), "/NH"])
.output()
// AUDIT-OK(bytes): tasklist substring PID check; lossy can only fail the
// match (fail-safe). (WI-4.3 follow-up)
.is_ok_and(|output| String::from_utf8_lossy(&output.stdout).contains(&pid.to_string()))
}
}
2 changes: 2 additions & 0 deletions crates/uffs-mcp/src/handler/definitions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -284,5 +284,7 @@ pub(crate) fn percent_decode_path(encoded: &str) -> String {
}
idx += 1;
}
// AUDIT-OK(bytes): final decode of a locally-assembled buffer for display.
// (WI-4.3 follow-up)
String::from_utf8_lossy(&decoded).into_owned()
}
5 changes: 5 additions & 0 deletions crates/uffs-mft/src/platform/system.rs
Original file line number Diff line number Diff line change
Expand Up @@ -734,11 +734,16 @@ fn query_memory_macos() -> Option<SystemMemory> {
.arg("hw.memsize")
.output()
.ok()?;
// AUDIT-OK(bytes): sysctl memsize for a stats display; the following
// .parse().ok()? already fails closed on any non-numeric/garbage byte.
// (WI-4.3 follow-up)
let total_str = String::from_utf8_lossy(&total_out.stdout);
let total_bytes: u64 = total_str.trim().parse().ok()?;

// Available: vm_stat → parse "Pages free" and "Pages inactive"
let vm_out = Command::new("vm_stat").output().ok()?;
// AUDIT-OK(bytes): vm_stat output parsed line-by-line for a stats display;
// each field parse fails closed. (WI-4.3 follow-up)
let vm_str = String::from_utf8_lossy(&vm_out.stdout);

// First line: "Mach Virtual Memory Statistics: (page size of 16384 bytes)"
Expand Down
Loading