Your SCA reports zero dependencies for a statically linked Go binary — is that good news?
answer
- Empty inventory versus clean artifact
- No lockfile travels with a binary
- Go embeds its module list
- Scan the artifact, not just the repo
- Static C keeps no metadata at all
basics
~20 sNo. A manifest-based scanner found nothing because no lockfile ships beside the binary. The result describes the scanner's input, not the artifact. Go binaries embed their module list, so an artifact-level scan still enumerates them.
solid answer
~50 sAn empty result from a lockfile-driven scanner means it found no lockfile, not that the artifact has no dependencies — a statically linked binary is shipped without `go.mod` or `go.sum` beside it. The fix is to inventory the binary itself: Go records the modules and versions compiled into an executable as embedded build metadata, and binary or filesystem-capable scanners such as Trivy and Grype read exactly that, so scanning the image or filesystem containing the binary recovers the module list a manifest scan missed. Better still, generate the inventory at build time when the resolved graph is right there, and attach it to the artifact so consumers never depend on re-deriving it. The general lesson matters more than the Go specifics: treat a zero-finding result as a claim about what the tool could parse, and always ask what inventory it managed to build before you believe it.
go deeper
Be ready to say that a scanner needs something to read, and that a lockfile lives in the source repository rather than inside a compiled binary.
Explain the two-stage pipeline — inventory, then match — and why an empty inventory produces a report that is indistinguishable from a clean one.
Show the recovery path and its limits: read the binary's embedded module metadata with an artifact-level scan, and capture the inventory at build time for shapes where reconstruction cannot work.
Own the failure mode across an estate: decide that a zero-component scan is a failed scan, and make inventory completeness a reported metric rather than something an engineer has to notice.
## Zero findings is a statement about the input The single most useful instinct in this area is suspicion of empty results. A scanner's pipeline is: build an inventory, then match that inventory against advisory data. If the inventory is empty, the match is trivially empty too, and the report looks identical to a genuinely clean artifact. Distinguishing "nothing vulnerable" from "nothing inventoried" is a senior habit, and the statically linked binary is the cleanest example of the trap. ## Why the manifest scanner sees nothing Lockfile and manifest SCA works by parsing declared and resolved dependency files. For a Go service, that is `go.mod` and `go.sum` in the source repository. Those files are build inputs; they are not part of the compiled output, and a container image that contains only a compiled binary — the classic minimal Go image — contains neither. Point OSV-Scanner at that image's filesystem looking for lockfiles and it will correctly report that it found none. The same shape recurs elsewhere and is worth naming, because interviewers like the generalisation: a vendored dependency tree with no manifest, a shaded or relocated fat jar where library classes have been rewritten into the application's namespace, and a statically linked C or C++ binary, which is the hardest case of all because it carries no dependency metadata whatsoever. ## Why Go is the lucky case Go embeds build information into the executable, including the main module and the list of dependency modules with their versions. That metadata is part of the binary, so a scanner that knows how to read it can enumerate the modules that were compiled in without any source files at all. Trivy and Grype both handle this class of binary analysis, which is why scanning the *image or filesystem* recovers what scanning for *lockfiles* missed. If you take one operational lesson away: for Go artefacts, scan the artifact, not just the repository. Statically linked C is the counter-example that shows this is a property of the language toolchain rather than of static linking. Nothing in that binary records which version of which library was linked in, so there is no metadata to read; recovering that inventory needs build-time capture or fuzzy matching that you should not want to depend on. ## Do it at build time instead Re-deriving an inventory from a finished artifact is always reconstruction. At build time the resolved graph is authoritative and free: the toolchain already knows every module and version it compiled in, including the build-only ones. Generating a component inventory there and attaching it to the artifact turns every later consumer's question — yours in six months, a customer's next quarter — into a lookup rather than an archaeology exercise. It also covers the cases where reconstruction genuinely cannot work. ## What good triage looks like here When a service reports zero findings, ask three questions before believing it: 1. **What did the tool inventory?** Most scanners will tell you how many components they found. Zero components is a red flag; a plausible count is a green one. 2. **Was the tool given the right artefact?** A repository scan and an artifact scan answer different questions, and for compiled languages the artifact is the one that ships. 3. **Does the artefact shape have known blind spots?** Static binaries, vendored trees, shaded jars and hand-copied files are the recurring ones. ## The interview answer Say plainly that an empty result is not evidence of a clean artifact, name the reason — no manifest travels with the binary — and give the recovery: scan the artifact with a tool that reads embedded build metadata, and generate the inventory at build time so nobody has to reconstruct it. If you also volunteer that a statically linked C binary would not be recoverable this way, you have shown you understand *why* the Go case works rather than having memorised that it does.
- Which artifact shapes fall into this same blind spot?Vendored dependency trees with no manifest, shaded or relocated fat jars where library classes are rewritten into the application namespace, files copied in by a build step rather than resolved, and statically linked C or C++ binaries. The last is the worst, because unlike Go it embeds no dependency metadata to recover.
- Why prefer generating the inventory at build time over reconstructing it from the artifact?Because at build time the resolved graph is authoritative and complete, while reconstruction is best-effort and language-dependent. Capturing it once and attaching it to the artifact means every later consumer looks it up instead of re-deriving it, and it covers the shapes where re-derivation simply cannot work.
- How would you catch this class of false green systematically?Alert on the inventory, not just the findings: a scan that reports zero components for a service that obviously has dependencies is a tooling failure, and it should be treated as a failed scan rather than a passing one. A component count per artifact makes the failure visible without any human noticing an unusually quiet report.
A luggage scanner that reports nothing because the bag never went through the machine looks exactly like one reporting an empty bag. The output is the same; the meaning is not.
saying these in an interview costs you the question
- Reads an empty result as a clean artifact
- Assumes every compiled binary embeds a dependency list
- Thinks static linking removes exposure to library advisories
- Believes scanning the repository proves what the binary contains
- Never checks how many components the scan inventoried