Safeguard
Application Security

Designing an application security program from first principles

Two backdoors nine years apart — event-stream in 2018, XZ Utils in 2024 — show why code-only AppSec programs fail. Here's a four-layer framework built from scratch.

Safeguard Research Team
Research
7 min read

On March 29, 2024, PostgreSQL developer Andres Freund noticed SSH logins on a Debian sid box were taking about 500 milliseconds longer than they should have, and burning extra CPU in valgrind while he did it. That small anomaly led him to a backdoor planted inside xz-utils versions 5.6.0 and 5.6.1 by a contributor operating under the name "Jia Tan," a supply-chain compromise later assigned CVSS 10.0 and tracked as CVE-2024-3094. Six years earlier, on September 9, 2018, a different attacker calling themselves "right9ctrl" had gained co-maintainer access to the widely used npm package event-stream and quietly added a dependency, flatmap-stream, whose payload targeted the Copay Bitcoin wallet and went undetected for roughly two and a half months and eight million downloads before researcher Ayrton Sparling flagged it. Neither incident was caught by a linter, a container scan, or a cloud misconfiguration rule — both lived in the dependency layer, the one most AppSec programs still bolt on last. That is the core problem this post addresses: most application security programs are built by accumulating tools, not by reasoning about where risk actually enters a system. Below is a four-layer framework — code, dependencies, containers, and cloud/runtime — built from first principles, with the historical incidents and standards that justify each layer.

Why start with code instead of infrastructure?

Code is where intent turns into instructions, so it's the first layer where a defect becomes exploitable, and it's the layer with the most mature tooling and taxonomy to lean on. The OWASP Top 10, last revised in 2021, remains the standard reference for classifying app-layer flaws — broken access control, cryptographic failures, and injection sit at the top, and every static analysis (SAST) tool on the market maps its findings back to that list or to the underlying CWE catalog it draws from. A first-principles program treats SAST and secrets scanning as gates at the pull-request stage, not as a nightly batch job, because a finding caught before merge costs a comment while the same finding caught in production costs an incident review. Code review gates matter here too: automated tooling catches pattern-level flaws, but logic errors — the kind where access control is technically present but wrong — still need a human reviewer who understands the business rule being enforced. The mistake most programs make isn't skipping this layer; it's stopping here, treating a clean SAST scan as equivalent to a secure application.

Why does dependency risk deserve its own layer instead of living inside "code"?

Because the vast majority of code running in a modern application was never written by the team shipping it, and the two most damaging software supply-chain incidents of the last decade both originated in a dependency, not in first-party code. The event-stream compromise worked because flatmap-stream's malicious payload was AES-256-encrypted and keyed on the npm_package_description field of the specific application importing it — Copay — so it stayed dormant and undetected in every other project that happened to pull it in transitively. The XZ Utils backdoor was more elaborate still: it modified the build process of liblzma to inject malicious code into sshd on affected Linux distributions, and CISA's guidance afterward was simply to downgrade below the compromised 5.6.x line. Software Composition Analysis (SCA) exists to catch the more mundane version of this problem — known-CVE dependencies — but a first-principles program has to plan for the harder case too: a dependency that isn't vulnerable, it's malicious, planted deliberately by someone who spent months earning maintainer trust.

What does a software bill of materials actually buy you?

An SBOM turns "what's in this build" from a question you re-derive by grepping lockfiles into a query you run against a stored inventory, and in the United States it stopped being optional practice once Executive Order 14028, signed in May 2021, pushed software bills of materials into federal procurement requirements. NIST followed with the Secure Software Development Framework, SP 800-218, finalized in February 2022, which lays out the practices — including third-party component verification — that EO 14028 expects vendors to demonstrate. In CycloneDX or SPDX format, an SBOM records every direct and transitive component, its resolved version, and increasingly its provenance and attestation status. That last part matters more after XZ Utils than it did before: the backdoor was hidden in the distributed release tarball but not fully present in the GitHub source tree, meaning a build system without provenance verification had no way to know the artifact it consumed didn't match the source it could audit. Safeguard tracks SBOM completeness against the NTIA minimum elements and SSDF practices like PW.6 (verify third-party components) directly, so an EO 14028 compliance gap shows up as a scored, per-project status rather than a manual audit exercise.

How do you know a dependency's attestation is real, not just present?

You verify it against a defined trust ladder, because "the package is signed" and "the package's build was reproducible from an audited source" are very different guarantees, and conflating them is exactly the gap that let XZ Utils ship a backdoored tarball while its source repository looked comparatively clean. The SLSA (Supply-chain Levels for Software Artifacts) framework formalizes this ladder — from documented build processes at the lower levels up to hermetic, two-party-reviewed builds at its highest tier — and Sigstore's cosign and Rekor transparency log give teams a practical way to sign and publicly log artifacts without managing long-lived private keys. A first-principles program assigns every component an attestation tier and treats "unknown origin" as its own risk category, distinct from "known-vulnerable." Safeguard implements this as SCAL (Safeguard Component Attestation Level), a 0–5 scale from fully attested, reproducible builds down to components whose origin can't be verified at all, feeding into a per-component Risk Score inside Explore SBOM so a SCAL 5 package with no CVEs still surfaces as risk rather than disappearing because it passed a CVE-only scan.

Why do containers need scanning separate from the application and its dependencies?

Because a container image bundles an entire OS layer — kernel utilities, shared libraries, package managers — that your SCA tool never sees if it only reads requirements.txt or package.json. Base-image scanning exists specifically to catch CVEs in that OS layer, and the industry response to bloated, high-surface-area base images has been a steady move toward minimal and distroless images that ship a runtime and nothing else — no shell, no package manager, nothing an attacker who gains code execution can pivot with. Signed image attestations, again via Sigstore/cosign and SLSA provenance, extend the same trust-ladder logic from the dependency layer to the artifact a cluster actually pulls and runs, closing the gap where a scanned, approved source tree gets rebuilt into an image nobody re-verifies before deployment.

What closes the loop at the cloud and runtime layer?

Policy-as-code gates and least-privilege runtime controls, because everything upstream — code, dependencies, containers — is a static judgment about an artifact, and cloud is where that artifact actually gets permissions, network reach, and data access. Infrastructure-as-code scanning catches a misconfigured IAM policy or an open security group before it's applied, the same way SAST catches a flaw before it merges; policy-as-code gates then enforce those same rules automatically on every deploy rather than relying on a manual review. Safeguard's Policies & Gates engine applies this logic to the layers it directly governs — SBOM-derived conditions like CVSS thresholds, EPSS-informed exploitability, license compliance, and required SLSA/SCAL attestation levels can all block a deployment automatically, with a documented exception workflow for the cases where a business justification genuinely outweighs the risk. The throughline across all four layers is the same: don't just detect a class of risk, decide in advance what you'll do when you find it, and make that decision enforceable rather than aspirational.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.