On February 4, 2022, NIST finalized Special Publication 800-218, the Secure Software Development Framework (SSDF), organizing secure development into four practice groups — Prepare, Protect, Produce, and Respond — that map cleanly onto the phases every engineering team already runs: design, build, test, and ship. The framework didn't appear in a vacuum. It followed Executive Order 14028 from May 2021, which pushed software supply-chain requirements like SBOMs and provenance attestation into federal procurement, and it arrived a few months before the industry got two of the starkest lessons in why phase-specific gates matter. Log4Shell (CVE-2021-44228) was privately reported to Apache on November 24, 2021, and publicly exploited within weeks — a single transitive logging dependency that dependency-scanning gates would have flagged in minutes, not days. And in March 2024, Andres Freund discovered a backdoor deliberately built into XZ Utils (CVE-2024-3094) over roughly three years of social-engineering maintainer trust, a supply-chain compromise that no vulnerability scanner would have caught because the malicious code wasn't a known CVE — it needed a code-review and provenance gate instead. This guide walks through where each type of security gate belongs in the SDLC, and why the wrong placement makes it nearly useless.
Why does gate placement matter more than gate choice?
Gate placement matters more than gate choice because the same control catches fundamentally different classes of risk depending on when it runs. A SAST scan run only at merge-to-main catches a SQL injection pattern before release, but a threat-modeling exercise run at that same late stage can't change an architecture decision that's already been implemented — by then, "add an authorization layer between these two services" is a rewrite, not a review comment. NIST's SSDF frames this explicitly: the Prepare group (define security requirements, threat model, choose secure defaults) precedes Protect and Produce (harden the toolchain, gate the code) precisely because architectural risk decisions compound in cost the later they're caught. A missed input-validation requirement at design time might cost an hour of discussion; the same gap caught by a DAST scan after deployment costs an incident response.
Where does threat modeling belong, and what does it actually gate?
Threat modeling belongs at design time, before a line of implementation code exists, and it gates architecture decisions rather than code patterns. Structured approaches like STRIDE (Microsoft's Spoofing, Tampering, Repudiation, Information disclosure, Denial of service, Elevation of privilege model, created by Loren Kohnfelder and Praerit Garg in 1999) walk a team through a data-flow diagram and ask, for each trust boundary, which of those six categories applies. This is the only gate in the SDLC that can catch a missing authentication boundary or an overly broad trust assumption — the kind of flaw no scanner detects because it isn't a code defect, it's a design gap. NIST's SSDF places this under the Prepare practice group (PO and PS control families) for exactly this reason: requirements and threat models are inputs to everything downstream, not outputs of it. Skipping this gate doesn't eliminate the risk category; it just defers discovery to a SAST or DAST finding, or worse, to an incident.
What should a SAST gate actually block on?
A SAST gate should block on a small, CWE-mapped set of high-confidence findings in code an attacker can actually reach — not on every pattern match a scanner produces. Static analysis traces untrusted input from a source (a request parameter, a CLI argument) to a dangerous sink (a SQL query, a command execution call) across functions and files, and tools that report the CWE/OWASP mapping alongside the dataflow trace let a team write a precise policy like "block on CWE-89 SQL injection or CWE-502 unsafe deserialization above a defined severity" instead of drowning developers in low-confidence noise. Safeguard's SAST engine is built to produce exactly this kind of finding — a source-to-sink dataflow trace with CWE mapping and severity — so a pipeline gate can act on specific, attributable findings rather than raw pattern counts. The placement question for SAST is simple: it should run on every pull request, not just nightly, because a finding surfaced at commit time costs a one-line fix; the same finding surfaced after merge costs a follow-up PR and a re-review.
Why can't a dependency-vulnerability count alone drive a release gate?
A raw dependency-vulnerability count can't drive a release gate because most flagged CVEs sit in code paths an application never executes, and gating on volume alone trains teams to ignore the gate. Industry SCA vendor research has repeatedly found that a large majority of CVEs surfaced by standard software composition analysis scans — commonly cited in the 70-95% range across studies — sit in functions that are never called at runtime in the scanned application. Log4Shell is the counterexample that justifies keeping the gate at all: it was both reachable and trivially exploitable, and organizations without an SCA gate in their build pipeline spent the following weeks manually grepping for log4j-core across every repository they owned. The fix is severity- and context-aware triage rather than raw counting — Safeguard's SCA engine scores each CVE finding against the affected component and severity so a gate can block on "above severity X in a directly used component" instead of every match against a manifest.
What does a code-review gate catch that automated scanning cannot?
A code-review gate catches intent and provenance issues that automated scanning structurally cannot, because scanners look for known bad patterns, not for a trusted maintainer's account being used to insert something new. The XZ Utils backdoor is the reference case: the malicious code was crafted specifically to evade casual review and static analysis, hidden in build-system test artifacts rather than in the obviously reviewed source files, and it was ultimately caught by a human — Andres Freund — noticing anomalous SSH login latency, not by a scanner. This is why SSDF's Produce practice group pairs automated gates with mandatory human review and why supply-chain frameworks increasingly ask for provenance attestation (e.g., SLSA levels) alongside vulnerability scanning: a scan tells you a component isn't on a known-bad list, an attestation and a review tell you who built it and whether the change makes sense. A code-review gate should require at least one reviewer with no authorship stake in the change, and should never be waivable for dependency or build-configuration changes specifically, since those are exactly the files attackers target when a repository's source files are well-reviewed but its build scripts are not.
How should these gates be enforced without stalling delivery?
These gates should be enforced through tiered actions — block, warn, notify, and require-approval — rather than a single binary pass/fail, because uniform blocking either stops delivery on low-risk findings or gets disabled entirely the first time it blocks a hotfix. A workable model ties action to confidence and severity: a reachable, high-severity SAST or SCA finding blocks the pipeline; a lower-confidence or unreachable finding warns and files a ticket; anything a team disputes goes through an explicit exception workflow with a business justification, an expiration date, and an audit trail rather than a silent override. Safeguard's policy-and-gate model implements this directly — a policy defines a matching condition and a severity threshold, links to a gate, and evaluates on every pipeline run, so teams can route findings to a block, warn, or notify action instead of a single pass/fail outcome, with exceptions tracked rather than left to individual discretion. The goal isn't zero findings at every gate; it's that the findings that reach production are the ones a human explicitly decided were acceptable, on the record, with an expiration date attached.