After Trivy, I thought the message was clear: a compromised security tool is not a vulnerability like the others.
With Checkmarx/KICS, the message becomes even more brutal.
We are no longer talking only about a tool that contains a flaw. We are talking about a security tool that teams deliberately run in their CI/CD pipelines, on their repositories, on their Terraform and Kubernetes files, sometimes with sensitive environment variables within reach.
In other words, a compromised security scanner can become an excellent observation point for an attacker. And that is precisely what this new wave around Checkmarx/KICS comes to remind us.
What happened?
On 22 April 2026, Checkmarx published a security update about a supply chain incident under investigation.
The potentially affected artifacts notably include:
- the public Docker image
checkmarx/kics; - the GitHub Action
checkmarx/ast-github-action; - the Checkmarx AST Results VS Code extension;
- the Checkmarx Developer Assist extension.
Checkmarx states that the malicious artifacts would not have replaced known-safe versions published before the identified exposure windows. The risk therefore depends heavily on when the artifacts were downloaded, executed or cached.
But that is also what makes the incident hard to handle operationally. In many CI/CD environments, we do not always know precisely which tag was pulled, at what time, on which runner, with which actual digest, from which cache, with which secrets reachable and with which outbound connectivity.
That is where the subject goes far beyond Checkmarx.
Which artifacts are affected?
According to the Checkmarx bulletin of 22 April, the public Docker image checkmarx/kics was exposed over a very short window, between 22 April 2026 12:31:35 UTC and 12:59:46 UTC.
The listed malicious tags notably include:
latestalpinedebianv2.1.20v2.1.21v2.1.20-debianv2.1.21-debianThe GitHub Action checkmarx/ast-github-action is also mentioned, with tag 2.3.35, over a window running from 22 April 2026 14:17:59 UTC to 15:41:31 UTC.
Checkmarx also lists two families of extensions:
- Checkmarx VS Code extension: versions
2.63and2.66; - Checkmarx Developer Assist extension: versions
1.17and1.19.
For the extensions, the exact windows remain to be confirmed as I write this.
Why is KICS an interesting target?
KICS is not a trivial tool. It is an IaC scanner. It analyzes Terraform, Kubernetes, CloudFormation files and other infrastructure configurations.
It can therefore see things many teams consider "not really secrets" but which are very useful to an attacker: names of cloud resources, account names, regions, internal paths, naming conventions, network topology, buckets, clusters, endpoints, variables, misplaced secrets, forgotten tokens, exposed keys in test files.
The problem is not only that a scanner can detect mistakes. The problem is that a compromised scanner can read exactly the same data as the legitimate scanner, then exfiltrate it.
It is a complete inversion of the security promise.
How does the pattern resemble Trivy?
In my analysis of the Trivy case, one point already seemed central to me: the attacker had not merely compromised a repository, he had touched a trust link used automatically by teams.
With Trivy, the story started with a poorly controlled GitHub Actions workflow, a privileged token, then a compromise that hit the releases, the tags and several distribution channels.
With KICS/Checkmarx, Docker states that a malicious actor authenticated on Docker Hub with valid Checkmarx publisher credentials, then pushed malicious images to the checkmarx/kics repository.
This point is essential. The attack does not consist of publishing a fake tool with an almost identical name. It consists of going through an official channel, with valid credentials, on a repository teams may already use in production.
That is what makes these incidents so dangerous.
Why did the binary keep working?
Another important detail: according to Docker, the poisoned KICS binary kept the legitimate scanning surface.
This is classic behaviour in serious supply chain attacks. The tool keeps doing what the user expects from it. It scans. It produces output. It appears to work. But in parallel it adds a discreet exfiltration path.
In the case documented by Docker, the scan data was collected, encrypted then sent to attacker-controlled infrastructure, notably via audit.checkmarx[.]cx, with the User-Agent KICS-Telemetry/2.0.
This is exactly why you must not settle for a check of the kind "the CI job passed, so everything is fine". A compromised tool can work perfectly while doing something else on the side.
How did the compromise spread downstream? The Bitwarden CLI case
Two days after the Checkmarx bulletin, a new chapter opens: Bitwarden CLI, the command-line client of the Bitwarden password manager, was compromised through the same vector.
What does Bitwarden confirm?
In its statement, Bitwarden indicates that a malicious npm package was distributed over a short window:
- package:
@bitwarden/cli@2026.4.0(the clean version at the time was2026.3.0); - official window: between 22 April 2026 17:57 ET and 19:30 ET;
- SafeDep specifies that the malicious npm release was published at 21:22:59 UTC on 22 April;
- two malicious files: a loader
bw_setup.jsand a payloadbw1.js, triggered by apreinstallhook in thepackage.json("preinstall": "node bw_setup.js"); - root cause: Bitwarden's GitHub repository used
checkmarx/ast-github-action, compromised during the Checkmarx incident.
Bitwarden states that no user vault data was accessed, that the npm release was deprecated, that the compromised access was revoked, and that a CVE is being issued for this version of the CLI.
Why does this chapter change the nature of the incident?
It is no longer only Checkmarx that is compromised. It is a consumer of the Checkmarx GitHub Action that in turn becomes a distributor of malicious code, in a package widely adopted on developer machines.
In other words, the compromise propagates downstream, through the repositories that used the checkmarx/ast-github-action action in their release pipeline. StepSecurity sums up the risk well:
A single developer with @bitwarden/cli@2026.4.0 installed can become the entry point for a broader supply chain compromise.
What does the bw1.js payload do?
According to JFrog, Socket, StepSecurity, Endor Labs, GitGuardian, SafeDep and OX Security, the malicious code chains several capabilities:
- secret theft: GitHub tokens (pattern
ghp_) and npm tokens (patternnpm_),.ssh,.env, shell history, GitHub Actions secrets, cloud credentials; - targeting of AI assistants: local configurations of Claude Code (
~/.claude.json), Cursor, Kiro (~/.kiro/settings/mcp.json), Codex CLI, Aider, Gemini CLI and OpenCode; - encrypted exfiltration: data compressed with gzip, encrypted with AES-256-GCM then wrapped with RSA OAEP, sent to
audit.checkmarx[.]cx(the same domain as KICS, IP94.154.172.43); - Cloudflare fallback channel: GitGuardian identified a secondary tunnel via
safely-irc-weblogs-few[.]trycloudflare[.]com; - GitHub-based fallback: creation of public repositories under the victim's account (observed pattern
helloworm00/hello-world,helloworm00/my-first-repo), with recovery of PATs staged in public commits markedLongLiveTheResistanceAgainstMachinesand discovery of exfiltration domains via commits signedbeautifulcastle; - self-propagating npm worm: with a stolen npm token, the malware automatically republishes malicious versions of the other packages the victim can publish;
- GitHub Actions workflow injection: with a stolen GitHub token, the malware adds a malicious workflow that captures the secrets reachable in the affected repositories.
OX Security notes a Shai-Hulud: The Third Coming string in the package, suggesting a lineage with the already known Shai-Hulud campaign. Socket also notes that the malware stops if it detects a Russian locale, an interesting operational signature for threat hunting.
Researcher Adnan Khan highlights another unprecedented point: this would be the first documented compromise of a package published via NPM trusted publishing. SafeDep tempers this by recalling that "trusted publishing reduces one class of token theft, but does not save a release pipeline that a poisoned workflow can still drive".
Bitwarden-specific IOCs
npm package : @bitwarden/cli@2026.4.0Loader : bw_setup.jsPayload : bw1.jsHook : preinstall (node bw_setup.js)
SHA-256 loader : 18f784b3bc9a0bcdcb1a8d7f51bc5f54323fc40cbd874119354ab609bef6e4cbSHA-256 payload : 8605e365edf11160aad517c7d79a3b26b62290e5072ef97b102a01ddbb343f14
Domains / C2 : audit.checkmarx[.]cx safely-irc-weblogs-few[.]trycloudflare[.]comIP : 94.154.172.43
Signatures : Shai-Hulud: The Third Coming LongLiveTheResistanceAgainstMachines beautifulcastleFallback accounts : helloworm00/hello-world, helloworm00/my-first-repoThe network IOCs shared with KICS (audit.checkmarx[.]cx, User-Agent KICS-Telemetry/2.0) remain relevant.
What if a machine installed version 2026.4.0 during the window?
Incident response must go further than a simple uninstall:
- consider all secrets on the machine as compromised: GitHub tokens, npm tokens, SSH keys, cloud credentials,
.envvariables, shell history; - inspect the recently created public repositories under the developer's GitHub account to spot a repository whose name follows the pattern word-word-three digits;
- audit the recently added GitHub Actions workflows in every repository reachable with the developer's token;
- audit the recently published npm packages if an npm token with publishing rights was present on the machine, because of the worm behaviour;
- check the local configurations of AI assistants: Claude, Cursor, Aider, Codex CLI, Kiro.
Which indicators should you look for?
The publicly shared indicators at least allow a first verification pass.
Domains and IPs to block or search for in your logs:
checkmarx.cxaudit.checkmarx.cx91.195.240.12394.154.172.43User-Agent to search for:
KICS-Telemetry/2.0Examples of simple searches in proxy or egress logs:
grep -R "audit.checkmarx" /var/log 2>/dev/nullgrep -R "checkmarx.cx" /var/log 2>/dev/nullgrep -R "KICS-Telemetry/2.0" /var/log 2>/dev/nullIn a centralized environment, the idea is to look for outbound connections to the mentioned domains, DNS resolutions, suspicious HTTP(S) connections from CI runners, traces of KICS executions during the exposure window, the Docker digests actually pulled and the local or intermediate caches that may have kept the image.
Why are Docker tags a trap?
This incident recalls a simple rule: a Docker tag is not an integrity guarantee.
latest is not a version. v2.1.20 is not cryptographic proof. A tag can be moved, rewritten or made to repoint temporarily to a malicious digest. The only identifier usable for a strict policy is the digest.
Bad example:
image: checkmarx/kics:v2.1.20Better:
image: checkmarx/kics@sha256:<expected-digest>Same logic on the GitHub Actions side.
Bad example:
uses: checkmarx/ast-github-action@2.3.35Better:
uses: checkmarx/ast-github-action@<commit-sha>This does not remove every risk, but it at least prevents a mutable tag from silently changing what gets executed.
What is the real question? What the scanner could see
The operational question is not only "did I use Checkmarx or KICS?". The real question is "what could this scanner see at the moment it ran?".
In a CI/CD pipeline, that can include the full content of the repository, the environment variables, CI/CD tokens, a GitHub or GitLab token, temporary cloud credentials, configuration files, SSH keys, .npmrc, .pypirc, .docker/config.json files, Kubernetes manifests, Terraform plans and scan outputs containing paths, resources or secrets.
That is why rotation must be driven by the actual exposure, not only by the name of the tool.
If the scanner ran in a job with no secret, no cloud credentials, no outbound network and on a public repository with no sensitive data, the risk level is not the same. If it ran on a shared runner, with access to CI/CD variables, cloud credentials and the Internet, the situation is completely different.
What would I check immediately?
Here is the minimal checklist I would apply after this kind of incident.
1. Identify the usages
Search for references to Checkmarx, KICS and the associated actions:
grep -R "checkmarx/kics\|checkmarx/ast-github-action\|kics" .github .gitlab-ci.yml . 2>/dev/nullIn GitLab CI, also look at the includes, internal templates and shared components. In GitHub Actions, check the workflows, composite actions and reusable workflows.
2. Identify the digests actually pulled
On the runners or build machines still reachable:
docker image ls --digests | grep -i checkmarxIf a registry mirror, a proxy or an internal cache is used, that level must also be checked. A tag restored on Docker Hub does not automatically purge your caches.
3. Purge the caches
Depending on the architecture, you must go through the local Docker caches, the GitLab Runner caches, the GitHub self-hosted runner caches, the registry mirrors, the pull-through caches, the CI artifacts, the dependency caches and the images replicated into an internal registry.
A fresh docker pull is not enough if a malicious digest is already present locally.
4. Search for the network IOCs
Search for the shared domains, IPs and User-Agent. At a minimum:
checkmarx.cxaudit.checkmarx.cx91.195.240.12394.154.172.43KICS-Telemetry/2.05. Rotate the exposed secrets
The rotation must not be symbolic. It must start from the following question: which secrets were reachable by the job or the machine that executed the compromised artifact?
To consider: GitHub or GitLab tokens, SSH keys, AWS, Azure, Google Cloud credentials, npm tokens, Docker Hub tokens, Kubernetes secrets, CI/CD variables, Terraform credentials, secrets present in IaC files, AI agent tokens or MCP configurations if present on the developer machine.
6. Review the IDE extensions
This is a point still too rarely handled in DevSecOps policies.
A VS Code extension is not a mere developer comfort. It runs in a rich environment: access to the workspace, potential access to configuration files, access to local variables, access to stored tokens, access to keys and credentials depending on the machine's configuration, ability to download additional code.
Extensions must be treated as software dependencies, not as invisible personal preferences.
What does this incident say about our pipelines?
The real problem is not Checkmarx. The real problem is our trust model.
We have built pipelines that automatically download and execute Docker images, GitHub Actions, install scripts, extensions, packages, scanners, helpers, wrappers and agents. Then we added secrets into those same environments because we did need to deploy, scan, publish, sign, push, notify, create releases and reach the cloud.
And now we discover that attackers target precisely these trust paths. They no longer need to compromise the application directly. They compromise the tool that watches the application. They compromise the tool that scans the infrastructure. They compromise the tool we placed in the pipeline in the name of security.
My recommendations after Trivy and Checkmarx
I do not believe the right answer is to ban open source or stop using scanners. That would be unrealistic and probably counterproductive. However, the posture must change.
1. Confine the scanners
A scanner must not have default access to all the secrets of the pipeline. Jobs must be separated: scan without secrets, build without production credentials, deployment with limited credentials, publication with minimal permissions.
An IaC scanner does not need an administrator cloud token to read Terraform files.
GitLab CI example: a scanner without secrets
Confinement starts with a simple rule: the scan job must not inherit the secrets needed for deployment.
Bad model:
variables: AWS_ACCESS_KEY_ID: $AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY: $AWS_SECRET_ACCESS_KEY
kics_scan: image: checkmarx/kics:v2.1.20 script: - kics scan -p .In this example, the scanner uses a mutable tag and runs in a context where cloud credentials may be available.
Better model:
kics_scan: image: checkmarx/kics@sha256:<valid-and-verified-digest> stage: test script: - kics scan -p . rules: - if: '$CI_PIPELINE_SOURCE == "merge_request_event"'The cloud credentials, publication tokens and deployment secrets must stay in separate jobs, protected, and only available when they are actually needed.
2. Reduce egress
A CI runner that can freely reach the Internet is an excellent exfiltration channel. Network filtering must not be decorative.
You must be able to say which domains are allowed, which outbound flows are normal, which jobs are allowed to reach the Internet and which jobs must be isolated.
3. Pin the executable dependencies
For images, aim for image@sha256:<digest>. For GitHub Actions, aim for owner/repo@<commit-sha>. For packages, rely on lockfiles, integrity verification, an internal registry, a promotion policy and a delay before automatic adoption.
4. Watch behaviours
A project's reputation is no longer enough. You must watch for unusual outbound connections, unexpected User-Agents, new domains, provenance changes, tags without an upstream release, images built from an unexpected source and abrupt digest variations.
Docker explains precisely that detection came from a correlation of signals: new tag, unusual provenance, no upstream release and abnormal publishing behaviour.
5. Do not trust the caches
Caches speed up builds. They also speed up the persistence of a compromised artifact.
After an incident of this kind, you must check and purge the runners, the internal registries, the mirrors, the dependency caches, the developer machines and the test environments.
6. Document CI/CD incident response
The question is not whether another tool will be compromised. The question is whether your team will know how to respond quickly.
You must therefore prepare a procedure for inventorying workflows, searching for artifacts, rotating secrets, purging caches, analyzing egress, temporarily disabling the compromised tools, along with a list of internal contacts and trusted sources to follow.
What do I change in my reading of DevSecOps tools?
After Trivy, I had already raised my level of demand. After Checkmarx/KICS, I go even further: a DevSecOps tool must be treated as a critical production dependency.
Not because the vendor is bad. Not because open source would be dangerous by nature. But because these tools sit in extremely sensitive places: at the heart of the pipelines, in contact with the code, in contact with the secrets, in contact with the infrastructure and in contact with the developer machines.
The scanner is not external to the system. It is part of the system. And when it is compromised, it inherits the trust we gave it.
Key takeaways
- The Checkmarx/KICS incident simultaneously hits the official Docker image, the GitHub Action and two IDE extensions: it is a hybrid surface, not a simple "compromised package".
- The poisoned KICS binary kept the legitimate scanning function while exfiltrating to
audit.checkmarx[.]cxwith the User-AgentKICS-Telemetry/2.0. - The compromise went through valid publisher credentials on Docker Hub, not typosquatting: the official channel itself was used.
- A Docker tag or GitHub Action tag is not an integrity guarantee. Only the digest or the commit SHA is.
- Rotation must follow the actual exposure of the job: which secrets were visible to the scanner at the moment it ran?
- IDE extensions are software dependencies, not personal preferences. They must enter the dependency policy.
- After Trivy then Checkmarx/KICS, scanners must be confined: fewer secrets, less egress, strict pinning, behavioural detection, no blind trust in caches.
- The compromise propagated downstream through
checkmarx/ast-github-actionall the way to Bitwarden CLI (@bitwarden/cli@2026.4.0, loaderbw_setup.js,preinstallhook): a consumer of the action was turned into a malware distributor notably targeting the configurations of AI assistants (Claude Code, Cursor, Kiro, Aider, Codex CLI, Gemini CLI, OpenCode).
Conclusion
Trivy had already sent us a very strong signal. Checkmarx/KICS confirms that it was not an anomaly. And the Bitwarden CLI extension of the incident demonstrates that the compromise of a scanner very quickly becomes the compromise of every project that consumes that scanner.
Attackers now target the tools we use to secure our delivery chains, then the products those tools help publish. It is logical: these tools are installed everywhere, executed automatically and rarely confined properly.
DevSecOps can therefore no longer boil down to adding a scanner to a pipeline. You must also secure the scanner: its execution mode, its permissions, its network, its updates, its caches and the secrets it can see. Otherwise, all we are doing is adding a new entry point in the name of security.
Sources
- Checkmarx, Security Update: April 22
- Docker, Trivy, KICS, and the shape of supply chain attacks so far in 2026
- Socket, Malicious Checkmarx Artifacts Found in Official KICS Docker Images, VS Code Extensions
- BleepingComputer, New Checkmarx supply-chain breach affects KICS analysis tool
- GitGuardian, No Off Season: Three Supply Chain Campaigns Hit npm, PyPI, and Docker Hub in 48 Hours
- The Hacker News, Bitwarden CLI Compromised in Ongoing Checkmarx Supply Chain Campaign
- GitGuardian, Bitwarden CLI: GitGuardian views on helloworm00
- SafeDep, Bitwarden CLI Supply Chain Compromise
- My previous post, KICS and LiteLLM compromised
- My previous post, Trivy compromised a second time