How we are building images with zero CVEs #2: compiling from source

This is the second post in the technical series about the engineering decisions behind Quor's images. In the previous article, we explored how the choice of base image, Alpine and distroless, structurally reduces the attack surface. But reducing the number of packages is not enough. Even with few packages, each of them carries a risk that most security teams do not see: the time between a fix being merged upstream and that fix actually reaching your image. This article is exactly about this gap and how compiling directly from the source code eliminates it completely.

Security Researcher

Heitor Gouvêa

The problem nobody visualizes

When a vulnerability is fixed in an open source project, the path to its production image is not direct. It has multiple stages, and each one has its own timing.

The typical flow works like this:

  1. Fix is merged upstream (for example, in the OpenSSL, Node.js, or Python repository);

  2. Distribution maintainers (Debian, Ubuntu, Alpine) bring this new version with the fix to their packages;

  3. Your CI/CD rebuilds the final application image;

  4. Deployment happens and the fix finally reaches production.

Under ideal conditions: disclosure coordination, fast maintainers, CI configured to detect base changes, this flow takes between one and four weeks. Under real conditions: it takes much longer.

A simple analysis tracking six high-impact vulnerabilities from 2021 to 2024 revealed a consistent pattern: even in cases of best coordination, where the package updated with the patch on the same day as upstream, container images remained exposed for one to four weeks. In the worst-case scenario, months.

To illustrate:

CVE

Fix available upstream

Patch in distro

Real exposure window

CVE-2024-6119 (OpenSSL)

Day 0

Day 0

7–28 days (Docker image)

CVE-2023-38545 (curl SOCKS5 heap overflow)

Day 0

Day 0

7–14 days

CVE-2021-44228 (Log4Shell, CVSS 10.0)

Day 1

Day 1–7

14–60+ days

CVE-2023-44487 (HTTP/2 Rapid Reset)

Day 0 (Tomcat)

43 days (nghttp2)

7–50+ days

This window has a name: patch gap. And it exists not due to negligence, but by design.

Why the patch gap is structural

Linux distribution maintainers do serious work. But the reality is that Debian, for example, has about a thousand active maintainers for over thirty thousand source packages. When a CVE is disclosed, the maintainer responsible for the specific package needs to identify the fix, backport it to the version packaged by the distribution (not necessarily the current upstream version), test, publish, and wait for the build. For high-profile packages like openssl and curl, this is fast. For others, it's whenever someone finds the time.

There is another less obvious mechanism: distributions generally do not upgrade to the new version of the software. They apply the specific fix to the version they already package. This is more conservative and less likely to break compatibility, but it means the process includes a manual backport step that takes its own time and can be incomplete.

The concrete result: even with few packages in the image (a necessary, not sufficient condition), the speed at which fixes reach you depends on how many intermediate layers exist between upstream and your image. And every layer is a delay.

Quor's approach: building directly from source code

At Quor, we eliminate most of these intermediate layers by compiling runtimes and dependencies directly from the source code of upstream projects. This means that when a fix is merged into the master branch of Node.js, Python, or any runtime we support, we don't have to wait for Debian, Ubuntu, or Alpine to package that fix. We build from the commit that already contains the fix.

The process works like this:

The critical difference is that our pipeline operates directly on the upstream repository. When a security fix passes the official project's tests and is merged, it already satisfies two sets of validation: the upstream project's regression tests, and our own integration tests. We don't have to wait for the next release tag.

The gap between merge and release

This point deserves attention because it is frequently misunderstood. Mature open source projects have predictable release cycles. Python follows monthly releases for security fixes. Node.js has point releases for critical vulnerabilities, but also bundles multiple fixes into scheduled releases. Other projects have quarterly or even wider spacing cadences.

Consider what this means in practice: a fix for a moderate-severity vulnerability might be merged today and included in a release only two or three months from now. The vulnerability is known to the maintainers. The fix exists in the repository. But it is not yet in any public image based on officially released versions.

A recent example illustrates this well: a path traversal vulnerability in pip had its fix reviewed, approved, and merged into the project repository. But because pip follows a quarterly release cycle (January, April, July, October), organizations relying on stable versions were forced to wait up to ninety days for the fix in an official release.

By building from source, Quor can include this fix before it reaches any public release. The produced image already contains the fix. The code passed the upstream team's tests. It passed our tests. And it is available.

How we identify relevant commits

Monitoring upstream repositories is not trivial when done rigorously. Commits with security impact are not always explicitly labeled, and analyzing every commit individually would be unfeasible at scale.

Our approach combines a few strategies:

  • Commit message and pull request analysis: security-related keywords (fix, vulnerability, CVE, security, sanitize, bounds check) are initial signals, but insufficient on their own. They are used as attention filters, not as decision criteria.


  • Correlation with vulnerability databases: when a CVE is published in NVD, OSV, or GHSA, it frequently references specific commits or pull requests in the upstream repository. This allows us to identify the exact fix and verify if it has already been merged before a release.


  • Diff analysis: For commits identified as relevant candidates, we analyze the diff, which files were changed, what type of change was made, and which modules are affected. This allows us to assess the actual impact before deciding if a rebuild is necessary.


  • Our own regression tests: every build generated by Quor passes through a test suite before being published. This serves as a second layer of validation beyond the upstream tests, which is especially important when we build from commits that have not yet been officially released.

Why this matters for your pipeline

The most direct consequence of having images built from source with fixes incorporated earlier is the reduction of time your infrastructure remains exposed to known vulnerabilities. But there is a second, less obvious and equally important effect: reducing noise in your security pipelines. Scanners integrated into CI/CD that block builds with high or critical severity CVEs frequently report vulnerabilities that already have a fix available upstream, but have not yet reached the base image you consume.

The result is a cycle of manual triage: checking if the fix exists, when it will arrive, and whether it is truly relevant to your context, which consumes hours of engineering per week. When the image already incorporates the fix before the CVE is published in the databases (or immediately after), this noise decreases. The pipeline speaks less, and when it does speak, what it says is more relevant.

→ Check out the first part of this content: How we are building images with zero CVEs #1: reducing the attack surface (Alpine and distroless)

Operating Kubernetes in production for more than 13 years. With Quor, this experience extends to software supply chain security as well.