Repository navigation
fix(checks): publish secret and injection findings, widen redaction - #1077
pengfei-threemoonslab wants to merge 4 commits into
Conversation
…ry secret shape SHIP-DOC-SECRET-IN-DESCRIPTION and SHIP-DOC-INJECTION-RISK are now published as visible findings, in report.json, report.md, SARIF and the packet, instead of being dropped into an evidence gap, and follow the same publication rule as SHIP-N8N-SECRET-IN-WORKFLOW-PARAMETER. Their support stays policy-ineligible, so they never block, never become named review items and change no exit code; the gap beside them now leads with the check's catalog description, as every finding-derived gap does. The shared redaction rules (version 0.2) add Basic credentials, PEM private-key blocks, Google AIza keys, unlabeled AWS secret access keys and authorization/auth labels, reusing the host readers' auth-scheme alternation, and sanitize_model now redacts the extra fields of extra="allow" models such as Finding. Goldens move only in rules_version, gap text and the report digest that current-control.json records. Closes #1026
|
Verification status at head
|
|
🤖 Engineering review — independent review, cycle 1 Findings
Points checked and found correct
Commands run
Full suite: not run (the change is confined to privacy, catalog flags and gap text; the targeted run covers every touched module). |
…est values
A `Basic` token is a credential only when it decodes to printable ASCII `user:password` (at least eight characters, a non-empty user), checked per match by a new `CheckedPattern`. Prose such as "Provides Basic ReadOnly access" or "Basic DataSets" is no longer redacted or reported as a secret, so it cannot raise SHIP-DOC-SECRET-IN-DESCRIPTION, the n8n secret finding or SEC-SECRET-001 and turn a passing sample into insufficient_evidence. A credential ending a sentence ("Basic <token>.") is now caught. The unlabeled AWS shape skips a 40-character run that reads as camelCase words or path segments, so a tool name such as getCustomerOrderHistoryForAccountV2Async and a path such as Sources/PaymentKit/Handlers/RefundTools2.json keep their identity and SARIF location. An `Authorization: Digest` value is redacted to the end of its line, and a 12-character or longer token after a bare `Authorization:` label is redacted. STABILITY now says the capability lock family is unredacted pending #1079.
|
🤖 Engineering review — implementer, addressed cycle 1 Head:
Docs: the CHANGELOG redaction bullet, the STABILITY migration note and Privacy section, Checks on
|
|
🤖 Engineering review — independent review, cycle 2 Delta review of Findings
No P0 or P1 findings. Probes requested for this cycle
Commands run (detached worktree at df92fb5)
Earlier findings
|
…ted match A `Basic` token now counts when it decodes to printable UTF-8 `user:password` with a non-empty user, as RFC 7617 defines the charset, so an unlabeled `Basic` credential with a non-ASCII password (jürgen:pässwörd1) is detected and redacted again instead of reaching report.json and the packet verbatim. CheckedPattern no longer lets a rejected match consume text: the search resumes one character after it began, so "Use Basic Basic <credential>" finds the credential the rejected "Basic Basic" overlapped. The leak tests plant the UTF-8 token in agent.declared_purpose.
|
🤖 Engineering review — implementer, addressed cycle 2 Head:
STABILITY's redaction bullet now says printable UTF-8 Checks on
|
…cret-observations-and-redaction # Conflicts: # STABILITY.md
|
🤖 Engineering review — independent review, cycle 3 No P0–P2 findings. Scope: delta Commands and results
Earlier findings
|
Summary
SHIP-DOC-SECRET-IN-DESCRIPTIONandSHIP-DOC-INJECTION-RISKsetpublish_when_policy_ineligibleindocs/checks/security.yaml, so a livesk_live_key or "ignore all previous instructions" in a tool description is a finding inreport.json,report.md, SARIF and the packet instead of only an evidence gap. Their support stayspolicy_eligible: false(agent_action: informational): no blocker, no named review item, no exit-code change; theinsufficient_evidenceverdict the gap already produced is unchanged.SHIP-N8N-SECRET-IN-WORKFLOW-PARAMETERcarries the same catalog flag; a parametrised test runs both throughrun_scanand checks the same publication (finding, SARIF result,report.mdline, value absent everywhere, advisory exit 0).policy_evidence_gap(detected=…)leads each finding-derived gap'swhywith the check's catalog description:SHIP-DOC-SECRET-IN-DESCRIPTION: Tool description contains a secret-like value. Policy applicability is supported only by heuristic evidence.0.2.core/privacy.pyaddsbasic_auth(Basic <token>decoding touser:password, header label kept),private_key(PEM block through its END line, or to the end of the text when truncated),google_api_key(AIza…), redaction-onlyaws_secret_access_key(unlabeled 40-char shape) and redaction-onlyauthorization/proxy-authorization/authlabeled values. The auth-scheme alternation is shared withcore/host_grants.py's header rule (byte-identical regex).bearer/basicfollowed by a tab no longer bypasses the precheck.RULES_VERSIONis0.2and is whatprivacy_audit.rules_versionrecords.sanitize_modelwalksmodel_extra. A value a check plugin attaches to aFinding(or anyextra="allow"model) is redacted in the first pass, so the in-memory report every writer reads is sanitised and the privacy audit counts it (findings[].debug_context).#secret-observations-1026),docs/checks.md,docs/passed-verdict-contract.md, rebuiltllms-full.txt.Type
Authorship
No person has read this diff before merge; it merges only if the independent review cycles and merge gates recorded in the PR comments pass.
Verification
CI is authoritative for
python -m ruff check .,python -m compileall -q src tests, andpython -m pytest.Additional local checks run (Python 3.14,
PYTHONPATH=src:.):python -m ruff check .— All checks passedpython -m compileall -q src tests— okpython scripts/generate_schemas.py --check— exit 0 (no schema ordocs/checks.jsonchange; the flag is excluded from the wire catalog)python scripts/regenerate_goldens.py --check— 24 artifacts, 0 changed (after regenerating 9; see Decisions)python scripts/verify_dependency_lock.py— OKpytest tests/test_privacy.py tests/test_documentation_checks.py tests/test_n8n.py tests/test_skill_review.py tests/test_docs_links.py tests/test_public_surface_contract.py tests/test_distribution_surface_parity.py tests/test_regenerate_goldens.py tests/test_reports.py tests/test_sarif.py tests/test_mcp_permissions.py tests/test_openshell_composition.py tests/test_openshell_inventory.py tests/test_openshell_row_detail.py tests/test_rule_credential_redaction.py tests/test_host_audit.py tests/test_current_control.py tests/test_reviewer_summary.py tests/test_evidence_gap_ranking.py— 1505 passed, 1 skippedorigin/main'ssrc/and check catalog, the new and changed tests fail (22 intests/test_privacy.py, 3 intests/test_documentation_checks.py); all pass on this branch.clean_read_only_agentcopy's tool names, descriptions anddeclared_purpose,origin/mainpublished them inreport.json,report.md,packet.{json,md,html}, SARIF (the AWS key via a semantic-gap result), the step summary andsuggested-declarations.yaml; this branch publishes none in any scan output, or in anyverifyoutput other than the two families named under Decisions.pytest -m "not perf and not slow" -q -p no:cacheprovider --ignore=tests/test_adapter_static_only.py --ignore=tests/test_ci_timing.py --ignore=tests/test_release_source.py --ignore=tests/test_required_source_availability.py(serial, about 3 h here): still running when this PR was opened, no failure in the first 18%; CI's six shards are authoritative, and the local result follows in a comment../shipgate verify --workspace . --config shipgate.yaml --base c6bb59ce --head HEAD: controlcomplete, every permissiontrue,release_decision.decision: passed.Release-readiness notes
docs/checks.mdSTABILITY.mdDecisions taken
SHIP-N8N-SECRET-IN-WORKFLOW-PARAMETERkeeps its existing support (none, so it can still route to review athigh) and gains the same catalog flag, which declares the rule and keeps it published if decide: may lexical heuristics move release verdicts, or only publish advisory findings? #1055 later gives it heuristic support. Moving n8n onto heuristic support now would have added an unwaivable gap to every n8n repository with a secret (passingchecks.ignoreusers included) and pre-empted decide: may lexical heuristics move release verdicts, or only publish advisory findings? #1055.SHIP-SCHEMA-*,SHIP-SCOPE-*,SHIP-API-PROMPT-TOOL-SCOPE-MISMATCH) infer whether a policy applies; publishing or not publishing them is decide: may lexical heuristics move release verdicts, or only publish advisory findings? #1055's question.SECRET_PATTERNS(detect and redact, so they also raise the two secret findings and skillSEC-SECRET-001). The unlabeled AWS shape and theauthorization/authlabels are redaction-only (REDACTION_ONLY_SECRET_PATTERNS,CREDENTIAL_LABEL_PATTERN): a context-free 40-character token orauth: oauth2_client_credentialswould otherwise raise a finding and, for the description check, an unclosable evidence gap that turnspassedintoinsufficient_evidence. The AWS shape requires upper, lower and a digit and an exact 40-character run not starting with/, and skips a run that reads as camelCase words or path segments, so Git SHAs,sha256:digests,tool_v2_…ids, JSON pointers,getCustomerOrderHistoryForAccountV2AsyncandSources/PaymentKit/Handlers/RefundTools2are untouched (pinned bytest_redaction_leaves_lookalike_text_alone). ABasic <token>counts, for detection and redaction alike, only when it decodes to printable ASCIIuser:password(cycle 1), so "Provides Basic ReadOnly access" stays prose and a passing sample stayspassed.privacy_audit.rules_version0.1→0.2in 7 reports; 33 gapwhystrings gain their check's description insimple_openai_api_agentandsupport_refund_agent(report and packet);conductor_agent/current-control.jsonre-recordsreport.json's sha256 and its own id. No decision, finding, fingerprint, blocker or exit code moved.verify.test_verify_publishes_no_planted_secret_in_any_rendered_artifactgreps every file in the reports directory exceptverification-input.diff(the change's exact bytes, a replay input the receipt digests) and the capability lock family, which publishes tool names verbatim for every shape including pre-checks: publish secret and injection observations, and redact every secret shape #1026 ones (see Follow-up candidates).tests/test_privacy.py::test_scan_redacts_public_outputs_and_reports_privacy_auditassertedfindings == []for a heuristic secret (P0: require authoritative evidence for policy findings #272's choice); it now asserts the finding is published, policy-ineligible and non-blocking, with the gap still present.Protected paths touched
none
Follow-up candidates
controls.evidence_ownerandsemantic_changes): the capability lock family (capabilities.lock.json,base.capabilities.lock.json,capability-lock-diff.{json,md},capability-delta-attestation.json) publishes tool names unredacted: a tool named with ansk-…key, a shape redacted since Add global privacy redaction layer #94, appears verbatim in the head lock, both lock-diff files and the delta attestation, whilereport.json, the PR comment andverifier.jsonredact it (src/agents_shipgate/core/capability_lock.pyhas no call intocore.privacy). Needs its own issue: redact or digest the published name without moving lock identity.src/agents_shipgate/inputs/n8n/_auth_risk.py:173truncates before redacting (_redact_text(value[:200])), so a credential straddling the 200th character is published in part.