Skip to content

cloudsec: evidence chain, coverage, code impact and remediation commands - #395

Merged
maximelb merged 1 commit into
masterfrom
evidence-chain-cli
Sep 23, 2026
Merged

maximelb merged 1 commit into
masterfrom
evidence-chain-cli

Conversation

@maximelb

Copy link
Copy Markdown
Contributor

Adds CLI and SDK access to four Code Security read and decision routes: a finding's evidence chain, Code Security coverage, code impact, and remediation runs.

Commands

  • cloudsec finding chain <id> [--runtime] [--summary] shows the eight-stage evidence chain for a finding (declared, committed, built, running, exposed, observed, responded, verified). Reasons and next actions are printed exactly as the server sent them, so a token this version does not know stays visible. --summary never displays an assertive outcome (verified, exposed, not_observed...) on a stage that is not proven.
  • cloudsec code coverage [--summary] shows coverage with explicit numerators and denominators. An unmeasured metric has no numbers. --summary shows a percentage only for a complete, fresh, untruncated count with a positive denominator. Otherwise it shows the counts, the reason and the next action.
  • cloudsec code impact --repo-urn <urn> [--commit <sha>] | --finding-id <id> shows which live resources a repository's infrastructure code touches.
  • cloudsec remediation list|get|create|approve|reject|cancel manages remediation runs.
    • Without --confirm, a decision sends nothing. It prints what it would apply to (action, targets, old digests, deadline, generation) and a token.
    • The token is derived from the run's generation and target digest. It stops matching when the run changes, and a token for one decision does not authorise another.
    • The server checks generation and target digest again and requires cloudsec.respond.

The SDK gains get_finding_evidence_chain, get_code_coverage, get_code_impact, list_remediations, get_remediation, create_remediation and decide_remediation. It also gains the helpers chain_stage_summary, coverage_percent and coverage_summary. Finding and run ids are checked against their canonical shapes before any request, so an id like .. cannot change the route.

Discovery profiles, the subcommand snapshot and doc/cli/cloud-security.md are updated.

Tests

tests/unit/test_cloudsec_evidence_chain.py uses byte-identical copies of the server's pinned wire bodies. It covers:

  • Counts. The CLI prints the server total for a query whose page holds 2 of 7 rows, plus the exact coverage numbers and chain gap count. The fixture hash is pinned.
  • Verbatim reasons. An unrecognised reason is printed as sent, and every gap has an action.
  • Percentages. No percentage is shown on an incomplete, truncated, stale, empty or unmeasured line.
  • No reassurance. An assertive outcome is hidden on a gap.
  • Routes. The SDK routes and bodies are correct, and malformed ids are refused before any request.
  • Decisions. Review sends nothing. Only the reviewed token sends, and it carries the reviewed generation and digest. A changed run, a token for another decision, and an undecidable state are all refused.

pytest tests/unit/ tests/microbenchmarks/: 4557 passed, 6 skipped.

🤖 Generated with Claude Code

Adds finding chain, code coverage, code impact and a remediation group
(list, get, create, approve, reject, cancel) with matching SDK methods.
Reasons and actions are printed as the server sent them; an assertive
outcome is never shown on a stage that is not proven; a coverage
percentage is shown only for a complete, fresh, untruncated count.
Approve, reject and cancel print a review first and send nothing without
a confirmation token bound to the run's generation and target digest.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@maximelb

Copy link
Copy Markdown
Contributor Author

Self-review of a pristine git archive extraction of the head commit. The automated reviewer does not run on this repository, and a separate review agent stopped on a rate limit before reporting, so I did this pass myself.

Checked, and sound:

  • No decision without the explicit second step.
    • The review call only reads the run.
    • A decision needs the token. For MCP that is an HMAC with a key the caller never sees. It binds org, run, decision, generation and target digest, expires after 5 minutes, and is compared in constant time.
    • A change to the run, a token for another decision, a forged token or an expired token never reaches the gateway. Each has a test, and removing the check fails them.
    • The generic tool dispatcher reaches the same handler, so it cannot skip the step.
    • The decision tool is not in the read-only profile.
  • The gateway still enforces authority. It requires cloudsec.respond and refuses a stale generation or digest. The client adds a step and never adds authority.
  • Ids. Finding and run ids are checked against their canonical shape before any URL is built, so .. and similar ids are refused locally.
  • No reassurance on incomplete evidence. Assertive outcomes are removed from stages that are not proven. A percentage is shown only on clean lines. Unrecognised reasons and actions pass through verbatim.
  • Counts. Counts are the server's, and the shared fixture's hash is pinned.
  • Disclosure scan. The diff, the fixtures, this PR's title and body are clean.

Known limits, recorded rather than fixed here:

  • An agent that ignores the tool's instructions could make the second call without asking its user. Stopping that needs client-side user prompts (MCP elicitation), which not every client supports. The gateway's permission check and generation fencing are the hard gates.
  • In the CLI the token is a hash of values the reviewer can see. It proves a review happened for this exact run state, and it is not a secret.

@maximelb
maximelb merged commit dbd15d4 into master Sep 23, 2026
6 checks passed
@maximelb
maximelb deleted the evidence-chain-cli branch September 23, 2026 18:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants