Send a holding statement within hours, a confirmed answer as soon as you can prove it, and a follow-up whenever anything changes. Say what you know, what you are still checking and when you will update. Base every "not affected" on what your builds actually installed during the attack window, not on today's lockfile.

This post is about what to say, when, and backed by what. For the investigation itself, how to find out whether a vulnerable component is in your products and since when, see are we affected by a CVE?.

Why customers ask you, not the package registry

The week after a widely reported attack, a small software supplier told us, clients were sending him text messages: is your stuff safe? Is my stuff safe? His customers handle sensitive data, his team has no security specialist, and the question needed an answer that day.

Customers are not asking whether a package was compromised. The registry and the researchers already said so. They are asking whether what you shipped them contains it, because your product is their exposure. They have their own customers, auditors or regulators asking them the same question. In regulated supply chains such as semiconductors, industrial equipment and health, the question is contractual, not curiosity.

They will also ask again if your answer cannot be checked. "We are not affected" with nothing behind it is a promise; "we checked every release build from 24 to 31 March and none installed the affected versions" is a statement they can rely on.

Four recent attacks and the question each one raised

The right answer depends on how the attack worked, so a template alone is not enough. For each of these, one fact decides what "affected" means for a supplier.

xz Utils backdoor (CVE-2024-3094), disclosed 29 March 2024. Andres Freund found a backdoor in xz versions 5.6.0 and 5.6.1. The release tarballs contained code that was not in the Git repository, and it injected the backdoor during the build. The NVD and Red Hat both score it 10.0 under CVSS 3.1. It mostly reached rolling and testing distributions: Debian testing, unstable and experimental, Fedora Rawhide and 41, openSUSE Tumbleweed and Kali, while stable releases were not affected. The question it raised: does any image or device we shipped include xz 5.6.0 or 5.6.1, and which base image did each build use?

Shai-Hulud npm worm, in waves since September 2025. Infected packages run a script at install time that steals npm, GitHub and cloud credentials and uses them to publish infected versions of more packages. The question it raised: did any CI job install an infected version, and were our publishing tokens exposed, which would make us a spreader? Our Shai-Hulud entry lists every wave and how to check.

LiteLLM on PyPI, 24 March 2026. Versions 1.82.7 and 1.82.8 were uploaded directly to PyPI with credential-stealing code that was never in the project's GitHub repository. In 1.82.8, a .pth file ran the payload whenever any Python interpreter started, not only when LiteLLM was imported; it collected environment variables, SSH keys, cloud credentials and Kubernetes secrets. HiddenLayer links the attack to TeamPCP, which also hit Trivy and Checkmarx tooling that month. LiteLLM's own advisory gives two explanations for the initial access: it traces the compromise to the Trivy dependency in its CI security scanning workflow, and says a maintainer's PyPI account may have been compromised. Even the exposure window differs inside that advisory, about 40 minutes in one place and 10:39 to 16:00 UTC in another. The question it raised: did a build or a shipped container include those versions, and what secrets were present in that environment?

axios on npm, 31 March 2026. An attacker took over a maintainer's npm account and published axios 1.14.1 and 0.30.4, which added a dependency, [email protected], whose install script dropped a remote access trojan for macOS, Windows and Linux. Datadog puts the window at 00:21 to 03:25 UTC; Snyk has the versions removed at 03:29 UTC. The question it raised: did any build resolve those versions in that window? A lockfile committed before the window answers "no" for builds that used it, and says nothing about builds that ran npm install without it.

AttackEcosystemAffected versionsWindowWhat "affected" means for a supplier
xz UtilsLinux distributions5.6.0, 5.6.1Weeks, until disclosure on 29 March 2024You shipped it, in an image or device built on an affected distribution
Shai-HuludnpmHundreds of packages, per waveHours to days per waveYou built with it, and your tokens may have spread it; you may also have shipped it
LiteLLMPyPI1.82.7, 1.82.824 March 2026, from 10:39 UTC (sources differ on the end)You built with it or shipped it, and secrets in that environment may be stolen
axiosnpm1.14.1, 0.30.431 March 2026, about 00:21 to 03:25 UTCYou built with it during the window; the trojan ran on the build machine

The same questions come up for a critical vulnerability that is not a supply chain attack. When PHP2Shell was exploited in October 2026, customers of every product that ships PHP asked which of their versions carry an affected PHP. The steps below apply unchanged.

The answer has three stages

StageWhenWhat you send
Holding statementWithin hours of the newsWhat you know, what you are checking, when you will update
Confirmed answerAs soon as you can prove it, usually day one"Not affected" for named versions, or "affected" with what to do
Follow-upWhen the fix ships, when you learn something new, when you close the caseThe same notice, revised

The first hour: the holding statement

A holding statement is the first public statement you make before the facts are in. Say something true, quickly. Silence gets filled with assumptions, and a customer who hears nothing from you assumes you have not noticed.

  • Who and where. One named sender, in the channel the customer used, plus your security page. One shared text beats ad-hoc replies: it stays consistent, and legal reviews it once.
  • What it contains. That you know about the attack, that you are checking your products and build systems, what you know so far, when the next update comes, and who to contact.
  • What it must not contain. "We are not affected" before you have checked, speculation about the attacker, or blame.

Template 1: holding statement

Subject: [Attack name]: our initial statement

We are aware of the reported compromise of [package or project] ([affected versions], published [date]). Our security team is checking every [product] release and our build systems for the affected versions. So far we have [found no affected versions in the products checked / not yet completed the check]. We will send an update by [date and time, time zone]. If you have questions in the meantime, contact [name, security contact address]. We will not speculate about impact before the check is complete.

For a text message or chat, two sentences are enough:

We know about the [attack name] compromise and are checking every [product] release and our builds now. You will have a confirmed answer from us by [time].

Have counsel approve all three templates before you need them.

Day one: confirm, with evidence

Two outcomes are possible, and each needs its own evidence. For a "not affected" answer, collect:

  • the affected package and versions, from the advisory or the researchers' write-up
  • your products and the versions customers actually run
  • for each of those versions, the build that produced it and the record of what that build installed, not the current lockfile
  • every build that ran during the attack window, including CI jobs that never produced a release
  • for worms that steal tokens, whether any CI secrets were present in jobs that could have run the malicious code

Are we affected by a CVE? explains how to establish each of these.

Every statement you make has to be backed by something you can show:

StatementWhat you must be able to showWhat is not enough
"Product X version Y does not contain package Z"The SBOM recorded from the release build of version YA scan of the current repository
"None of our builds installed the compromised versions"Install records for every build in the windowA lockfile that some jobs bypass
"Our release pipeline was not compromised"Network and secret-use records for the windowThe absence of alerts
"Customers on version Y are safe after upgrading"The record of the fixed build, and the credentials you rotatedA changelog entry

Template 2: not affected

Subject: [Attack name]: [product] is not affected

We have completed our check for the [package] compromise ([affected versions]). The following versions of [product] are not affected: [list of products and versions checked]. We checked every release build and CI build that ran between [start date] and [end date]. This statement is based on the record of components installed by each release build, not on a scan of our current source code. None of these builds installed the affected versions. If you need the evidence, we can send the SBOM for the versions you run, or a VEX statement. If we learn anything that changes this assessment, we will tell you. Contact: [name, security contact address].

Say "not affected" for named versions. Never write "100% safe". If the customer then asks for the full SBOM, see answering customer SBOM requests.

Template 3: affected, with mitigation

Subject: [Attack name]: action required for [product] versions [x to y]

Our check for the [package] compromise found that [product] versions [x to y] are affected. The first affected build was produced on [date]; version [z], released [date], no longer contains [package version].

What this means: [plain-language risk, for example "a component installed in these versions can send credentials from the system it runs on to an outside server"].

What to do now: upgrade to [product] [z]. If you cannot upgrade right away, [mitigation, for example block outbound connections to the listed hosts]. If you used [product] versions [x to y] in [environment], rotate [credentials or keys] that were available to it.

What we have done: we removed the affected versions from our builds, rebuilt [product] from clean dependencies, rotated the credentials our build systems use and [other actions]. [We have notified the relevant authorities where required.]

We will send the next update by [date], or sooner if anything changes. Contact: [name, security contact address].

An "affected" notice can also be a legal duty; see the reporting section below.

Where VEX and CSAF fit. A "not affected" or "fixed" statement can be published as VEX, so customers' scanners stop flagging the package. Larger customers may expect a machine-readable advisory as well; CSAF is the format most of them ask for.

Follow-up: close it properly

Send a follow-up when the fix ships, when your investigation finds something new, and a final note when the case closes. Keep one advisory per incident and revise it rather than sending a new one each time (update, do not replace). Record what you told whom and when: the next audit or customer security questionnaire will ask.

What to say when you don't know yet

"We don't know yet" builds more trust than a "not affected" you have to retract. Phrases that are honest without alarming anyone:

  • "We have not found [package] in [products checked so far]. We are still checking [remaining products or build systems] and will update you by [time]."
  • "The versions you run were built before the attack window. We are confirming that no later build replaced them."
  • "We are checking whether any of our build systems ran the affected versions. Your installed product is a separate question, and we will answer both."

Phrases to avoid:

  • "No impact whatsoever"
  • "Fully secure"
  • "Unaffected by any supply chain attack"
  • "Our scanner found nothing"

When the attack becomes a reporting duty

If you sell products with digital elements in the EU, the Cyber Resilience Act has applied its reporting obligations since 11 September 2026, the day ENISA launched the Single Reporting Platform. An actively exploited vulnerability in your product, or a severe incident having an impact on its security, triggers an early warning within 24 hours of becoming aware of it, a notification within 72 hours and a final report, submitted through the platform to your coordinating CSIRT and ENISA. The CRA guide walks through the steps.

Whether a compromised dependency you shipped, or a compromise of your own build pipeline, meets those definitions is a judgement for you and your counsel, not for this post. The regulation defines the terms:

  • An "actively exploited vulnerability" is "a vulnerability for which there is reliable evidence that a malicious actor has exploited it in a system without permission of the system owner" (Article 3, point 42).
  • An incident is severe where "it negatively affects or is capable of negatively affecting the ability of a product with digital elements to protect the availability, authenticity, integrity or confidentiality of sensitive or important data or functions", or where "it has led or is capable of leading to the introduction or execution of malicious code" in the product or in a user's systems (Article 14(5)).

Article 14(8) adds a duty towards users: inform the impacted users, and where appropriate all users, of the vulnerability or incident and of any mitigation they can apply, "where appropriate in a structured, machine-readable format that is easily automatically processable". Template 3 may be part of meeting that duty.

Other regimes apply too. Under NIS2, essential and important entities notify significant incidents to their CSIRT or competent authority, and many customer contracts carry their own notification clauses with their own deadlines.

Be ready before the next one

  • A record of what each release build installed, kept for the support period of the product (build-time SBOMs).
  • A list of which customers run which versions.
  • The three templates above, approved by legal.
  • A named sender and a channel for each kind of customer.
  • A security page and a security.txt file where updates go (coordinated vulnerability disclosure).
  • Egress control on CI, so a compromised package cannot send secrets to a host the build does not need (GitHub Actions egress policy).

For incidents that arrive by the hundred rather than one at a time, see scaling a PSIRT.

How CRACI fits

The evidence table above keeps asking for one thing: a record of what each release build installed. That is what CRACI keeps. CRACI runs your GitHub Actions builds on its own runners and records every package each job actually pulled in, including packages fetched by install scripts and packages restored from caches. Every past build's SBOM is kept, so "did any build install axios 1.14.1 during the window?" is answered from build history, with the period you were affected, as far back as your retention goes.

Known malicious releases that have a published CVE, such as the xz backdoor, are flagged like any other vulnerability, and a policy gate can block builds that contain them. CRACI does not detect a new malicious package by its behavior or reputation.

CRACI's egress policy blocks connections to hosts a build does not need, which limits what a malicious install script can send out, even in an attack nobody has seen before. When a customer asks for the evidence, you can export the SBOM for the versions they run as CycloneDX or SPDX and send it. CRACI supports VEX.

Answer "were we affected?" from your build history, or read more about how supply chain attacks work.

When CRACI is not the answer

  • You need help with the communication itself. PR and legal advice are a different job.
  • The affected software was not built on CRACI. CRACI records GitHub Actions builds that run on its runners; other CI systems are on the roadmap.
  • The question is about a vendor's product you did not build. Vendor SBOMs can be added to CRACI, but the answer is only as good as the vendor's SBOM.
  • You need to catch a brand-new malicious package before any advisory exists. CRACI flags known malicious releases once an advisory with a CVE is published; it does not detect malware by behavior or reputation (responding to zero-day vulnerabilities).

Frequently asked questions

What do I tell customers when a supply chain attack is in the news?

Send a holding statement within hours: you know about the attack, you are checking your products and builds, what you know so far, and when you will update. Follow it with a confirmed answer for named product versions as soon as you can back it with build records, and a follow-up when anything changes.

What should a holding statement say?

What happened in one sentence, that you are checking your products and build systems, what you have found so far, the time of the next update and a contact. It must not say "not affected" before you have checked, speculate about the attacker or blame anyone.

How do I say "we are not affected" without overpromising?

Name the products and versions you checked, the date range of builds you checked and the evidence you used, such as the record of components each release build installed. Say "not affected" for those versions, offer the SBOM or a VEX statement, and promise to update if anything changes. Avoid "100% safe" or "fully secure".

What should a "we were affected" notice to customers contain?

Which products and versions are affected, since when and until which fixed version, the risk in plain language, what the customer should do now (upgrade, rotate credentials, block hosts), what you have already done, when the next update comes and who to contact.

What evidence should back a "not affected" statement?

For each version customers run, the record of what the build that produced it installed, not today's lockfile. Add the records of every build that ran during the attack window and, for credential-stealing attacks, whether secrets were present in jobs that could have run the malicious code.

Do I have to report a supply chain attack under the Cyber Resilience Act?

Possibly. Since 11 September 2026, manufacturers of products with digital elements sold in the EU must report actively exploited vulnerabilities and severe incidents having an impact on product security: an early warning within 24 hours, a notification within 72 hours and a final report. Whether a given attack qualifies is a judgement for you and your counsel.

How soon should we reply to customers?

Within hours with a holding statement, even if all it says is that you are checking and when you will update. The confirmed answer follows as soon as you can prove it. A fast honest "we are checking" does more for trust than a late "not affected".