Why does a manifest-based SBOM miss a compression library vendored into a C++ service's source tree?
answer
- parsers read declarations, not code
- vendored code declares nothing at all
- silently missing beats admittedly missing
- matching keys on identifiers, not names
- declare it, then watch upstream drift
basics
~20 sManifest and lockfile parsing records declarations, and vendored source declares nothing — it is just files in your repository. The component ships in the binary while the document says it is not there, which is worse than an admitted gap.
solid answer
~50 sThe pre-build vantage enumerates what the project *asked for*. A library copied into a `third_party/` directory was never asked for through a package manager, so there is no manifest entry, no lockfile line and no version to record. In C and C++ this is common, and often there is no lockfile at all. The result is not merely an incomplete document but a confidently wrong one: advisory matching keys on component identifiers, so an absent component is never matched and the service reports clean while shipping a library years behind upstream. Recovering it means changing vantage — binary or source analysis fingerprinting embedded version strings and symbols — and then *declaring* it: record the upstream version you forked from and the patch delta, and watch upstream. The control is a rule that nothing enters a vendored directory undeclared.
code
json · 16 lines{
"components": [
{
"type": "library",
"name": "transcode-app",
"version": "4.2.0",
"purl": "pkg:generic/[email protected]"
},
{
"type": "library",
"name": "compression-lib",
"version": "unknown",
"description": "vendored under third_party/, locally patched"
}
]
}go deeper
Know that a vendored library is just source files copied into the repository, and that a tool reading dependency declarations therefore has no way to know it is there.
Explain why both the pre-build and post-build vantages fail here — nothing declared, nothing separately packaged — and why an absent entry produces a false clean result in vulnerability matching.
Show the recovery path end to end: find it with source and binary evidence, declare it with an upstream version and patch note, and add the review control that stops the next copy landing undeclared.
Own the policy question. Decide whether vendoring is permitted at all in your estate, what a team must record when it is, and how you fund the recurring upstream-diff work that keeps those declarations from going stale.
### The scenario A video-transcoding worker written in C++ has a copy of a compression library checked directly into its source tree, added years ago so a patch could be applied without waiting for upstream. It compiles as part of the service. The team generates an SBOM from build metadata and manifests, gets a tidy document, and matches it against advisories with no findings. The service handles proprietary media pipelines, so both availability and trade-secret code matter; the vendored library is exactly the kind of parsing-heavy component where a malformed input becomes a memory-safety problem. ### Why the vantage cannot see it Manifest and lockfile parsing answers one question: *what did this project declare it depends on?* Vendored source is not a declaration. It is indistinguishable, to a parser, from code the team wrote. Three properties make it worse in C and C++ specifically: 1. **There may be no lockfile at all.** Many C and C++ builds have no ecosystem-wide dependency manifest, so the pre-build vantage has almost nothing to read to begin with. 2. **The copy is usually forked.** Local patches mean the code matches no upstream release exactly, so even a human cannot always name a single version truthfully. 3. **It compiles in.** The shipped artifact contains the library's code with no separate file, no package record and no loader dependency — so the post-build filesystem vantage will not see it either, for the same reason a statically linked dependency is invisible. ### Why this is worse than a gap An SBOM with a known gap is a document you read carefully. An SBOM that silently omits a component is a document that produces a *negative finding you believe*. Vulnerability matching works by comparing recorded component identifiers against advisory ranges. No entry means no comparison, which means no finding, which reads identically to "not vulnerable". This is the direction-of-claim trap in this domain: an SBOM tells you what is inside, and when it is wrong about that, everything computed downstream from it inherits the error while looking healthy. ### Finding it Since no declaration exists, you need evidence-based vantages: - **Source-tree inspection.** Directories named for third-party code, unusual licence headers, and large blocks of code in a style unlike the rest of the repository. Build-system inspection helps: the build must reference those sources somewhere. - **Binary and symbol analysis.** Compiled-in libraries often leave exported symbol names and embedded version strings behind. This is fingerprinting, not declaration — it can tell you *which* library with reasonable confidence and is much weaker on *which version*, particularly once local patches are applied. - **Asking.** For long-lived C and C++ code the fastest instrument is often the engineer who added it. ### Fixing it properly Detection alone changes nothing that recurs. The durable fix has three parts. **Declare it.** Add the component to the SBOM explicitly, with the upstream project, the upstream version you branched from, and a note that it is modified. Both major SBOM formats support hand-curated entries alongside generated ones. **Make it matchable.** An entry with a name and no identifier is a marginal improvement, because automated advisory lookup keys on identifiers rather than free text. Record an identifier you can actually match on and be honest that a forked copy makes range matching approximate — the advisory tells you the upstream version was affected; only reading your patch tells you whether your copy is. **Prevent the next one.** The control is a repository rule: nothing lands in a vendored directory without an accompanying declaration recording upstream project, version and patch rationale, enforced at review. Pair it with a periodic diff against upstream so the fork's drift is a tracked number rather than a discovery made during an incident. ### The generalisation worth stating Vendored source, statically linked libraries and bundled single-file distributions are the same failure in three costumes: **the component ships without a label.** Pre-build parsing misses it because nothing was declared; post-build scanning misses it because nothing is separately packaged. Any estate where these are common needs a declaration discipline, because no choice of generator or vantage will recover what was never recorded anywhere.
- You add the component by hand but have no usable identifier for it. Is the entry still worth having?Yes, but downgrade your expectations. Automated advisory matching will not fire on it, so its value is human: an incident responder searching the document finds the component and knows to check it manually. Treat it as a flagged known-weak entry, and invest in getting a matchable identifier and an upstream version recorded rather than leaving free text.
- Is a statically linked dependency the same problem as a vendored one?The visibility symptom is identical — the component ships without a label — but the remedies differ. A statically linked dependency was resolved by a build system, so build-time generation recovers it from the graph. Vendored source was never resolved by anything, so no vantage recovers it automatically; it has to be declared by a human once and maintained deliberately.
- What would you change in the build so this stops recurring?Route the copy through the dependency system if you possibly can, so it becomes a declared, versioned component with your patch applied as an explicit overlay. Where that is impossible, enforce a review rule that a vendored directory cannot change without its declaration record changing, and schedule a recurring diff against upstream so drift is measured continuously.
saying these in an interview costs you the question
- Assumes a clean advisory match means no vulnerable component
- Thinks a deeper artifact scan would have found vendored code
- Records the component by name with no matchable identifier
- Treats detection as the fix and skips the declaration discipline
- Claims the SBOM proves the service is not exploitable