Shifting security left means finding problems while the developer still has the context to fix them cheaply. The economics are simple: a vulnerability caught in the editor costs minutes, in code review costs an hour, and in production costs an incident.

The failure mode is equally simple: bolt on five scanners, generate four thousand findings, block every build, and watch the organization disable the whole thing within a month.

Which control belongs at which stage

flowchart LR
    A["Editor<br/><small>seconds</small>"] --> B["Pre-commit<br/><small>seconds</small>"]
    B --> C["Pull request<br/><small>minutes</small>"]
    C --> D["Build<br/><small>minutes</small>"]
    D --> E["Pre-deploy<br/><small>minutes</small>"]
    E --> F["Runtime<br/><small>continuous</small>"]
    A -.- A1["IDE security linting"]
    B -.- B1["Secret detection"]
    C -.- C1["SAST · IaC scanning · dependency review · policy checks"]
    D -.- D1["Container image scan · SBOM generation · artifact signing"]
    E -.- E1["Signature and provenance verification · admission policy"]
    F -.- F1["DAST · runtime detection · continuous posture scanning"]
ControlWhat it findsFalse positive tendencyShould block?
Secret detectionCommitted credentialsLowYes, always
SASTCode-level vulnerability patternsHigh without tuningHigh severity only
SCA / dependency scanningKnown CVEs in dependenciesMedium — reachability mattersCritical/high with a fix available
IaC scanningInsecure infrastructure definitionsLowYes, for security-relevant rules
Container scanningOS and library CVEs in the imageMedium — many are unreachableCritical, with a base image update available
DASTRuntime-exposed vulnerabilitiesMediumNo — run against staging, ticket the findings
License scanningNon-compliant dependency licencesLowYes, for prohibited licences

Secret detection deserves special treatment

It is the one control that should block unconditionally, because the cost asymmetry is extreme: a committed credential is compromised the moment it is pushed, and Git history is effectively permanent.

Three layers:

  1. Pre-commit hook — fastest feedback, and prevents the secret from ever entering history. Bypassable, so not sufficient alone.
  2. Server-side push protection — rejects the push. Not bypassable by a local configuration.
  3. Continuous history scanning — finds what was committed before the controls existed, and what slipped through.

And the response must be rotate first, then clean history. Removing a secret from Git history does not un-compromise it; a scraper may have had it within seconds of the push. Teams that rewrite history and skip rotation have done the expensive half of the work and none of the protective half.

Making findings actionable

The difference between a security programme that works and one that gets disabled is almost entirely in how findings are presented.

Report only on changed code in a pull request. A developer who touched one file should not receive four hundred pre-existing findings. Deal with the existing backlog separately, as a prioritised project with its own owner.

Prioritise by exploitability, not by CVSS alone. A critical CVE in a dependency your code never calls is less urgent than a medium one on your authentication path. The signals that matter:

  • Reachability — is the vulnerable function actually invoked?
  • Exposure — is the component reachable from the internet?
  • Known exploitation in the wild — a small subset of CVEs are ever exploited, and those should jump the queue regardless of score.
  • Fix availability — a finding with no patch is a risk-acceptance decision, not a developer task.

Give every finding a fix. “SQL injection risk at line 42” is a task. “SQL injection risk at line 42 — use a parameterised query; here is the corrected line” is a fix. Where a tool can generate the patch as a pull request, it should.

Set thresholds by severity and let the rest through. Blocking on everything means blocking on nothing, because the block gets bypassed.

The backlog problem

Introducing scanning to an existing codebase produces a large number of pre-existing findings. The instinct is to fix them all before enabling enforcement; this delays enforcement indefinitely.

flowchart TD
    A[Enable scanning in report-only mode] --> B[Baseline: snapshot all existing findings]
    B --> C[Block only on NEW findings<br/>from this point forward]
    C --> D[Estate stops getting worse immediately]
    D --> E[Triage the baseline by exploitability]
    E --> F[Burn down critical and high<br/>as scheduled work with an owner]
    F --> G[Lower the blocking threshold<br/>as the backlog shrinks]

Stopping the bleeding first is the right order. A codebase that is not getting worse while the backlog burns down is a far better position than one waiting for a cleanup project that never gets prioritised.

Tuning out the noise

Every scanner produces false positives, and untuned scanners produce mostly false positives.

  • Suppress with a reason and an expiry, never silently. A suppression file entry should record who, why, and when it should be reconsidered.
  • Track the false-positive rate per rule. A rule generating 90% false positives should be disabled — it is consuming attention and providing negative value.
  • Tune before enforcing. Run in report-only mode for a few weeks, tune, then turn on blocking. Enforcing an untuned scanner is the fastest way to lose the organization’s cooperation.

Ownership

Findings without an owner are not work; they are a report. Route them automatically using the same service catalogue that drives incident routing — finding in repository X goes to the team that owns repository X, appearing in their normal backlog rather than in a security tool they do not open.

The security team’s job in this model is to own the rules, the thresholds, and the exceptions — not to chase individual findings across the organization.

Adoption checklist

  • Secret detection runs pre-commit, at push, and continuously over history.
  • Leaked credentials are rotated first; history cleanup is secondary.
  • Pull request findings are limited to changed code.
  • Prioritisation uses reachability, exposure, and known exploitation — not CVSS alone.
  • Every finding includes a concrete fix; automated fix pull requests where possible.
  • New scanners run in report-only mode and are tuned before enforcement.
  • Existing findings are baselined; only new findings block.
  • Suppressions require a reason and an expiry date.
  • Findings route to owning teams automatically via the service catalogue.

Last updated 19 Aug 2026, 00:00 UTC. history