Conversation
* Audit the environment scope the deploy credentials live in The secrets audit read repository scope alone, which is where the merge bot's credentials live and not where the deploy's do, so an unconfigured deploy passed it. It now asserts both, including that a scoped credential is absent from the environment that must not carry it. The deploy root is no longer declared, since the rsync destination is anchored at the key's confinement root and a declared-but-unread name is drift an audit cannot tell from a missing one. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * Guard the lookup that drives the loop, and correct the manifest note A guard written in a for header never fires, because command substitution discards the exit status, so the lookup is assigned before it is iterated. The manifest note described the environment split as carrying a deploy root the manifest no longer declares. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * List environment credentials by name through the tool that reaches them The section claimed a listing could not reach environment scope, which it can with an environment flag, and the raw API calls read one page. The listing prints every variable's value unless names are requested, so an audit run wrote the deploy endpoint into its own output. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Updates the repository’s audit documentation/spec to explicitly verify GitHub Actions secrets/variables at both repository scope (merge bot) and environment scope (deploy), closing a gap where an unconfigured deploy could pass a repository-only secrets audit.
Changes:
- Document separate repository-scope vs environment-scope secrets/variables verification in
AUDIT.md, including safeghcommands that list names only. - Refine
spec/secrets.jsonenvironment variable/secret declarations (notably removingDEPLOY_ROOTfrom environment variables and documenting why). - Add an additional “empty query reads as clean” pitfall reminder to
TODO.md.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
| TODO.md | Adds a reminder about empty-result queries falsely appearing as “clean.” |
| spec/secrets.json | Updates environment-scoped declarations/notes (removes DEPLOY_ROOT, adds rationale). |
| AUDIT.md | Documents auditing secrets across both repository and environment scopes with safer CLI patterns. |
The table listed a name no environment carries and no workflow reads, which left the operator-facing list disagreeing with the manifest about whether it should exist. Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 3 out of 3 changed files in this pull request and generated no new comments.
Suppressed comments (1)
AUDIT.md:65
- The note about
gh variable listis factually misleading: by defaultgh variable listdoes not print variable values (values are exposed via JSON selection like--json value). Keeping the warning accurate matters because this runbook is meant to be executed verbatim and trusted.
Suggestion: keep the safety guidance but rephrase it as “use --json name for machine-readable output and to guarantee only names are emitted; do not request the value field (or use gh variable view) in audit output.”
`--json name` is not decoration. The bare `gh variable list` prints every value, so an audit run without it writes the deploy host, the user, and the known-hosts entry into whatever captured its output.
|
Declining the premise, taking half the suggestion. Wording sharpened in 2d3aee4. The bare listing does print values, in both modes. Measured on The terminal form renders a Your second half is a real improvement and is now in the text: do not request the I have also made the wording say which mode does what, because the original sentence described the captured form while reading like a claim about the terminal form. That ambiguity is what made the finding reasonable to raise. Worth stating plainly: this runbook is meant to be executed verbatim and trusted, which is exactly why I measured rather than reasoned about it. |
The warning described the captured form, where a value prints in full, and read as though it described the terminal form, where the column is truncated. Both emit values, and the captured one is what an audit keeps. Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Promotes one commit. Merge commit, per the
mainruleset.What lands
The secrets audit read repository scope only. That is correct for the merge bot's credentials and blind to the deploy's, which are environment-scoped, so an entirely unconfigured deploy passed it. This is the only repo in the fleet where that gap exists.
Both scopes are now asserted, and the third assertion is the one presence-checking misses: a scoped credential must be absent from an environment that does not list it.
productionholding a Pangolin access token is a finding rather than a harmless extra, because production answers unauthenticated and a token there means a check could pass through a gate production is not supposed to have.Findings from writing and reviewing it
spec/secrets.jsondeclared a deploy root that no environment carries and no workflow reads, since the rsync destination is anchored at the key's confinement root. A declared-but-unread name is drift an audit cannot tell from a missing one.jqpath that had moved, iterated zero times, and reported clean. Review then caught that the guard was written in aforheader, where command substitution discards the exit status, so it never fired. Both lookups are assigned before they are iterated.Verification
The documented commands run verbatim against the live configuration: staging carries 3 secrets and 4 variables, production 1 and 4, and the staging-only pair is correctly absent from production.
Separately, the deploy pipeline this audit covers has now run for real: a staging deploy dispatched from
mainbuilt, uploaded, flipped, and verified release20260804-231050, passing the full 1,245-URL contract against the live site.🤖 Generated with Claude Code