📋 What Is It?
Insufficient Flow Control Mechanisms describe a pipeline where a single actor - or an attacker with a single foothold - can push code, configuration, or artifacts all the way to production without passing through an adequate set of checks, reviews, or gates. The vulnerability is not a bug in one script; it is the absence of an enforced sequence of independent approvals between "someone changed something" and "that change is running in production." When gates are missing, weak, or self-satisfiable, whatever enters the front of the pipeline reaches the end unchallenged.
⚠️ Common Exploits
- Direct Push to Mainline: No branch protection, so a foothold writes straight to
main - Self-Approval: The author is also the only approver of their own PR
- Non-Blocking Checks: Build/test/scan run but are not marked required, so merge proceeds while red
- Auto-Merge Abuse: A PR merges on open/label before any human reviews the diff
- Deploy on Push: Reaching
mainequals reaching prod - no separate approver - Fork PR in Privileged Context:
pull_request_targetruns untrusted code with secrets - Pipeline Definition Tampering: Ungated workflow files delete their own gates
- Token / Bot Bypass: Automation identities sit outside branch-protection rules
🔴 Attack Flow
↓
2. Locate the weakest transition (push, merge, deploy)
↓
3. Advance the change (direct push / self-approve / merge red)
↓
4. No second decision is ever demanded
↓
5. IMPACT: Attacker code reaches a trusted release or prod!
❌ Vulnerable Code
✅ Secure Code
✓ Prevention Checklist
- Protect the mainline: reachable only via reviewed PR
- Require ≥2 approving reviews; forbid self-approval
- Enable require-last-push-approval / dismiss stale reviews
- Mark build, test, and scan as required status checks
- Put production behind a protected environment
- Require a separate, distinct deploy approver (four-eyes)
- CODEOWNERS on all pipeline definition files
- Never expose secrets to fork PRs (use pull_request)
- Apply protection to admins and bots - empty bypass list
- Prefer short-lived OIDC over long-lived deploy tokens
- Detect drift: assert gates still exist on a schedule
🔍 Detection & Tools
Controls & Tooling:
Audit & Drift Detection:
How to Test:
- Try a direct
git pushto the protected branch - Open a PR and attempt to self-approve and merge
- Merge with failing/absent status checks - should be blocked
- Confirm deploy requires a distinct second approver
- Verify fork PRs cannot read repo secrets
- Check whether admins/bots bypass protection rules
🌍 Real-World (Incident Classes)
- Build-System Compromise (Supply-Chain Class): Adversaries with a foothold in a vendor build environment injected code that was then signed and distributed as a legitimate update - no gate verified that the shipped release matched reviewed source.
- Compromised CI Tooling (Pipeline-Integrity Class): A widely used pipeline uploader script was altered at its distribution point and ran inside many organisations' pipelines, exfiltrating environment secrets - no integrity gate (pinning/verification) interrupted it.
- Direct Malicious Commits (Direct-Push Class): Attackers pushed malicious commits directly to a major open-source project's mainline; the remediation was explicitly to add mandatory pull-request review and branch protection.
- Fork PR Poisoned Pipeline Execution (Researcher-Documented): Public repos configured so that merely opening a fork PR runs attacker code in a privileged context holding CI secrets - no maintainer approval stood in the way.
📌 Quick Tips
- DO NOT deploy on every push to main
- DO NOT allow self-approval of PRs
- DO NOT run fork code with secrets
- DO separate merge from deploy
- DO make checks blocking, not advisory
- DO code-own the pipeline definitions
- DO verify gates continuously for drift
📜 Compliance
Related Standards:
- SLSA Two-person review / provenance
- SOC 2 CC8.1 - Change Management
- ISO 27001 A.14 - Secure Development
- NIST SSDF PO/PS - Protect the Software
- SoD Separation of Duties