When a container image gets scanned for its software bill of materials, most engineers assume something like a virus scan is happening — a tool crawling files looking for known signatures. That's not what Syft does. Anchore's open-source SBOM generator, first released in 2020 and hitting a stable v1.0.0 in January 2024, works more like a filesystem archaeologist: it unpacks layers, reads package manager metadata, fingerprints binaries that have no metadata at all, and stitches the results into a dependency graph. Understanding that mechanism matters, because Syft has become the de facto default for SBOM generation — it ships inside Anchore Enterprise, gets invoked by Docker's docker sbom command, and runs inside countless CI pipelines. If you're going to trust its output for a compliance audit or a vulnerability match, it helps to know exactly what it is — and isn't — actually inspecting.
What Actually Happens When You Run syft scan?
Syft's first move is to resolve its scan target into a filesystem it can walk — not a network call, not a signature database lookup. If the target is a container image reference like alpine:3.19, Syft uses the go-containerregistry library to pull the manifest and each compressed layer tarball, then extracts them into a temporary directory, applying OverlayFS-style whiteout rules so files deleted in a later layer don't appear as "present." If the target is a local directory, an SBOM file, or a running container, Syft skips the pull step and walks the filesystem directly. Either way, the result is the same starting point: a synthetic, squashed filesystem tree representing exactly what a container would see at runtime. Everything downstream — cataloging, classification, relationship-building — operates on that tree, not on the original layers or registry metadata.
How Does Syft Decide Which Files to Parse?
Syft doesn't parse every file — it dispatches specific file paths to specific "catalogers," and as of the v1.x line there are more than 40 of them, each scoped to a package ecosystem. A cataloger for npm looks for package-lock.json and package.json; one for Python looks for requirements.txt, poetry.lock, Pipfile.lock, and installed *.dist-info/METADATA files; separate catalogers handle Debian's /var/lib/dpkg/status, RPM's rpmdb.sqlite or Berkeley DB files, Alpine's /lib/apk/db/installed, Go's embedded buildinfo sections, Java's .jar/.war/.ear archives (which Syft actually opens and reads pom.xml or MANIFEST.MF from inside), and Rust's Cargo.lock. Each cataloger runs independently against the same file tree, which is why a single Node.js container commonly triggers five or six catalogers at once — OS packages, npm packages, and sometimes a stray Python or Go binary layered in by a base image.
How Does Syft Handle Multi-Stage and Multi-Layer Images?
Syft evaluates the fully squashed image, not each layer as a separate artifact, which is precisely why multi-stage Dockerfiles can hide packages from it. A common real-world case: a build stage installs build-essential and a dozen compilers to compile a Go binary, then a final FROM scratch or FROM distroless stage copies only the compiled binary across. Because Syft scans the final image's squashed tree, it correctly reports zero trace of build-essential — that layer never made it into the shipped artifact. That's accurate for the runtime image, but it means SBOMs generated at different pipeline stages (build image vs. final image) will diverge sharply, and teams that scan the wrong stage get a false sense of a minimal footprint. Syft does support a --from flag to target a specific layer or platform variant, but the default behavior always resolves to the final, fully-merged filesystem.
How Does Syft Find Packages With No Metadata File at All?
For binaries with no accompanying package metadata, Syft falls back to binary classifiers that pattern-match against known byte signatures rather than reading a manifest. This matters for statically compiled Go binaries, stripped ELF executables, or vendored libraries like OpenSSL or libcurl copied directly into an image without a package manager record. Syft's binary cataloger scans for version strings embedded by common build toolchains — for example, a Go binary's embedded buildinfo section reveals the exact go.mod dependency list and the Go compiler version used, even with no vendor/ directory present. For C libraries, it matches against a curated table of byte-offset signatures maintained in Syft's binary-classifier gists. This is powerful, but also the mechanism most likely to miss something: a custom-compiled or unusually stripped binary with no recognizable signature simply produces no package entry, and the SBOM silently under-reports rather than flagging an unknown.
What Format Does Syft Actually Output, and Why Does That Matter?
Syft can emit the same scan result in more than a dozen formats — SPDX 2.2 and 2.3 (JSON and tag-value), CycloneDX 1.4 through 1.6 (JSON and XML), and its own native syft-json format — and the choice changes what information survives. Syft's native format preserves the richest detail: per-file SHA256 digests, the exact cataloger that found each package, and layer-level provenance showing which image layer introduced a given file. Converting that same scan to SPDX 2.3 keeps license and package-identity fields but drops most of the layer-attribution metadata, while CycloneDX 1.5 preserves dependency-graph relationships more faithfully than SPDX but represents licenses less precisely. Downstream tools like Grype, Dependency-Track, and most compliance platforms expect one specific format and schema version, so a mismatch between the format Syft was configured to emit and what the consuming tool parses is one of the most common causes of "packages missing from my vulnerability scan" support tickets.
Where Does This Single-Scan Approach Break Down at Scale?
Syft is architected as a point-in-time, single-artifact scanner, which means it has no native concept of fleet-wide drift, historical comparison, or continuous monitoring across the hundreds of images an organization ships in a given quarter. Anchore built Syft this way deliberately — it's meant to be a fast, embeddable library (the same code Anchore Enterprise calls internally), not a platform. That's a reasonable design choice for a CLI tool, but it leaves real gaps: there's no built-in way to diff today's SBOM against last week's and flag a newly introduced package, no cross-repository view of which of your 200 services still run a vulnerable Log4j version, and no policy engine deciding whether a given SBOM should block a deploy. Teams that adopt Syft directly typically end up gluing together the CLI, a database to store historical SBOMs, Grype for CVE matching, and custom scripts for policy enforcement — which is exactly the integration burden that pushed many of those same teams toward Anchore Enterprise, or toward a platform built to close those gaps from day one.
How Safeguard Helps
Safeguard is built around the same open, well-understood SBOM mechanics described above, but treats the scan as the start of a continuous process rather than a one-off CLI invocation. We ingest SBOMs at every stage of the pipeline — build image, final image, and runtime — so multi-stage divergence gets flagged explicitly instead of silently producing a "clean" report that only reflects the last stage. Every SBOM is versioned and diffed automatically, so a new package, a license change, or a dependency swap in a component's pom.xml shows up as a discrete, attributable event tied to the commit and pipeline run that introduced it, not something you discover during an audit six months later.
Because Safeguard understands the same catalogers-plus-classifiers model that drives detection accuracy, it also surfaces the failure modes Syft itself won't: images where a binary cataloger found zero matches on a suspicious statically-linked executable, or services still emitting SPDX 2.2 when downstream policy checks expect CycloneDX 1.5 and are silently dropping license data as a result. Vulnerability correlation, policy gating, and compliance evidence (SOC 2, FedRAMP, and similar frameworks) are generated from that same continuously updated SBOM history, so security and compliance teams get a fleet-wide view — across every image, every stage, every day — instead of the isolated, single-scan snapshot that a CLI tool alone can produce.