Repository navigation
fix(harness): catch secrets by shape in the kit's one cleaner (TH-9500) - #308
Open
khushalsonawat wants to merge 2 commits into
Open
khushalsonawat wants to merge 2 commits into
khushalsonawat wants to merge 2 commits into
Conversation
…t, outbound and the scenario send TH-9491 cleans a URL's userinfo, query and fragment. This adds what it can't see, in three steps of agent._redact (secret_shapes.py): 1. on the raw value: rule 1 (a password broken by whitespace, ?, # or /), rule 1b (a password or user holding @ before the password's /) and rule 2 (a query after a break); a value that is one URL joins first; 2. TH-9491's URL step, unchanged; 3. key patterns (GitHub, OpenAI, Anthropic, AWS, Slack, Stripe, Google and others), JWTs, PEM key blocks, Telegram bot URLs, and named values with guards for honest text. The chat stream's seam (ChatHold) never cuts inside a match, so every split streams what cleaning the whole reply stores; before prose, past N, it cuts only at a line break and holds while a URL's userinfo or its own :// is still open. Outbound runs the full cleaner. The scenario send builds its persona record from cleaned fields; the session's activity line is cleaned before its cut. The reviewers' offset sweeps are tests: every case streams equal to the stored message, with no part of its secret in any event. Fake provider keys in tests are split literals. Refs: TH-9500 Signed-off-by: khushalsonawat <khushalsonawat@gmail.com>
…smoke Both files are touched by this change and pass whole on the base (c0e51b7: 235 and 43 passed on Python 3.12.3). Refs: TH-9500 Signed-off-by: khushalsonawat <khushalsonawat@gmail.com>
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
TH-9491 (#278) cleans a URL's userinfo, query and fragment. It can't catch a secret that isn't inside a URL, or a password split by a space, a line break,
/,?,#or@. This adds those shapes to the kit's one cleaner (providers/agent.py_redact), in three steps (providers/secret_shapes.py):?,#or/.@before the password's/. This covers Dileep's [TH-9491] fix: keep URL credentials in an agent's description out of what the kit sends and stores #278 inputs (postgres://admin:Xk9/Qp+Zr8@db…,https://user:pa#ss@host…) and Ash's (postgres://admin:P@ss/Xk9@db…,smtp://me@gmail.com:app/pass@smtp…).password=,token:,"api_key": "…"), with guards for honest text.Where it runs:
ChatHold,_DeltaCleaner) never cuts inside a match, so every split streams what cleaning the whole reply stores.://, is still open.Verification
Python 3.12.3, one run after the last change, locally under a memory cap;
test_secret_shapesand chat also ran on 3.11. CI (sdk-smoke.yml) runstest_builder_chat.py,test_scenario_source_provision_agent.py,test_reading_entry.py,test_hosted_entrypoint.pyandtests/providers(sotest_secret_shapes.pyandtest_providers_agent_build.py).Results:
tests/providers/test_secret_shapes.py: 235 passed. It includes speed budgets on 5 MiB crafted shapes, and rule 1b is held to at most 2× the cleaner without it.tests/harness/test_builder_chat.py: 393 passed. The reviewers' line-break sweeps are tests here, and every case streams equal to the stored message, with no part of its secret in any event.test_outbound.py: 242 passed, andtest_contract_writer.py: 43, locally. Both pass whole on the base (c0e51b7: 235 and 43), andc62d03caadds them tosdk-smoke.yml. Its fix-only check (one reviewer) is clear, with no findings: on Python 3.12.14 the two files give 285 passed.test_scenario_source_provision_agent.py: 48 (in CI).test_scenario_source.py: 115, locally; this PR doesn't change that file.test_providers_agent_build.py: 86 passed and 41 skipped locally (nohypothesis). CI runs the property tests.Failures that are the same on the base:
tests/test_harness_sandbox_server.py: 2 GitHub tests (test_public_github_branch_url_populates_job_ref,test_private_github_submission_becomes_hosted_job).test_hosted_entrypoint.py(livekit isn't installed locally).Planted bugs: 57 planted, 52 caught. The 5 that survive change nothing, or are constant-factor slowdowns. Every review fix is pinned by a row that fails without it.
Review:
Known limits
Passwords and hosts the rules can't read
%40, or a?or#after the password's@.://can stream in chat before prose. The stored message is clean.text@host(https://u:p@api.example.com/a@b.com→https://b.com), the same for/react@18and/repo@main, the scp form, and a port rule 1 doesn't read. The password is still removed.BROKERS=amqp://rabbitmq,amqp://b@other.example→amqp://other.example.Chat before prose, past N (4,096 characters)
[redacted]:@line before each URL;mailto:or bare hash lines;/@path;://…, then more than about 12 K of glued URLs.=and its value on three separate lines can stream lower-cased.Speed (crafted input only, linear, under 5 s)
a://u@takes 1.03 s on the base already._url_aroundtakes 1.6–1.8 s on 5 MiB of repeated named-value hosts.Not cleaned here (recorded for follow-up)
platform.py'spersona_ofcallers) sends persona records uncleaned.jdbc:prefix and a line break inside://.Checklist