chainguard-dev/malcontent
malcontent is Chainguard's open-source malware/supply-chain discovery tool that analyzes binaries (ELF, Mach-O, PE, a.out) and scripting-language source across Linux, macOS and Windows using 14,500+ YARA rules (including third-party rules from Avast, Elastic, FireEye, Mandiant, Nextron and ReversingLabs). Its standout feature is differential analysis ("mal diff"), which compares two versions of a build artifact or OCI image against a known-good baseline to surface newly introduced risky behaviors — the technique the project demonstrates on the 3CX supply-chain compromise by flagging unexpected high-risk changes to libffmpeg. It also supports threshold-based scanning ("mal scan") and unfiltered capability enumeration ("mal analyze"), and is designed to run inside CI/CD pipelines, including air-gapped environments via embedded rules.
What it is
Operational details for malcontent, Chainguard's YARA-based malware/supply-chain scanner, covering its OCI registry authentication model, a UPX trust-boundary control, build/install steps, and a known blind spot: other scanners flag malcontent itself as malicious.
How it works
OCI image pulls default to anonymous. Authentication is opt-in via --oci-auth, which reads credentials from MALCONTENT_REGISTRY_USER/MALCONTENT_REGISTRY_PASS rather than the ambient Docker keychain (~/.docker/config.json), which malcontent never touches. Credentials are scoped to exactly one registry host: they are sent only when the target image's registry matches MALCONTENT_REGISTRY_HOST, falling back to anonymous pulls for any other registry.
flowchart TD
A[OCI pull requested] --> B{Registry host matches MALCONTENT_REGISTRY_HOST?}
B -->|yes| C[Send Basic auth from env vars]
B -->|no| D[Fall back to anonymous pull]Separately, malcontent's UPX integration trusts a binary at /usr/bin/upx (or other standard install directories) by default. Pointing it elsewhere via MALCONTENT_UPX_PATH is treated as an explicit trust override that bypasses the directory allowlist — but the tool still enforces that the path is absolute, resolves to a regular executable file, and is not group- or world-writable.
Preconditions / Affected
- Host normalization matters: Docker Hub's host is
index.docker.io, notdocker.io— setMALCONTENT_REGISTRY_HOST=index.docker.ioor authentication won't trigger. - The UPX override is only relevant if you need a non-standard UPX binary location; otherwise the default allowlist applies silently.
What to do
Build from source requires Go, Rust, YARA-X, pkgconf, and libssl-dev:
sudo apt-get install -y pkgconf libssl-dev
make install-yara-x
make out/mal
sudo install out/mal /usr/local/binOr pull the prebuilt image:
docker pull cgr.dev/chainguard/malcontent:latestCaveats
The page states that other YARA-based scanners (e.g., Elastic Agent, per issue #78) have historically flagged malcontent itself as malicious, since YARA tools often contain strings matching the malicious patterns they search for. This is a documented false-positive source when running malcontent alongside other security tooling, not a defect in malcontent's detections.
Technologies named here
Techniques named here
- Differential analysis
- Capability analysis
- Threshold-based scanning
- Supply-chain compromise detection
Threat actors named here
- 3CX Compromise
Related in Supply Chain


