SDL in CI/CD Pipeline
What This Is
A Security Development Lifecycle (SDL) defines the security activities required at each stage of building software - threat modeling at design time, secure coding standards during implementation, testing before release, and a response plan after deployment. Historically, SDL was a document teams referenced manually. Embedding it in CI/CD means SDL gates are enforced automatically at the point they're relevant, instead of relying on someone remembering to run through a checklist.
Mapping SDL Stages to Pipeline Stages
Design PR Opened Merge to Main Pre-Deploy Post-Deploy
│ │ │ │ │
▼ ▼ ▼ ▼ ▼
Threat model SAST + secrets SCA + full SAST DAST against Runtime monitoring
trigger for scan + lint scan + security staging + + incident
new features (fast, every sign-off for high- manual pentest response plan
commit) risk changes for major active
releases
Design Stage: Threat Modeling Trigger
Not every PR needs a full threat model, but certain changes should automatically flag one as required - a lightweight CI check can catch this:
# Flag PRs touching auth/payment/data-access code for mandatory threat model sign-off
- name: Check for high-risk paths
run: |
if git diff --name-only origin/main | grep -E '(auth/|payment/|pii/)'; then
echo "::warning::This PR touches a high-risk area - threat model review required before merge"
fi
See Threat Modeling for the STRIDE process itself.
PR Stage: Fast, Cheap Gates
Every PR, every time, should run in under a few minutes:
name: PR Security Gates
on: pull_request
jobs:
fast-checks:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Secrets scan
uses: gitleaks/gitleaks-action@v2
- name: SAST (fast subset)
run: semgrep --config auto --error
Merge Stage: Deeper, Slower Checks
Full SAST rule sets and SCA don't need to block every commit push, but should gate the merge to main:
name: Merge Gates
on:
push:
branches: [main]
jobs:
full-scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Full SAST scan
run: semgrep --config p/owasp-top-ten
- name: SCA
run: trivy fs --severity CRITICAL,HIGH --exit-code 1 .
See SCA in CI/CD Pipeline for the SCA step in depth.
Pre-Deploy Stage: Runtime Verification
Before promoting to production, run DAST against a staging environment and require a security sign-off for major releases - see DAST in CI/CD Pipeline.
Security Sign-Off as Code, Not a Meeting
Instead of a manual "security team approves the release" meeting, encode the sign-off criteria as required CI status checks in your branch protection rules: no open critical SAST/SCA findings, DAST scan passed, threat model completed for flagged high-risk changes. GitHub/GitLab branch protection can require these checks to pass before merge is even allowed - removing the human bottleneck for the common case, while still allowing an explicit manual override (logged) for genuine emergencies.
Best Practices
- Match gate cost to pipeline stage - cheap/fast checks on every commit, expensive/slow checks at merge or pre-deploy.
- Make high-risk paths self-flagging - don't rely on developers remembering which directories need extra scrutiny.
- Encode sign-off as branch protection rules, not a Slack approval - it's auditable and can't be skipped by accident.
- Keep an emergency override path, but log every use of it and review those logs regularly.