- Foundation — identity, secrets & repo hygienePublished
- 2Shift Left — security in the pipelineYou're reading this
- Supply Chain — dependencies, IaC & artefactsComing Sep 2026
- Runtime & Response — detect and reactComing Oct 2026
- Govern & Mature — policy, metrics & evidenceComing Nov 2026
DevSecOps Program · Part 2 — Shift Left: Security in the Pipeline
The second deep-dive of the DevSecOps Program: wiring SAST, SCA and secret scanning into CI as merge gates developers trust — with progressive enforcement, noise control and clear exit criteria.
This is Part 2 of the DevSecOps Program — the phase where security stops being a thing you remember to do and starts being a thing the pipeline does for you on every change. Part 1 built the foundation: identity is consolidated behind one IdP, secrets live in a vault, every change flows through a reviewed pull request, and you know what you run. Crucially, you also expressed branch protection as code — and inside that Terraform there was a required_status_checks block with a single ci context and a comment promising that Part 2's security gates attach here. This is where we cash that promise. "Shift left" is not a slogan about doing security earlier for its own sake; it is the concrete practice of moving the detection of a vulnerability from production (expensive, public, incident-shaped) to the pull request (cheap, private, a five-minute fix while the code is still fresh in the author's head). The mechanism is unglamorous and effective: run the right scanners on every change, and make the important ones block the merge.
Scope of this phase
Every part of this program is scope-bounded so you know exactly when you are done and are not tempted to boil the ocean.
- In scope: pre-commit hooks graduating into CI as required status checks; three pipeline scanning layers — SAST (static analysis of your code), SCA / dependency scanning (known-vulnerable third-party packages), and secret scanning (credentials in the diff); and the discipline of turning those scanners into trusted gates through noise management, progressive enforcement, fast feedback and clear ownership.
- Explicitly out of scope (later parts): infrastructure-as-code scanning, SBOMs and signed artefacts (Part 3), DAST and runtime monitoring (Part 4), and continuous control monitoring and metrics (Part 5). You will run scanners against your source and dependencies here, not your running system or your build provenance.
- Definition of done: the exit checklist at the end of this part. When every box is ticked, at least one gate in each of the three layers blocks a merge on new high/critical findings, and your developers trust it enough not to route around it.
The order matters: none of this is safe to switch on before Part 1. A SAST gate is theatre if a developer can push straight to main; a secret-scanning gate is pointless if secrets were never supposed to be in code in the first place. With the foundation in place, the gates have something real to attach to.
Scanner versus gate: the exit-code 1 idea
Before the tools, the single most important distinction in this phase. A scanner runs, finds things, and reports them — it files a ticket, annotates the PR, populates a dashboard. A gate does all that and blocks the merge when it finds something above a threshold. Mechanically the difference is one thing: the process exit code. A scanner run that always exits 0 is informational; the same run configured to exit 1 on a high-severity finding is a gate, because CI marks the check failed and branch protection refuses the merge.
Everything in this part is about deciding, deliberately and per finding-class, which scanners get to exit 1. Get greedy — block on everything — and developers drown in noise, lose trust, and pressure you to switch it all off. Get timid — block on nothing — and you have bought dashboards, not security. The craft of shifting left is drawing that line in the right place and moving it rightward (stricter) over time as the signal proves itself.
The three pillars at this stage
Three classes of problem are cheap to catch in the pipeline and expensive everywhere else. Pick one solid tool per layer — coverage beats tool count — and wire each into CI.
SAST — static application security testing
SAST reads your source code without running it and flags dangerous patterns: SQL injection built from string concatenation, command injection, unsafe deserialization, weak crypto, hard-coded logic flaws. It runs in CI against the diff (and periodically against the whole tree). Semgrep is the pragmatic default — fast, multi-language, with rulesets you can tune and extend. SAST's strength is catching your bugs; its weakness is false positives, which is exactly why tuning (below) matters most here.
SCA — software composition analysis / dependency scanning
Most of the code you ship, you did not write. SCA inventories your third-party dependencies and matches them against known-vulnerability databases (CVEs), flagging packages with published advisories and, ideally, telling you the fixed version to bump to. It catches the class of problem that caused the largest real-world incidents of the last decade — a vulnerable transitive library nobody knew was there. Trivy or Grype cover this well and run in seconds; language-native tooling (npm audit, pip-audit, Dependabot/Renovate for the fixes) complements them. SCA findings are usually higher-signal than SAST because a CVE is a fact, not a heuristic — which makes SCA a good first gate to enforce.
Secret scanning — credentials in the diff
Part 1 installed a pre-commit secret-scanning hook to stop leaks at the developer's machine. That hook is local and optional — a developer can skip it with --no-verify or a fresh clone. The CI layer is the backstop that cannot be skipped: Gitleaks (or GitHub's native secret scanning) runs server-side on every push and PR, scanning the diff for credential patterns. Because a leaked secret is a fact and an emergency, secret scanning is the layer you enforce as a hard gate earliest and most aggressively.
Security gates across the pipeline
Every gate blocks the merge or deploy on failure — and emits timestamped evidence as it runs. Tap a stage for detail.
Read the diagram as a sequence of stages, each with its own gate. Secret scanning and SCA sit early and cheap; SAST runs on the diff; the whole set reports back onto the pull request before a human review is even requested. Nothing here replaces the code-owner review from Part 1 — the gates run alongside it, so a human approves intent while the machine enforces the non-negotiables.
The same gate on every platform
You do not need a bespoke pipeline. The same three scanners, wired to fail the build on high-severity findings, express cleanly on every major CI platform — the syntax differs, the control is identical.
name: security
on: [pull_request]
permissions:
contents: read
jobs:
scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: SAST (Semgrep)
uses: semgrep/semgrep-action@v1
with:
config: p/ci # curated ruleset
- name: Secret scan (Gitleaks)
uses: gitleaks/gitleaks-action@v2
- name: Dependency scan (Trivy)
uses: aquasecurity/trivy-action@0.24.0
with:
scan-type: fs
severity: CRITICAL,HIGH
exit-code: "1" # fail the build on high/criticalWhichever platform you run, the shape is the same: a security workflow triggered on pull requests, running secret scanning, SCA and SAST, each configured with a severity threshold that decides whether it exits 0 (report) or 1 (block). That workflow's job name is the context you register in branch protection — turning a passing green check into a merge requirement.
Graduating pre-commit hooks into CI
Part 1's pre-commit hooks are the fast, local first line — instant feedback, no CI round-trip. But local hooks are advisory: they rely on every developer having them installed and not bypassing them. The graduation pattern is to run the same checks again in CI, where they are mandatory and attributable:
# the same secret scan Part 1 ran locally, now as a non-skippable CI gate
name: security
on: [pull_request]
jobs:
secrets:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with: { fetch-depth: 0 } # full history so the diff scan is complete
- name: gitleaks
uses: gitleaks/gitleaks-action@v2
# non-zero exit on any finding => this check fails => merge blockedLocal hooks give speed; CI gives enforcement. Run both. The developer sees the problem in seconds locally and cannot merge past it if they skip the hook — the belt and the braces.
Then wire the job name into the branch protection you already manage as code, extending the contexts list that Part 1 left as a placeholder:
required_status_checks {
strict = true
contexts = ["ci", "security"] # Part 1 left "ci"; Part 2 adds "security"
}That one-line change is the whole point of the phase: the security workflow is now a required status check, so branch protection will not allow a merge while it is red. The gate is live.
Making gates developers actually trust
This is the part that decides whether the phase succeeds or quietly gets disabled in three weeks. A gate that developers trust is one that fails only when something is genuinely wrong and fixes are obvious. A gate that cries wolf gets routed around, exception-listed, or ripped out — and then you have negative security, because everyone assumes it is covering something it is not. Four disciplines keep gates trustworthy.
Start narrow: high and critical only
Do not enable every rule at maximum strictness on day one. Configure each scanner to block only on high and critical severity to begin with, and let mediums and lows report without failing the build. A tight, high-signal gate that fires rarely and correctly builds trust; a noisy one burns it. You can always tighten later — you rarely get a second chance at first impressions.
# example: fail the build on HIGH/CRITICAL, report the rest
severity-threshold: high # medium/low are annotations, not blockers
exit-on: [HIGH, CRITICAL] # only these flip the exit code to 1Tune the noise
Every SAST tool ships false positives. Budget explicit time to triage them and suppress the ones that are genuinely wrong — inline ignore comments with a reason, or a tuned ruleset checked into the repo so the suppression is itself reviewed. The goal is a gate where a red build reliably means "you introduced a real problem," because the moment a red build usually means "the scanner is being dumb again," the gate is dead. Track your false-positive rate; it is the single best proxy for how much your developers trust the gate.
Progressive enforcement: warn, then block
Do not flip a new gate straight to blocking. Run it in warn mode first — it reports on PRs and annotates findings but exits 0, so nothing is blocked. Watch it for a sprint or two: is it finding real issues, what is its false-positive rate, is the feedback fast? Once it has earned trust, flip it to block mode (exit 1) for new findings. Handle the backlog of pre-existing findings separately — baseline them so the gate enforces "no new problems" rather than holding every PR hostage to historical debt. New code is clean; old debt is burned down on its own track.
Fast feedback and clear ownership
A gate that adds fifteen minutes to every PR gets hated regardless of how good its findings are. Keep the security checks fast enough to run on every PR — scan the diff, not the world, on PR events; cache dependency databases; run the heavy full-tree scans on a schedule rather than the critical path. And assign ownership: someone owns each gate's configuration, its false-positive triage, and its threshold. An unowned gate rots — thresholds drift, suppressions pile up unreviewed, and one bad week of noise kills it. Ownership is what keeps a gate honest over time.
What is a gate versus what is informational, at this stage
Not everything you scan should block. A sane starting posture:
| Layer | Gate (blocks merge) | Informational (reports only) |
|---|---|---|
| Secret scanning | Any verified secret in the diff | Historic findings already rotated |
| SCA / dependencies | New high/critical CVE with a fix available | Low/medium CVEs; findings with no fix yet |
| SAST | High/critical rules with low false-positive rates | Medium/low rules; noisy rules under evaluation |
The principle: block on the serious and the certain; report on the rest. A verified secret and a fixable critical CVE are unambiguous and actionable — block. A medium-severity SAST heuristic still proving itself belongs in warn mode until it earns promotion. Revisit this table every quarter and move rules rightward-to-leftward (informational to gating) as they prove their signal.
Definition of done — Shift Left exit checklist
You are ready for Part 3 when every one of these is true:
- Three layers live in CI: SAST, SCA and secret scanning all run on every pull request, triggered server-side so they cannot be skipped.
- At least one hard gate per layer: each layer blocks a merge on new high/critical findings — the scanners exit
1and branch protection enforces it. - Wired into branch protection as code: the
securityworkflow is a required status check in the Terraform from Part 1; thecontextslist was updated and reviewed. - Pre-commit graduated: the local secret-scanning hook is mirrored by a non-skippable CI check.
- Tuned, not noisy: gates start at high/critical only; a false-positive triage process exists and an owner is assigned to each gate.
- Progressive enforcement in place: new gates ran in warn mode before blocking; pre-existing findings are baselined so gates enforce "no new problems."
- Fast enough for every PR: checks scan the diff on PR events and complete quickly; heavy full-tree scans run on a schedule, off the critical path.
Tick every box and security now runs on every change without anyone remembering to trigger it — and the pass/fail record of every gate becomes ISO 27001, SOC 2 and NIS2 evidence that your controls operate continuously, not just at audit time.
What's next
Part 3 — Secure the Supply Chain builds directly on these gates. With your own code and dependencies now scanned on every change, we turn to what goes into and comes out of the build: infrastructure-as-code scanning, generating an SBOM so you can prove exactly what is in every artefact, signing those artefacts so their provenance is verifiable, and policy-as-code to enforce it all. The pipeline you gated in this phase is the place that machinery attaches. It ships next month.
Wiring these gates is also where continuous evidence starts to compound: ISMShed ingests the pass/fail history of your SAST, SCA and secret-scanning gates and maps it to controls across ISO 27001, ENS, NIS2, DORA, SOC 2 and GDPR — so "our security tests run on every change" becomes a claim you can prove with a click rather than a screenshot you reassemble before each audit. If you would like experienced hands to tune the gates against your own stack — drawing the block-versus-report line where it fits your risk and your team — Axelia's DevSecOps and GRC consultants run the program alongside you. Get Shift Left right, and every later phase gates on a pipeline your developers already trust.
