pull_request_target workflow that checks out untrusted code is risky on its
own; combine it with a writable GITHUB_TOKEN and it becomes a full repository
takeover.
Attacker Mind is Pipefort’s correlation layer. It looks across the findings
from a scan and reports toxic combinations: named attack scenarios that
exist only when a set of findings co-occur. Each combination carries:
- a severity —
CRITICALorHIGH(toxic combinations sit above the per-finding severity scale, because the chained impact exceeds any single ingredient); - an attack chain — the ordered stages of the compromise, so you can see what the attack actually looks like;
- the contributing findings with their
file:linelocations; and - a break-the-chain recommendation — the single highest-leverage fix that collapses the whole scenario.
How detection works
Combinations are pure correlation over findings that the scanner already produced, so the same engine powers both the CLI and the web dashboard. Combinations are matched on rule IDs (not OWASP categories), because a category likeCICD-SEC-4 is emitted by more
than one rule (workflow shell-injection and the read-write GITHUB_TOKEN
repository setting) — matching on rule ID keeps each ingredient precise.
A combination only forms from findings that survived your
rule settings: if you disable a rule, it can never be
an ingredient.
Combinations are scoped:
- file-scoped combinations require their anchor ingredient inside one workflow file and are reported once per such file;
- repo-scoped combinations correlate across the whole repository and are reported once.
The shipped combinations
| Combination | Severity | Ingredients (rule IDs) | Breaks when you… |
|---|---|---|---|
| Pwn Request | CRITICAL | cicd-sec-1-ppe-checkout + a writable token (cicd-sec-5-missing-permissions or cicd-sec-4-wperm-write); amplified by cicd-sec-4-ppe-shell-injection | stop checking out the PR head ref in pull_request_target |
| Poisoned Exfiltration | CRITICAL | cicd-sec-4-ppe-shell-injection + a reachable secret (cicd-sec-6-hardcoded-secrets, cicd-sec-2-long-lived-pat, or cicd-sec-7-debug-logging-enabled) | route untrusted input through an env var instead of the shell |
| Persistent Supply-Chain Foothold | HIGH | cicd-sec-3-unpinned-action + best-prac-3-self-hosted-runners; amplified by best-prac-2-missing-timeout | pin the action to a full commit SHA |
| Untrusted RCE on Infra | HIGH | best-prac-1-pipe-to-shell + best-prac-3-self-hosted-runners; amplified by cicd-sec-4-wperm-write | download, verify, then execute — never pipe to a shell |
| Silent Supply-Chain Tampering | HIGH | cicd-sec-9-download-without-checksum + (cicd-sec-3-unpinned-action or cicd-sec-10-continue-on-error-job) | verify a checksum/signature for every download |
| Open Trigger Secret Leak | HIGH | cicd-sec-8-repository-dispatch-unfiltered + (cicd-sec-7-debug-logging-enabled or cicd-sec-6-hardcoded-secrets) | add a types: allowlist to the trigger |
Where to see them
- CLI — every scan prints an Attacker Mind — Toxic Combinations section
after the findings (and includes them under the
toxic_combinationskey in JSON output). - Web app — the Attacker Mind dashboard aggregates combinations across all your repositories and draws each attack chain.