A critical vulnerability is published, the advisory spreads, and within an hour someone asks in the company chat: are we affected? Log4Shell made the question famous. CVE-2021-44228 affected Log4j Core from 2.0-beta9 up to 2.15.0, a library that sat several layers deep in the dependency tree of countless Java products, and most teams had to find out the hard way where it was.

The question sounds simple. It is actually three questions:

  1. Is the vulnerable component in anything we build or ship?
  2. If it is, can it be exploited in our product?
  3. Since when, and in which releases?

Most processes answer the first two. The third is the one customers, auditors and regulators ask next, and it is the hardest to answer after the fact.

Step 1: Find out whether the component is there

Companies usually get to an answer in one of four ways, roughly in order of maturity.

Ask around and search. Someone posts in Slack, engineers search their repositories and lockfiles for the package name, and a spreadsheet collects the replies. This is still the most common method. It is slow, it depends on who answers, and it misses anything that isn't declared in a file: dependencies of dependencies, vendored libraries, base images, tools installed during the build.

Check the scanner dashboards. Dependency scanners flag the CVE per repository within hours. That is faster, but a scanner reports on the code in front of it today, usually the default branch. It doesn't tell you which releases your customers are running, or what a build three months ago actually pulled in.

Query an SBOM inventory. Teams that keep a software bill of materials for every release can ask one question across all of them: which products and versions contain the affected versions of this package? This is the first method that answers for software you shipped, not just software in the repository.

Match continuously. When SBOMs are monitored against vulnerability data, the list of affected products exists before anyone asks. The chat thread starts with the answer instead of the question.

The quality of every method depends on what the inventory actually contains. The usual blind spots are:

  • Transitive dependencies. The vulnerable package is rarely one you added yourself. It comes in three or four levels down.
  • Anything the manifest doesn't declare. Packages installed by build scripts, npx tools, Dockerfile steps, restored caches and vendored C and C++ code.
  • Old releases. A branch that no longer builds can't be rescanned, but the product built from it may still be in the field.

Step 2: Decide whether it matters

A component can be present without being a real risk, so the next step is triage:

  • Version and configuration. Does the version fall in the affected range exactly? Is the vulnerable feature compiled in and enabled?
  • Reachability. Does your code call the vulnerable function at all?
  • Exposure. Is the component reachable from the network or from untrusted input, or only used at build time?
  • Mitigations. Is there sandboxing, a network restriction or a configuration that blocks the exploit?
  • Exploitation signals. CVSS gives the severity, EPSS estimates the probability that a CVE will be exploited in the next 30 days, and CISA's Known Exploited Vulnerabilities catalog lists the ones already exploited in the wild. Together they decide what gets looked at first.

Step 3: Record the decision

The outcome is recorded per product and version, increasingly as a VEX (Vulnerability Exploitability eXchange) statement. CISA's minimum requirements for VEX define four statuses: not affected, affected, fixed and under investigation. A "not affected" statement has to say why, for example because the vulnerable code is not present or cannot be reached by an attacker.

That record feeds everything that follows: the fix and the releases that carry it, the security advisory for customers, and any report a regulator expects. Under the EU Cyber Resilience Act, for example, a manufacturer must send an early warning about an actively exploited vulnerability in its product within 24 hours of becoming aware of it.

Step 4: Work out the exposure window

"Are we affected?" has a second half that often gets skipped: for how long were we affected?

The vulnerability existed long before the CVE was published. Log4Shell had been in Log4j since 2.0-beta9, released years before anyone disclosed it. Your exposure started the day a build first pulled in a vulnerable version, and it ended the day the last build containing it was replaced. You need that window to:

  • tell customers which of the releases they run are affected, not just the latest one
  • scope an incident investigation to the right period, if the vulnerability may have been exploited
  • show an auditor or a regulator when the problem entered the product and when the fix shipped

Scanning today's code can't give you that. The current lockfile says what the repository wants now, not what each past build installed. The only reliable source is a record of every build, taken while it ran and kept afterwards.

How CRACI answers it

CRACI runs your GitHub Actions jobs on its own runners and records every package each build actually pulls in, direct and transitive, including packages installed by scripts and restored from caches. Each build's SBOM is kept, so the history is already there when a CVE is published.

When a new CVE lands, the answer is already there: every past build and its SBOM, which of them contained the vulnerable dependency, direct or transitive, and the period you were affected. No spreadsheet, no rescanning, no reconstructing from lockfiles. The first build that contained it and the build that removed it give you the exposure window, as far back as your retention goes (180 days by default, custom retention on Enterprise).

Once you know, a policy gate can block builds that contain this CVE, so a later dependency update or a restored cache can't quietly bring the vulnerable version back into a release.

From there the rest of the process has something to stand on:

  • CRACI monitors the SBOMs continuously, so new CVEs are matched against your builds without anyone asking.
  • Security teams triage findings and send them to the teams that own the fix, and CRACI supports VEX for recording the outcome.
  • CRACI's inventory shows which software versions are deployed to which products.
  • For the CRA, CRACI submits the early warning, notification and final report on your behalf.

Book a demo to see the build history of one of your own repositories.