A GitHub Actions workflow. Step 1 checks out code and sets an org-level token — GITHUB_TOKEN with org:write scope. Step 4 is a third-party action: `uses: some-vendor/action@main` — unpinned, no hash, fetched fresh from main at runtime. That action runs in the same job, same environment, same token. You've handed org:write to a third-party action you don't control and didn't review. The scanners didn't catch it. There was no secret pattern. It was a reachability problem.
This is the failure that security scanners consistently miss. Not because they're bad at what they do — because they're asking the wrong question.
Why pattern scanners don't see this
gitleaks: finds secret patterns in code. Doesn't model pipeline execution flow.
trivy: finds CVEs in packages and container images. Doesn't know what tokens are in scope at step 4.
checkov: validates policy rules against config syntax. Doesn't trace which steps can reach which credentials.
These tools evaluate each thing in isolation. None of them can tell you that the token set in step 1 is reachable from the unpinned action in step 4. That's a graph problem, not a pattern problem. You need a tool that builds the graph.
taudit's model
taudit builds a directed graph of your pipeline. Nodes are steps, jobs, actions, and secrets. Edges are reachability — what can reach what given the execution order. It runs BFS from each secret and token to enumerate every step that can access it. Then it applies 6 rules:
| Rule | What it catches |
|---|---|
| RULE-01: unpinned-action-with-token | Unpinned action (no SHA) runs in same job as a secret or elevated token |
| RULE-02: cross-job-secret-inheritance | Secret set in job A is inherited by job B via needs: without explicit scoping |
| RULE-03: excessive-token-scope | GITHUB_TOKEN or other token has broader scope than any step requires |
| RULE-04: secret-in-shared-env-block | Secret placed in job-level env block, visible to all steps including untrusted ones |
| RULE-05: missing-least-privilege | Step requests a permission not constrained to minimum required for its function |
| RULE-06: org-wide-token-in-step | Token with org-level scope used in a step that only needs repo-level access |
What it looks like
taudit doesn't tell you your pipeline is secure. It tells you where authority goes and whether it travels further than you intended. Those are different questions. Most tools only answer the first one.