Repository navigation
fix(brain): redact plaintext credentials from provider tool context - #573
Conversation
vladimirrott
left a comment
There was a problem hiding this comment.
Reviewed at 9447e552. One blocking item: the redactor removes host facts that are not secrets, in one case every line after the match.
Screening before and after normalisation is the right order, since a zero-width joiner can hide a key from the first pass, and pushing the cap after redaction means truncation can never split a PEM block into an unredacted tail. The integration test that records what the provider receives on turns 2 and 3 is the test #552 asked for.
I ran the sanitize_tool_output you ship over a few host facts, from a small example binary inside a --network=none container. I did not build this branch on my machine. Output, envelope included:
[query_package_repos] deb http://sg.archive.ubuntu.com/ubuntu jammy main
-> deb http://<redacted> jammy main
[query_environment] HF_HOME=/root/.cache/huggingface
-> <redacted>
[query_logs] Oct 07 sshd[812]: Unable to load host key: /etc/ssh/ssh_host_rsa_key
-> Oct 07 sshd[812]: Unable to load host key: <redacted>
[query_logs] Oct 07 app: password="
Oct 07 sshd[900]: Accepted publickey for root from 203.0.113.9
Oct 07 sudo: root : COMMAND=/bin/sh
-> Oct 07 app: password="<redacted>
[query_processes] mysqldump --password hunter2 mydb
-> mysqldump --password hunter2 mydb
Blocking: an unterminated quote erases the rest of the output. value_range returns (start, raw.len()) when the closing quote never comes, so one log line with password=" and no closing quote takes every line after it, here an accepted root login and a sudo command. Any local process can write such a line to the journal, and from then on the planner reads nothing that follows it. Stopping an unquoted or unterminated value at the end of the line would keep the safety you wanted (no exposed tail) without that reach.
Fixing that one unblocks the PR. The other rows are worth a look while you're in there:
sg.on its own counts as a SendGrid prefix, so the Singapore Ubuntu mirror disappears fromquery_package_repos. SendGrid keys areSG.<22>.<43>; requiring that shape fixes it.hf_andnpm_have the same looseness, which is howHF_HOME=...vanishes whole, key name included.key:followed by a path counts as an assignment, which costs the sshd line its file name.--password hunter2(space, no=) passes untouched, and #552 namesquery_processesas a source.
Your count is consistent: three new tests, 1936 → 1939 in the evidence file and the three docs. #587 also edits crates/sysknife-brain/tests/planner.rs and moves the count, so whichever lands second needs a rebase and a regenerated baseline. That part is mechanical.
9447e55 to
b49e394
Compare
vladimirrott
left a comment
There was a problem hiding this comment.
Approving at b49e3940.
You fixed every row from my table, and each fix is narrower than the bug: value_range stops a quoted value at \r or \n, SG., hf_ and npm_ now need their real lengths, the path exception applies to the bare key label and no other, and --password hunter2 gets caught by the -- check. tool_output_unterminated_quotes_preserve_following_log_lines runs both quote kinds against both line endings, plus the empty and escaped-backslash values, which covers more than the one line I sent you.
My merge gate ran that test in a container, then again with the newline stop removed (if matches!(ch, '\r' | '\n') { replaced by if false {):
running 'tool_output_unterminated_quotes_preserve_following_log_lines' unmutated
rust suite: evidence of 1 executed unit(s), passing unmutated
applying the mutation and re-running
receipt recorded for #573 at b49e3940 (observed: clean pass, mutated fail)
Your count holds after the rebase: six new tests (five #[test], one #[tokio::test]) and 1943 → 1949 in the evidence file and all three docs. I did not build this branch on my machine.
Nothing blocks on the code. #571 and #587 also move the test count from 1943, so this one will need a rebase and a regenerated baseline once whichever of them lands first. That part is mechanical and not yours to apologise for. When you push it, I'll merge without a new round of review provided four things hold: the push changes no file under crates/**/src, apps/*/src, packaging/ or .github/; this same mutation still turns the test red on the new head; the board is fully green; and my approval is still on the PR.
…utput * origin/main: fix(audit): chain SQLite status transitions and expose verdicts (lacs-project#571) fix(security): cover setup provider credential formats (lacs-project#578) # Conflicts: # README.md # docs/distro-support.md # docs/introduction.md # tests/evidence/workspace-tests.json
|
Merged current |
The merge with main kept main's 1,952, which left out the six tests this branch adds, so test_baseline saw 1,958 and failed. Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_01UpyrhbDTW1gBwzGAC44GYU
vladimirrott
left a comment
There was a problem hiding this comment.
Approving again at 2b7d081. Since my review at b49e394 the branch only picked up main and the test count. The merge with main kept main's 1,952, so I pushed 1,958 (1,943 at your base, plus 9 from #571 and #578, plus your 6). Your unterminated-quote test still fails with the newline check removed, same as before.
Main now carries 1,958 after lacs-project#573, and this branch adds four tests. Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_01UpyrhbDTW1gBwzGAC44GYU
* fix(brain): narrow intent credential admission * fix(brain): refuse flagged and punctuated credential values * test: count #587's four tests on top of main (1,962) Main now carries 1,958 after #573, and this branch adds four tests. Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_01UpyrhbDTW1gBwzGAC44GYU --------- Co-authored-by: hushen <[email protected]> Co-authored-by: Vladimir Rotariu <[email protected]> Co-authored-by: Claude Opus 5.5 <[email protected]>
Entries for #570, #571, #572, #573, #575, #576, #577, #578, #579, #584, #586, #587, #589, #592, #594 and #603, plus the dependency bumps in #595, #596, #597, #599 and #600. Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_01UpyrhbDTW1gBwzGAC44GYU
Summary
Read tools could forward journal passwords and bearer tokens to an LLM provider and retain them for later turns. Redact plaintext credential assignments, Bearer values, recognizable provider-key shapes, and private-key blocks before normalization and truncation; screen normalized content and retained provider context too.
An unterminated quoted assignment now stops at the line boundary, preserving subsequent accepted-login and sudo records. Empty assignment, command-line option, and Bearer values also preserve later log lines. Require the SendGrid
SG.<22>.<43>, Hugging Face, and npm token formats, retain generickey:file paths and ordinary environment/cache values, and redact spaced credential options such asmysqldump --password hunter2 mydbwithout losing neighboring arguments.Related Issue
Closes #552.
Validation
Ubuntu validation passed: cargo fmt --all -- --check; UPDATE_TEST_BASELINE=1 bash scripts/test_baseline.sh (1,949 passed, six configured skips); cargo clippy --workspace --all-features --all-targets --locked -- -D warnings; cargo doc --workspace --all-features --no-deps --locked; evidence/public-claim and secret screens; changed-document markdownlint. The generated baseline and all three published counts are synchronized. Three isolated review regression tests passed after the original redactor failed all three.
Full scripts/ci-local.sh --no-postgres completed as FAIL/INCOMPLETE: its sole failing check was action-pin verification because remote gh is missing. Rust (including docs with RUSTDOCFLAGS=-D warnings), frontend (72 tests), every release/E2E regression invoked by the board, configured Markdown lint, and YAML lint passed. The privileged action-steps assertions, markdown-link-check, ShellCheck, cargo-audit, and live PostgreSQL contract were skipped due to the local environment or --no-postgres. GitHub CI on the updated head passed all executed checks, including the PostgreSQL contract and privileged scripts checks.
Notes for Reviewers
No dependency or prompt changes. This is a plaintext pattern screen: unlabelled passwords, encoded credentials, and unknown formats can still reach a provider. It is independent of intent admission in #543. Rebased onto main to resolve the count conflicts; tests cover normalization, quoted values, truncation, private-key blocks, provider shapes, benign facts, and subsequent provider context.
Assisted by OpenAI Codex; validation figures come from actual runs.