fix(#34): paste-flow bearer-URL log + fnox set via stdin#40
fix(#34): paste-flow bearer-URL log + fnox set via stdin#40bglusman wants to merge 1 commit intofeat/secret-paste-web-uifrom
Conversation
Two LEGIT-FIX items from the PR #34 triage: 1. zeroclawed-secret-paste/src/lib.rs:151 — full paste URL (which contains the one-shot bearer token) was logged at info!, landing in shared logs / journalctl / shell history of any caller that captured stdout. Address-only at info!; URL gated to debug! (opt-in via RUST_LOG). 2. onecli-client/src/fnox_client.rs:174 — `fnox set <name> <value>` passed the value as argv, visible in `ps`/`/proc` to other users on shared hosts. Switched to stdin: argv is now `set <name> -` and the value flows over the child's stdin pipe. The argv-order test was rewritten to assert BOTH that the value is absent from argv AND that it arrives on stdin — an explicit guard against future regression.
There was a problem hiding this comment.
Pull request overview
Improves secret-handling security in the workspace by reducing accidental leakage of bearer tokens and secret values via logs and process listings.
Changes:
- Adjust
zeroclawed-secret-pastelogging to avoid emitting the full one-shot URL (token) atinfo!, keeping it atdebug!only. - Update
FnoxClient::setto send secret values over stdin (fnox set <name> -) instead of argv. - Update
FnoxClienttests to assert the value is absent from argv and present on stdin.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
| crates/zeroclawed-secret-paste/src/lib.rs | Prevents leaking one-shot bearer tokens via info! logs by logging only the bound address. |
| crates/onecli-client/src/fnox_client.rs | Prevents leaking secret values via process args by switching fnox set to stdin and strengthening tests. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| if let Some(mut stdin) = child.stdin.take() { | ||
| stdin | ||
| .write_all(value.as_bytes()) | ||
| .await | ||
| .map_err(FnoxError::NotInstalled)?; | ||
| // Drop closes stdin so fnox sees EOF. | ||
| } | ||
| let output = child | ||
| .wait_with_output() | ||
| .await | ||
| .map_err(FnoxError::NotInstalled)?; |
There was a problem hiding this comment.
write_all(..) and wait_with_output() I/O failures are currently mapped to FnoxError::NotInstalled, which will produce a misleading “install fnox” message for cases like broken pipe (fnox exits early due to bad args/version), permission errors, or other runtime I/O issues. Consider introducing a distinct error variant for subprocess I/O (or mapping these to FnoxError::Failed after capturing stderr) so operators get the real underlying failure reason.
| &format!( | ||
| r#"echo "$1 $2 $3" > {} ; cat > {} ; exit 0"#, | ||
| argv_log.display(), | ||
| stdin_log.display() | ||
| ), |
There was a problem hiding this comment.
The fake-fnox shell snippet writes logs using unquoted paths from argv_log.display()/stdin_log.display(). If the temp dir path contains spaces or shell-significant characters, the test can fail unexpectedly. Quote/escape the paths in the generated script to make the test robust.
…ning (#44) Squash-merge of integration/super-combined — 4 weeks of feature work + cross-PR security fixes + codex agent's hardening, all green CI (14/14 checks). ## Features landing - **fnox secret-resolver integration** (#15) + FnoxClient subprocess wrapper (#21) - **Adversarial commit-reviewer + mechanical pre-commit gate** (#18) - **{{secret:NAME}} substitution engine** in security-proxy URL/headers/body (#19) - **Per-secret destination allowlist** (#22) — RFC §11.1 attack defense - **!secure chat commands** (set/list) on Telegram (#20), Matrix (#28), WhatsApp (#31) - **zeroclawed-mcp** scaffold — agent-facing secret discovery server (#23) - **install.sh wires MCP** into Claude Code agent configs (#26) - **zeroclawed-secret-paste** — localhost web UI for one-shot secret input (#34) - **Bulk paste UI** — .env-style multi-secret onboarding with per-line results - **LAN-friendly defaults** — bind 0.0.0.0 + RFC 1918 Origin acceptance - **WhatsApp HMAC verification** (was always-true placeholder before — codex hardening) ## Security fixes folded in - /vault/:secret bearer auth + 127.0.0.1 default bind (#39) - URL-embedded secrets honor destination allowlist (#41) - Paste-flow: bearer URL only at debug, fnox set via stdin not argv (#40) - Paste-flow: graceful shutdown, exit-on-submit, reject Origin: null (#43) - Subprocess timeouts + kill_on_drop on FnoxClient - BrokenPipe-tolerant stdin write (Linux CI surface) - Header-value log redaction - OneCLI bound to 127.0.0.1 by default - Sanitized real API token + Telegram IDs from sample configs (#36) ## Architecture / refactors - Consolidated onecli binary into security-proxy (#17) - Hardcoded vault URL removed from onecli-client - security-proxy resolver wired into hot path - Extracted build_app router; migrated /vault/:secret route - !secure parser uses split_whitespace (was splitn), audit-logs invocations ## Test coverage added - security-proxy substitution engine + body/headers tests - onecli-client retry + Http(_) variant + adversarial fallthrough suite - onecli-client client.rs rewritten from tautologies to wiremock-backed - config/validator coverage (was zero, now 290-line module covered) - 16 zeroclawed-secret-paste tests including bulk-mode cases ## Docs / RFCs - agent-secret-gateway holistic architecture - consolidation-findings (what #28 must address) - secret-input-web-ui RFC (input-only, new-by-default) - browser-harness integration spike - test-quality-audit Round 1+2+3 (host-agent + zeroclawed priority files) ## Codex agent's hardening cherry-picks - Subprocess timeouts on fnox calls - map_spawn_error helper - Validator hardening + atomic-counter digest race fix - WhatsApp HMAC implementation + tests - proxy header-value log redaction CI: all 14 checks green at squash time. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
|
Subsumed by #44 (squashed to |
…ning (#44) Squash-merge of integration/super-combined — 4 weeks of feature work + cross-PR security fixes + codex agent's hardening, all green CI (14/14 checks). ## Features landing - **fnox secret-resolver integration** (#15) + FnoxClient subprocess wrapper (#21) - **Adversarial commit-reviewer + mechanical pre-commit gate** (#18) - **{{secret:NAME}} substitution engine** in security-proxy URL/headers/body (#19) - **Per-secret destination allowlist** (#22) — RFC §11.1 attack defense - **!secure chat commands** (set/list) on Telegram (#20), Matrix (#28), WhatsApp (#31) - **zeroclawed-mcp** scaffold — agent-facing secret discovery server (#23) - **install.sh wires MCP** into Claude Code agent configs (#26) - **zeroclawed-secret-paste** — localhost web UI for one-shot secret input (#34) - **Bulk paste UI** — .env-style multi-secret onboarding with per-line results - **LAN-friendly defaults** — bind 0.0.0.0 + RFC 1918 Origin acceptance - **WhatsApp HMAC verification** (was always-true placeholder before — codex hardening) ## Security fixes folded in - /vault/:secret bearer auth + 127.0.0.1 default bind (#39) - URL-embedded secrets honor destination allowlist (#41) - Paste-flow: bearer URL only at debug, fnox set via stdin not argv (#40) - Paste-flow: graceful shutdown, exit-on-submit, reject Origin: null (#43) - Subprocess timeouts + kill_on_drop on FnoxClient - BrokenPipe-tolerant stdin write (Linux CI surface) - Header-value log redaction - OneCLI bound to 127.0.0.1 by default - Sanitized real API token + Telegram IDs from sample configs (#36) ## Architecture / refactors - Consolidated onecli binary into security-proxy (#17) - Hardcoded vault URL removed from onecli-client - security-proxy resolver wired into hot path - Extracted build_app router; migrated /vault/:secret route - !secure parser uses split_whitespace (was splitn), audit-logs invocations ## Test coverage added - security-proxy substitution engine + body/headers tests - onecli-client retry + Http(_) variant + adversarial fallthrough suite - onecli-client client.rs rewritten from tautologies to wiremock-backed - config/validator coverage (was zero, now 290-line module covered) - 16 zeroclawed-secret-paste tests including bulk-mode cases ## Docs / RFCs - agent-secret-gateway holistic architecture - consolidation-findings (what #28 must address) - secret-input-web-ui RFC (input-only, new-by-default) - browser-harness integration spike - test-quality-audit Round 1+2+3 (host-agent + zeroclawed priority files) ## Codex agent's hardening cherry-picks - Subprocess timeouts on fnox calls - map_spawn_error helper - Validator hardening + atomic-counter digest race fix - WhatsApp HMAC implementation + tests - proxy header-value log redaction CI: all 14 checks green at squash time. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Summary
Two LEGIT-FIX items from the PR #34 review triage:
info!, landing in shared logs / journalctl / shell history of any caller that captures stdout. Address-only atinfo!; URL gated todebug!(opt-in viaRUST_LOG).fnox set <name> <value>passed the value as argv, visible inps//procto other users on shared hosts. Switched to stdin: argv is nowset <name> -and the value flows over the child's stdin pipe.Test plan
cargo test -p onecli-client --lib fnox_client— 11 pass; argv-order test rewritten asset_uses_stdin_for_value_not_argvasserting BOTH absence from argv AND presence on stdincargo test -p zeroclawed-secret-paste --lib— all 7 passNotes
PR #34 still has other LEGIT-FIX items (no graceful shutdown, CLI doesn't exit on submit, Origin: null acceptance) — leaving those for a follow-up to keep the diff focused on the two highest-impact secret-leak fixes.
🤖 Generated with Claude Code