Also, you can find my book on building an application security program on Amazon or Manning
I want to pick back up on the topic of PaC (policy as code) and start showing how it can help with the securing the software supply chain. It’s no surprise that the software supply chain has been under increasing pressure over the years as attackers recognize that they can get more bang for their buck by poisoning the software “upstream” from their target.
And there is no shortage of examples of supply chain compromise. We’re familiar with Solarwinds, and Log4j over the past few years that sharpened everyone’s focus on this topic. But, to be clear, there have been plenty of other examples. In March 2023, over 600,000 users of 3CX's popular VoIP software unknowingly downloaded a trojanized update that gave attackers a backdoor into critical infrastructure across healthcare, aerospace, and hospitality sectors. And while this sounds like many of the other stories we’re familiar with, this one added an additional layer of flair.
North Korea's Lazarus Group didn't just target 3CX directly. Instead, they first compromised Trading Technologies' X_Trader software (discontinued in 2020 but still downloadable in 2022), which a 3CX employee downloaded onto a corporate device. That single infected installer gave attackers access to 3CX's build environment, where they injected multi-stage malware into legitimate 3CX updates, complete with digital signatures. A supply chain attack that triggered another supply chain attack? A security matryoshka doll of compromise!
This attack shows how our fundamental trust model can be exploited. 3CX didn't know they were compromised. Their customers didn't know the updates were malicious. The software appeared to be digitally signed (although that was through another vulnerability), distributed through official channels, and looked completely legitimate. And to add insult to injury, the organizations following best practices by religiously applying vendor updates were the ones who got infected first!
Every npm install, pip install, or Maven dependency we pull is essentially inviting strangers into our codebase. And unlike the dramatic SolarWinds headlines, most supply chain compromises happen quietly. Or at least until something significant breaks. To be clear, this is a broad problem. In 2024, malicious packages grew by 156% year-over-year, with attackers uploading hundreds of thousands of malicious packages to open-source repositories.
The math is simple: more dependencies + more malicious packages = exponentially higher risk.
The Dependency Dilemma
Today's applications are like a Lego build. A varied set of pieces and some instructions on how to put it together. While it’s great to finish that build and display it, modern software is far more complex. Each piece of dependent code can be developed from a different manufacturer. Each with their own security program (or not). Some of the dependencies come from large organizations, some from a small team of maintainers. And while the dependency maintainers could have excellent security practices, anywhere along that supply chain could have just one failure. What’s that saying about the weakest link?
So what do these failures look like in practice?
Package managers like npm, PyPI, and Maven facilitate installation and management of dependencies, but developers need automation to identify and update vulnerable components. It’s not practical to do this manually.
Open source components can become vulnerable over time as technology changes and attackers learn more about the component. However, software manufacturers often neglect dependencies or fail to upgrade them appropriately often because it “just works” as is.
Supply chain attacks continue to become more sophisticated with attackers exploiting dependency confusion, typosquatting, and directly compromising legitimate packages to inject malware into the software supply chain.
Most organizations can't answer basic questions like "What version of Log4j are we running?" or "Which of our applications use this vulnerable npm package?" This isn't just a tools problem, it's an asset management and visibility problem.
While there are solutions and tools to help with managing dependencies, we can also continue to mature our PaC practices to reduce our supply chain risks.
Automated Dependency Governance
In part 1 of this series on PaC I showed an example of how to prevent root containers in your IaC. Here I’ll show how we can create policies that act as intelligent gatekeepers for our supply chain.
Instead of hoping developers make good choices, we can codify supply chain security into enforcement. Going back to our code using Rego and OPA we can create a simple dependency policy example:
deny[msg] if {
some vuln in input.vulnerabilities
vuln.severity == "CRITICAL"
msg := sprintf("Critical vulnerability found: %s in package %s@%s", [vuln.cve, vuln.package, vuln.version])
}
In this example a check is made to see whether there are critical vulnerabilities in the SBOM. This can be used as a security gate in the CI/CD pipeline where the build would generate an SBOM that can be enriched with vulnerability data from public sources like the NVD. From here the policy evaluation would occur and the build would fail if a critical vulnerability is found in the dependencies.
I created a broader sample of this vulnerability check with an SBOM that I ran through OWASP Dependency Track to get the CVE details. You can find the Rego file here and the sample (stripped down) SBOM here. To test it, you can run a local version of OPA, or use the Rego Playground to test:
While this is very simplistic in its current state, it can be extended to be more than a check against the NVD. Organizations can maintain their own threat intelligence repository from other sources like OSV, GitHub Security Advisory, or commercial feeds. Additionally, depending on what source of information you use, you may extend this policy check to include specific component versions, license compliance, hash validation, and more.
Unlike security checklists that are often poorly implemented, or alert fatigue from the vast amount of security tools, PaC policies enforce decisions automatically, through build failures, while providing clear reasoning. More importantly, the team can keep components from going into production that violate the security policies.
Lifecycle Integration
Where does this leave us with integration into our existing SDLC? We can start by looking for ways to integrate policies into our development stages starting with the build stage (where we have the best opportunity to block bad components):
npm/yarn: Policies can be used to evaluate package-lock.json and yarn.lock for vulnerability thresholds, license compliance, and dependency age.
Python/pip: SBOM generation from requirements.txt with policy validation against PyPI security advisories where can enforce minimum version thresholds, exclude known vulnerabilities, and apply license filters.
Maven/Gradle: Dependency tree analysis with policies enforcing internal artifact repositories and version pinning while validating against known vulnerability indexes.
Container Dependencies: Base image validation, multi-stage build security, and package manager policies within containers with SBOM + CVE mapping.
Testing Policies
Projects using SBOMs to manage open source dependencies showed a 264-day reduction in mean time to remediate vulnerabilities compared to those without SBOMs according to SonaType’s State of the Software Supply Chain. This means that we should not just focus on generating SBOMs, but also test that our policies follow best practices to ensure they are valid when needed. For instance:
Validate SBOM completeness and format compliance (SPDX, CycloneDX)
Cross-reference vulnerability databases (NVD, GitHub Advisories, OSV)
Enforce dependency update policies and security patch requirements
Should a policy check fail the policy and the inputs that it is checking need to be reviewed for validity and corrected. You don’t want to find out that your SBOM is trash during the middle of an incident.
Continuous Supply Chain Monitoring
While OPA is not a run-time scanner, the OPA APIs can be used to validate the environment vs an SBOM with a little bit of work or the use of the tools in the infrastructure. One example is using a sidecar that watches SBOMs that are posted to a shared volume, or registry. From here you can trigger alerts, workflows, or even a shutdown of the pod or container in critical scenarios.
This is ideal for a case where a zero-day is discovered and your team needs to identify the locations of all affected components. Developers push hotfixes, containers rebuild with updated base images, and the SBOMs generated at build time can become stale in short order. Runtime policies bridge this gap by continuously reconciling what's supposed to be running against what's actually running. Bottom line, the runtime policies should:
Compare runtime dependencies against approved SBOMs
Detect dependency drift and unauthorized package installations
Validate cryptographic signatures and attestations continuously
When supply chain incidents occur, having dependency policies in place enables a rapid response where policies can be used to identify affected components and applications, help understand blast radius, and inform appropriate response procedures.
The Path Forward
Supply chain security isn't just about scanning for vulnerabilities, it's about arming the team with dependency awareness backed by automated policy governance. This is where a PaC can help limit the impacts of a weak link in the supply chain that can lead to a catastrophic day.
Also, you can find my book on building an application security program on Amazon or Manning