DEV Community

Yuhe He
Yuhe He

Posted on

Auditing a CI System Nobody Documented: The Four-Question Walk

Handed an unfamiliar repo with "can you look at our CI?" is the most common version of this job: no docs you trust, a year of merged workflow PRs, no one who remembers why step 4 of deploy.yml pipes through tee. A checklist (my previous post) answers "is this repo safe." An audit answers a different question: "what is this system, and what does it do to secrets when nobody is optimizing it." Four questions, walked in order, get a map in under an hour.

Question 1: What can a stranger start? Enumerate every path from outside to a runner: push, PR, comment, issue label, schedule, dispatch, workflow_run chains. Each path gets one line: trigger → job → what it can touch. The chain edges (workflow_run A→B) are the ones people forget; a chain inherits the secrets of the repo, not of the starter. The output of this question is a graph, and the graph usually has one node the team never named: the comment-triggered job that deploys, the label that publishes, the PR that writes into a shared cache.

Question 2: Where do secrets physically land? Secrets are injected env vars; envs end up in process environments, command lines, logs, artifacts, caches, third-party APIs' request bodies, and exception payloads. Walk each job's outputs, not its YAML: one real failing run's log bundle, downloaded, grepped for a canary (rotate a secret to a known token, run the pipeline, search every output surface for the token). The canary walk is the only audit step that cannot lie, because it asks the system where it put the value, and the system answers.

Question 3: Who wrote the trust boundary, and does it still hold? Every org has one: "deploy runs only from main," "secrets only in the deploy namespace," "forks never see keys." The boundary is not in the docs; it is in whichever if: condition someone wrote on a Friday. Find the condition, test it as an adversary: a fork PR that edits the condition's input, a renamed branch, a label added from a fork, a schedule that fires during a freeze. Trust boundaries fail by being conditions — conditions have inputs, and inputs are attacker-owned more often than authors assume.

Question 4: What is the recovery path when a secret leaks, and who rehearsed it? Ask for the rotation runbook by name. The honest answers are: it exists and is tested (rare), it exists and is a wiki page (common), it is "we'd regenerate and redeploy" (most). The audit's real output is the cost curve: rotation cost × blast radius. Org-wide secrets used by 40 workflows are the finding; repo-local secrets with a tested runbook are the model. A finding that arrives with "here is the rotation drill that makes this cheap" gets fixed; a finding that arrives as a risk letter gets filed.

The walk produces four artifacts: a trigger graph, a canary map, a tested boundary, and a rotation bill. Four questions, an hour, a repo you understand operationally — which is the only understanding that survives contact with an incident. The audit's deliverable is not a verdict; it is the map you wish you'd had the day the secret leaked, delivered on the day you did not need it.

— 何雨禾 (heyuhe). The canary walk is the one step I refuse to skip, because it is the only one where the system tells you the truth about itself.


The walk condensed into a brief you can run against your own repo tonight: GhostAction Event Brief ($5). What the walk finds in the wild: GhostAction Is Harvesting CI Secrets Right Now.

Top comments (0)