← All posts

taudit: modelling authority flow in CI/CD pipelines

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:

taudit rule set
RuleWhat it catches
RULE-01: unpinned-action-with-tokenUnpinned action (no SHA) runs in same job as a secret or elevated token
RULE-02: cross-job-secret-inheritanceSecret set in job A is inherited by job B via needs: without explicit scoping
RULE-03: excessive-token-scopeGITHUB_TOKEN or other token has broader scope than any step requires
RULE-04: secret-in-shared-env-blockSecret placed in job-level env block, visible to all steps including untrusted ones
RULE-05: missing-least-privilegeStep requests a permission not constrained to minimum required for its function
RULE-06: org-wide-token-in-stepToken with org-level scope used in a step that only needs repo-level access

What it looks like

PortableText [components.type] is missing "code"

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.