Secure SDLC Gates & the DevSecOps Maturity Model
An earlier chapter in this blueprint, DevOps Foundations & the CDP Toolchain, inventoried the tools. The secure SDLC — required reading before this page, not optional background — mapped security activity onto SDLC phases in the abstract: requirements, design, development, testing, deployment, operations. The CDP exam does not test that map directly. It drops you into a live pipeline and asks whether a specific check is running at the specific stage where it can actually stop something, and whether you can tell a gate that's merely present from a gate that's actually doing its job. This chapter is entirely about that mechanical layer: the five points in a real CI/CD pipeline — pre-commit, pull request, build, deploy, and runtime — where a gate can physically live, and how to use OWASP's DevSecOps Maturity Model (DSOMM) to reason about how mature any one of those gates actually is, instead of treating "do we have a scanner" as a yes-or-no question.
A school day has four checkpoints for gum, and they are not equally good. Checkpoint one is your own conscience at your locker — you could just not spit it out, and nobody's watching, so this one is really just a courtesy. Checkpoint two is the teacher checking backpacks at the classroom door — harder to sneak past, and it's the first one that actually works. Checkpoint three is a surprise assembly sweep by the vice principal — impossible to dodge on purpose. And checkpoint four is the janitor finding a piece stuck under a desk three weeks later, no matter what got past the first three. Four checkpoints, four different jobs: the locker check saves everyone time, the classroom door is where enforcement actually starts, and the janitor's job only exists because the other three aren't perfect. A pipeline has almost exactly this shape — it's just called pre-commit, PR, build/deploy, and runtime.
From the phase map to pipeline mechanics
☺ Like you're 10: The secure SDLC page told you which kind of check belongs at each life-stage of a feature; this page tells you exactly where, physically, in your real pipeline that check has to sit for it to actually work.
Requirements, design, development, testing, deployment, operations — that six-phase map is the right way to think about what kind of security activity belongs where in a project's lifecycle, and if any of it is unfamiliar, go read the secure SDLC first; this page assumes it. But a phase is a concept, not a place a YAML file can point to. A real pipeline has a much smaller, much more concrete set of physical locations a check can actually run: a git hook on a developer's laptop, a CI job triggered by a pull request, a CI job that runs after merge, a CD job that promotes an artifact toward production, and the running system itself once it's live. Security in CI/CD already covered two things this chapter assumes you know — the distinction between a gate that hard-fails a build and one that merely warns, and the fact that the pipeline itself is a privileged system worth protecting (least-privilege credentials, signed commits, ephemeral runners). This chapter doesn't repeat that; it answers a narrower and more mechanical question: at which of these five physical points does a given check belong, why can't it run any earlier, and how do you judge whether it's mature enough once it's there?
The five points where a gate can physically live
☺ Like you're 10: Not every checkpoint is built the same way — some can be skipped by the person being checked, and some can't, and that difference is the whole story.
Every control this course covers — SAST, DAST, SCA, secrets detection, IaC scanning, image signing, admission policy, runtime detection — has to attach to one of exactly five physical points. The table below is the one to memorize cold; it's the skeleton the rest of this chapter, and several chapters after it, hang off of.
| Stage | Where it physically runs | Typical checks | Can the person being gated skip it? |
|---|---|---|---|
| Pre-commit | A git hook on the developer's own machine, before git commit completes | Secrets scan (gitleaks), a fast linter, a narrow Semgrep rule subset | Yes — git commit --no-verify skips every local hook |
| Pull request | A CI job triggered on pull_request, evaluated by branch protection before merge is allowed | Full SAST, SCA, a server-side secrets re-scan | Only an admin with branch-protection override — should be logged and alerted on |
| Build / package | A CI job after merge, before the artifact is pushed to a registry | Container image scan, IaC scan, SBOM generation, image signing | No, if the pipeline is the only credentialed path to the registry |
| Deploy | The CD stage and the cluster's own admission boundary | DAST against staging, policy-as-code admission control, promotion approval | No, if cluster RBAC and admission control are correctly locked down |
| Runtime | The running system, continuously, after deploy | Cloud/cluster posture scanning, runtime threat detection, automated response | N/A — it's detection, not a gate a change passes through |
The first gate that can't be bypassed by the person it's gating is the first one that provides an actual security guarantee. Everything upstream of that point — a pre-commit hook, an IDE plugin, a linter on save — is a courtesy that saves round-trips and keeps noise out of CI. It's genuinely valuable, but it is not a control you can rely on, because relying on it means trusting every developer to never run --no-verify under deadline pressure. Exactly one of them ever will.
Pre-commit and the PR gate: the shift-left half
☺ Like you're 10: Two checkpoints happen before anyone else even sees the code — but only one of them is actually a wall.
Pre-commit hooks, run through a framework like pre-commit or Husky, are the cheapest feedback a developer will ever get: milliseconds to a few seconds, on exactly the lines that changed, before the change has even left their laptop. A typical config wires a secrets scanner and a narrow static-analysis pass into every commit:
# .pre-commit-config.yaml
repos:
- repo: https://github.com/gitleaks/gitleaks
rev: v8.18.4
hooks:
- id: gitleaks
- repo: https://github.com/returntocorp/semgrep
rev: v1.78.0
hooks:
- id: semgrep
args: ["--config=auto", "--error"]The catch is the one already in the table above: git commit --no-verify skips it entirely, and nothing about the local hook stops that. This is why the same secrets check has to run again, server-side, at the PR gate — a developer can opt out of the courtesy, but they cannot opt the pull request out of a required status check they don't control. Branch protection is the actual enforcement mechanism: a CI job posts a status for each check, and the repository is configured to refuse merge until every required status is green.
# .github/workflows/pr-gate.yml (excerpt) — each job name below is
# marked "required" in the repo's branch-protection settings
jobs:
sast:
steps: [ { run: "semgrep ci --config=auto" } ]
sca:
steps: [ { run: "snyk test --severity-threshold=high" } ]
secrets:
steps: [ { run: "gitleaks detect --no-git -v" } ]Notice what's not in that PR job: DAST. That's not an oversight — DAST needs a running instance to attack, and nothing has been deployed anywhere yet at PR time. This is a common exam trap in its own right: a scenario that asks you to add DAST as a required PR check is asking you to do something that is not merely a bad idea but structurally impossible. See Static Analysis & Secrets Detection and Software Composition Analysis in Depth for how to actually tune the SAST and SCA jobs above so they're accurate enough to block on — this chapter only places them.
"We run SAST in CI" and "SAST blocks a critical finding from merging" are two different claims, and the gap between them is where most teams quietly live. A scan that runs and posts a comment nobody reads is not a gate — it's a report. If the job isn't listed as a required status check in branch protection, a critical finding and a green merge button can coexist. Before you credit any control on this page as a "gate," confirm it's wired into branch protection or an admission controller, not just wired into the pipeline.
Build and deploy: where the artifact itself gets checked
☺ Like you're 10: Once the code has actually been turned into a real package, you check the package itself — what's inside it, and who's allowed to vouch for it.
The build stage runs after merge, on the artifact that's about to become a release candidate — a container image, most commonly. Four things typically happen here, in this order: an image scan (Trivy or Grype) against the built layers, an IaC scan (Checkov or tfsec) against any Terraform plan the same pipeline is about to apply, an SBOM generated from the exact artifact that was built (not a manifest file — the real thing), and finally a signature over that artifact so anything downstream can verify it came from this pipeline and hasn't been altered since.
# build stage — SBOM, then sign what actually got built syft acme/checkout:1.4.3 -o cyclonedx-json > sbom.json trivy image --severity CRITICAL,HIGH --exit-code 1 acme/checkout:1.4.3 cosign sign --yes acme/checkout:1.4.3 cosign attest --yes --predicate sbom.json --type cyclonedx acme/checkout:1.4.3
Deploy is where two different kinds of gate meet. DAST becomes possible for the first time here, because it's the first point a running instance exists to attack — zap-baseline.py against a freshly deployed staging environment is the common shape. And the cluster's own admission controller enforces policy as code at the exact moment a workload is scheduled, independent of whatever the CI pipeline decided:
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: require-image-signature
spec:
validationFailureAction: Enforce # Audit while tuning, Enforce once trusted
rules:
- name: verify-cosign-signature
match: { resources: { kinds: ["Pod"] } }
verifyImages:
- imageReferences: ["registry.acme.io/*"]
attestors:
- entries: [ { keys: { publicKeys: "..." } } ]That admission policy is what actually closes the loop on the build-stage signature above — a signed image that nothing ever checks is just a decoration. See Infrastructure as Code Hardening for the IaC-scan side of this stage in depth, and the Kyverno and Sigstore & cosign tool pages for the mechanics behind the snippets above; the policy-as-code drill has you write one of these from scratch.
Runtime: the gate that can't block, only catch
☺ Like you're 10: Once something's already running, you can't un-ship it by refusing a merge — the best you can do is notice fast and react.
Runtime doesn't belong in the same category as the previous four stages, and the exam rewards knowing exactly why. A pre-commit hook, a PR check, a build scan, and an admission policy all sit between a change and production — they can refuse to let something through. Runtime monitoring sits after production. There is no merge left to block and no admission decision left to make; a tool like Falco watching syscalls, or a continuous cloud-posture scan, can only detect a problem and trigger a response — an alert, an automated rollback, a quarantine — after the thing it's watching for is already true. That's not a design flaw; it's what makes runtime necessary in the first place, echoing the point What is DevSecOps? makes about shift-left being "also," not "only" — a leaked credential in a log stream, a fresh CVE against a dependency that was clean when it shipped, or a misconfiguration introduced by a live change none of the first four gates ever saw. Cloud security posture covers the continuous-scanning half of this in depth, and the Container Runtime Security and Detection Engineering & Security Observability deep dives cover the detection tooling itself; what a runtime finding actually triggers downstream is covered in Vulnerability Management & Triage and, for a full incident, incident response & forensics.
Reasoning about maturity with DSOMM instead of an all-or-nothing checklist
☺ Like you're 10: Instead of asking "do we do SAST, yes or no," DSOMM asks "how far along is our SAST, on a scale, and how far along is our secrets detection, separately" — so two teams can both be genuinely doing DevSecOps while looking nothing alike.
The secure SDLC already introduced two program-level maturity models — SAMM, which is prescriptive, and BSIMM, which is descriptive. OWASP's DevSecOps Maturity Model (DSOMM) is a third, and it answers a different question than either: not "how mature is the organization" but "how mature is this one specific, concrete activity, independent of every other one." DSOMM organizes activities into dimensions — Build and Deployment, Culture and Organization, Implementation, Information Gathering, and Test and Verification — and inside each dimension, a specific practice like "SAST" or "secrets detection" is its own line item, scored on its own four-level scale, with each level defined by a concrete, checkable criterion rather than a vague adjective like "good." The project's exact current activity list and level wording live at the OWASP DSOMM site and have been revised across releases — treat the table below as illustrative of the model's shape, and check the live version before you cite exact wording on the exam.
| Activity | Level 1 | Level 2 | Level 3 | Level 4 |
|---|---|---|---|---|
| Secrets detection | A developer runs a scanner manually, occasionally | Automated in CI on every PR, non-blocking | Blocking locally and at the PR gate, non-bypassably | Structurally prevented — short-lived credentials injected at runtime, nothing left in the tree to leak |
| SAST | Ad hoc scan against a snapshot, run by a security engineer | Automated on every PR, results posted, no merge block | Required status check, ruleset tuned so false positives don't stall the team | Feedback in the IDE as the developer types; fix-time and false-positive metrics feed ruleset tuning |
| SBOM generation | Generated by hand, only when a customer or auditor asks | Automated at build, produced as a build artifact | Signed and attached to the image as an attestation, queryable per release | Continuously correlated against new CVE disclosures, feeding vulnerability management automatically |
The point of scoring per-activity, independently, is that it's honest about how real programs actually invest: a payments team might sit at Level 4 on secrets detection and Level 2 on SAST, deliberately, because a leaked credential is a worse day than a missed medium-severity static finding. A checklist that only records "SAST: yes/no" erases that distinction entirely and can make a team that's shallow-but-broad look identical to one that's deep-but-narrow. See Maturity Models: DSOMM, SAMM & BSIMM for the full three-way comparison, including which one a given audit or exam scenario is actually asking about.
DSOMM's granularity is the whole point: it replaces "are we doing DevSecOps" — a question with no useful answer — with "which of roughly a dozen concrete activities are we investing in, and how far, given our actual risk profile." A team is never simply "mature" or "immature" under this model; it has a profile, and the interesting exam question is always about a specific cell in that profile, not the average.
Common exam traps in this chapter
☺ Like you're 10: These are the specific ways this chapter tries to trick you — memorize the trap, not just the fact.
- A pre-commit hook is never the answer to "how do we guarantee X." It's bypassable by design (
--no-verify); if a scenario needs a guarantee, the answer is the PR gate or later, never pre-commit alone. - DAST cannot be a required PR check. It needs a running instance, and nothing is deployed at PR time — a scenario proposing this is testing whether you notice the impossibility, not whether you'd approve of the idea.
- "The scan runs" and "the gate blocks" are not the same claim. A non-required CI job can go red forever without stopping a single merge — always check whether the job is wired into branch protection or admission control.
- Runtime tooling is not a gate. It's detection after the fact. A scenario that asks you to "block" something at runtime is asking for a response action (quarantine, rollback), not a merge/deploy refusal.
- DSOMM levels are not meant to be uniform across activities. A team correctly at Level 4 on one activity and Level 1 on another isn't behind — it's expressing a risk-based choice. Don't read an uneven DSOMM profile as a failure state by default.
- DSOMM, SAMM, and BSIMM answer three different questions. Per-activity technical maturity, prescriptive org-wide roadmap, and descriptive industry benchmark, respectively — a question naming one by name is not asking about either of the other two.
Benny: Relax, Timmy — I ran gitleaks before I committed. We're covered.
Timmy: You ran it, or the hook ran it and you let it? Because --no-verify exists, and I've watched you use it before a deadline.
Benny: ...once. Fine, twice. But it's still scanned again at the PR, right?
Professor Owl: Which is exactly why pre-commit isn't where the guarantee lives. The PR gate is the first checkpoint you can't opt out of — that's the boundary that matters.
Foxy: So why do we even bother with pre-commit if it doesn't guarantee anything?
Timmy: Because catching it in two seconds on your own machine beats catching it in CI five minutes later, in front of the whole team. It's not the wall. It's the courtesy before the wall.
Professor Owl: And notice neither of us said your SAST and your secrets detection have to be at the same maturity level to count. Ours aren't. That's DSOMM working as intended, not a gap to apologize for.
This chapter placed the gates; it deliberately didn't go deep on tuning any single one of them so it didn't have to repeat what Security in CI/CD and SAST, DAST & SCA already cover. The rest of this blueprint does that depth work one gate at a time — Static Analysis & Secrets Detection and Software Composition Analysis next, then Dynamic Analysis in Practice, Infrastructure as Code Hardening, Compliance as Code at Scale, and Vulnerability Management & Triage. Each one assumes you already know where its gate physically sits — that's what this chapter was for.
1. Name the five physical points a gate can live at, in order, and identify the first one a developer cannot bypass on their own. 2. Why is it structurally impossible to make DAST a required pull-request check? 3. A team says "we run SAST in CI." What one follow-up question determines whether that's actually a gate? 4. Why is runtime monitoring not classified as a "gate" the way the other four stages are? 5. Under DSOMM, is a team at Level 4 on secrets detection and Level 1 on SAST necessarily behind? Why or why not?
Check your answers
- Pre-commit, pull request, build/package, deploy, and runtime. The pull-request gate is the first one the developer being checked cannot skip on their own — pre-commit can always be bypassed with
git commit --no-verify. - DAST needs a running instance of the application to send real requests against, and nothing has been deployed anywhere at pull-request time — there is no live target for it to attack yet.
- "Is that job listed as a required status check in branch protection?" A scan that runs and reports without being required can go red indefinitely without ever blocking a merge — it's a report, not a gate.
- Because it sits after production rather than between a change and production — there's no merge or admission decision left to refuse. It can only detect a problem that's already true and trigger a response (alert, rollback, quarantine), not prevent one.
- No. DSOMM scores each activity independently on purpose, so an uneven profile reflects a deliberate, risk-based investment choice (secrets leaking is treated as worse than a missed static finding, say) rather than a failure to reach uniform maturity everywhere.