DevSecOps · Apr 22, 2026 · 7 min read

When the pipeline is the target

Attackers now compromise CI/CD directly - a misconfigured workflow trigger leaked credentials that force-pushed malicious commits.

The riskiest code in a release is often code nobody on the team wrote. It arrives through a dependency that runs at build time, a workflow trigger that trusts the wrong context, or a token that a job had no reason to expose. The application can be flawless and the release still ships an attacker's payload, because the thing that was compromised was not the app - it was the pipeline that built it.

In one 2025 incident an actor exploited a misconfigured pull_request_target workflow to exfiltrate credentials, then used them to force-push commits carrying a multi-stage credential stealer. No CVE in the product. The supply chain was the vulnerability, and the build system handed the attacker production reach.

01Every stage trusts the stage before it

A pipeline is a chain of trust assumptions. Source trusts the committer. Build trusts the dependency graph and runs it as code. Test trusts that the steps were not swapped. Deploy trusts that everything upstream held. Break any link and the blast radius is production, not a branch:

SOURCE BUILD TEST DEPLOY commit / PR ──► deps + compile ──► apPosture gate ──► release trigger config third-party code proven findings production trigger assumed ok runs as code fails closed full blast radius
The gate sits at the test stage, between code that executes during build and a release that reaches production. It is the last point where a decision can be made on evidence rather than trust.

Most teams put a scanner somewhere in that chain and call it a gate. A scanner that can be skipped, swapped, or that silently fails open is not a gate - it is a suggestion. The day an attacker is inside the pipeline is exactly the day a soft gate waves the build through.

02A gate that fails open protects nothing

apPosture's gate fails closed. If the gate cannot be evaluated - the policy engine errors, a scanner is missing, the run is incomplete - the build does not quietly pass. Under the default fail mode the decision is forced to fail and the inability to evaluate is written to the audit log as a security-relevant event. A gate that passes silently when it is broken is worse than no gate, because it manufactures false confidence.

The verdict is not a single guess either. The gate combines a configured policy and rule engine with a per-call severity threshold, and takes the worst of the two. The threshold fails the build when any finding at or above the configured severity exists; by default that means a new high or critical finding. New is measured against a baseline, not the whole backlog, so a pull request is judged on what it adds over production rather than on debt it inherited.

Gate signalDecisionBuild in MonitorBuild in Block
New finding at or above thresholdfailrecorded, build passesbuild fails (exit 1)
Policy / rule verdict: warnwarnrecordedrecorded, build passes
Gate cannot be evaluatedfail (closed)recorded and auditedbuild fails and audited
No new finding above thresholdpassbuild passesbuild passes

03Monitor to Block, without touching the pipeline

Enforcement is a server-side toggle, not a line in a YAML file. In Monitor mode the gate still runs, still computes the decision, and still records it - but it never breaks the build, even on a fail-closed verdict. Teams watch exactly what would block them for a few weeks, tune the policy, then flip a single switch to Block when the signal is clean.

Keeping enforcement off the pipeline definition matters for the same reason the whole post does. If the rule that decides whether to block lived in the workflow file, anyone who could edit that file - including a future attacker who already owns the trigger - could disable the gate in the same commit that introduces the payload. The decision lives where the code under test cannot reach it.

04CI gates stay lean, manual review goes deep

A gate has to answer in pipeline time, so the CI path does not run the full triage stack. It scans the source engines - SAST, SCA, secrets, IaC - fingerprints the findings, diffs against the baseline, and returns a decision. A manual rescan from the dashboard takes the other trade: it opts into the full AI and ML false-positive triage on SAST, applied before the findings are persisted, so the saved verdicts carry that deeper judgement. Two run types, two budgets, recorded as CiRun history either way.

AspectCI gate runManual rescan
Triggerci / webhookmanual
SAST triagelean (fast feedback)full AI / ML FP triage
Goalblock the build on proven riskdeep review, save verdicts
Posture writedefault branch onlypersisted

Posture persistence being default-branch-only is a deliberate guard, not an oversight. A pull request or feature branch is gated in full, but its findings are not written into the shared posture. Unmerged code would otherwise inflate production risk and corrupt the very baseline the next pull request diffs against. What you are about to ship is judged; what you have not merged does not pollute the record of what is live.

05Block on proof, warn on noise

The point of putting a gate in front of a release is undercut the moment it blocks on things that turn out not to be real. A gate that cries wolf gets a waiver rule, and a waiver rule is a permanent hole. So the same evidence bar that governs the rest of the platform governs the gate: it is built to fail the build on findings backed by proof - a leaked secret, a reachable injection, a real misconfiguration the IaC parser reads as fact - and to keep softer signals as warnings rather than hard stops.

The agent that runs all of this is checksum-pinned, and the same IaC parser that grades your cloud also reads pipeline and trigger configuration as facts. When the pipeline is the target, the system that judges your releases has to be the hardest thing in the pipeline to subvert - fail-closed, off the editable workflow, and unwilling to block on anything it cannot prove.

Evidence over heuristicsReachable beats foundMonitor → Block: rolling out a gate without slowing teamsIaC-grounded threat modelingAir-gapped AppSec with no phone-homeOne platform vs three tools
See it on your own app →