TL;DR
The widely cited rule of thumb — popularised by Pressman (1987) and Boehm's defect-cost work — holds that fixing a defect found in development costs 1x, in QA 10x, and in production 100x. Shift left security is the practice of moving vulnerability detection as close to code authoring as possible. CI gates on automated security scans are the primary implementation mechanism.
The phrase "shift left" refers to the software development lifecycle viewed as a timeline from left to right: requirements, design, development, testing, staging, production. Moving security testing left means running it earlier in that sequence.
The "1x / 10x / 100x" cost curve is most often attributed to an IBM Systems Sciences Institute study, but the exact dollar figures (often quoted as $80–$240 in dev, $960–$7,600 in QA, $7,600–$80,000 in production) come from a frequently re-cited source that has not been independently verified — see Laurent Bossavit's analysis in The Leprechauns of Software Engineering. The directional pattern, however, is corroborated by NIST RTI International (GCR 02-805, 2002) and Capers Jones' industry surveys: defects cost dramatically more the later they are caught, and security vulnerabilities follow the same curve — with the additional cost of breach response, customer notification, and regulatory exposure if the issue is exploited before discovery.
The 100x multiple is not an abstraction. A SQL injection found in a code review takes one developer an hour to fix — a parameterized query, a test, a PR. The same SQL injection found after a breach involving 500,000 records involves forensic investigation, legal review, regulator notification, breach response retainer, customer communications, and likely civil litigation. The engineering fix is still one hour. Everything else is overhead from the delay.
The core implementation is simple: run security checks in your CI pipeline and gate on results.
A minimal implementation for a web application:
# .github/workflows/security.yml
name: Security Gate
on: [push, pull_request]
jobs:
security:
runs-on: ubuntu-latest
permissions:
security-events: write
steps:
- uses: actions/checkout@v4
- name: Dependency audit
run: npm audit --audit-level=high
# Fails the build on HIGH or CRITICAL dependency CVEs
- name: SAST scan
uses: semgrep/semgrep-action@v1
with:
config: p/owasp-top-ten
- name: Upload SARIF results
uses: github/codeql-action/upload-sarif@v4
with:
sarif_file: semgrep.sarifThis catches a class of vulnerabilities at PR time, before code reaches staging. The key is that the build fails — not just reports. A scan that runs but never blocks anything is not a gate; it is a report generator that nobody reads.
Not all security findings warrant failing a build. A practical gating policy:
| Severity | CI Gate Action |
|---|---|
| CRITICAL | Fail immediately — block merge |
| HIGH | Fail — block merge (with override process for exceptions) |
| MEDIUM | Warn — does not block, creates tracking issue |
| LOW / INFO | Log only — reviewed quarterly |
New-finding-only gating prevents a common failure mode: a codebase with 200 pre-existing medium-severity findings would fail every build if all findings gate. Most CI/CD platforms support a "new findings only" mode that compares against the last clean commit. Start there.
CI-integrated SAST covers what is visible in source code: injection patterns, hardcoded secrets, insecure cryptographic functions, dangerous API calls. What it does not cover: runtime behavior, business logic flaws, authorization failures, and any vulnerability that only manifests when code executes against a real application stack.
This is where DAST and pentest tooling fills the gap. The same gating logic applies — trigger on a deployed staging environment, consume SARIF output, gate on CRITICAL findings.
- name: Trigger BreachVex scan
run: |
SCAN_ID=$(curl -sf -X POST https://api.breachvex.com/v1/scans \
-H "Authorization: Bearer ${{ secrets.BREACHVEX_API_KEY }}" \
-H "Content-Type: application/json" \
-d '{"target": "https://staging.yourapp.com"}' | jq -r '.id')
echo "SCAN_ID=$SCAN_ID" >> $GITHUB_ENV
- name: Wait for scan completion and download SARIF
run: |
# Poll until complete, then download
curl -sf "https://api.breachvex.com/v1/scans/$SCAN_ID/sarif" \
-H "Authorization: Bearer ${{ secrets.BREACHVEX_API_KEY }}" \
-o breachvex.sarif
- name: Upload to GitHub Code Scanning
uses: github/codeql-action/upload-sarif@v4
with:
sarif_file: breachvex.sarifThe technical implementation is the easy part. The harder part is changing who owns security defects.
In traditional security models, a quarterly pentest finds vulnerabilities, a security team writes a report, and a ticket is filed with the engineering team to fix it — at some point. Accountability is diffuse, timelines are loose, and findings get lost in backlog.
Shift left changes the ownership model. When a vulnerability is found in a developer's PR, the developer owns it. The fix cycle is immediate. There is no handoff between security and engineering. The security team shifts from report-writing to toolchain ownership: maintaining the CI gates, tuning signal-to-noise, building the exception process.
Teams that operationalize this consistently find that security debt stops accumulating. The gate prevents new vulnerabilities from entering production. The remaining work is remediating the pre-existing backlog — which is finite and can be tracked.