Skip to content

The Drift Log · Provenance, licensing and the paper trail

Automated Detection of License Conflicts in Vendored Code

21 October 2026 · 3 min read · 550 words · inference

A wax seal resting on a page of hash digests

A repeatable, model‑independent scan of vendored code can flag license conflicts before SBOM generation.

The hidden risk in vendored code

Many teams copy a library into a vendor/ directory and forget to check its licence. The failure mode is a license conflict that surfaces only after the SBOM has been published. The conflict can invalidate the SBOM, force a costly re‑license, or expose the company to infringement claims. The problem is not the presence of vendored code; it is the lack of a verifiable provenance record before the code reaches the SBOM.

Scan the directory and assign licences

A reliable scan starts with a file‑by‑file licence lookup. The steps are:

  1. Walk the vendored tree.
  2. For each source file, read the first few lines for an SPDX identifier (SPDX-License-Identifier:).
  3. If no identifier is found, fall back to a heuristic that matches known licence header patterns.
  4. Record the result in a simple CSV: path, licence, confidence.
#!/usr/bin/env bash
root=$1
echo "path,license,confidence"
find "$root" -type f \( -name "*.js" -o -name "*.ts" -o -name "*.c" \) | while read f; do
  header=$(head -n 10 "$f")
  if grep -q "SPDX-License-Identifier:" <<<"$header"; then
    lic=$(grep "SPDX-License-Identifier:" <<<"$header" | cut -d: -f2 | tr -d ' ')
    echo "$f,$lic,high"
  elif grep -q "MIT License" <<<"$header"; then
    echo "$f,MIT,medium"
  else
    echo "$f,UNKNOWN,low"
  fi
done

The script produces a deterministic list of licences. The output can be version‑controlled, guaranteeing repeatability. The same approach can be wrapped in the typed TypeScript client (@shpbl/sdk) if you prefer a programmatic API – see the /sdk page for an example integration.

Flag incompatibilities against your policy

A licence conflict is only a conflict relative to a policy. Define a minimal matrix that captures the licences you allow to coexist. For example:

| Allowed with | MIT | Apache‑2.0 | GPL‑2.0 | |--------------|-----|------------|--------| | MIT | ✅ | ✅ | ❌ | | Apache‑2.0 | ✅ | ✅ | ❌ | | GPL‑2.0 | ❌ | ❌ | ✅ |

With the CSV from the scan, a short Python script can raise a failure for any row that violates the matrix.

import csv, sys

policy = {
    "MIT": {"MIT", "Apache-2.0"},
    "Apache-2.0": {"MIT", "Apache-2.0"},
    "GPL-2.0": {"GPL-2.0"},
}

conflicts = []
with open(sys.argv[1]) as f:
    for path, lic, _ in csv.reader(f):
        if lic == "UNKNOWN": continue
        allowed = policy.get(lic, set())
        for other_path, other_lic, _ in csv.reader(open(sys.argv[1])):
            if other_path == path: continue
            if other_lic not in allowed:
                conflicts.append((path, lic, other_path, other_lic))

if conflicts:
    for c in conflicts:
        print(f"Conflict: {c[0]} ({c[1]}) ↔ {c[2]} ({c[3]})")
    sys.exit(1)

Running the script in CI aborts the build if a conflict exists. The failure is explicit: file X is GPL‑2.0 while the project permits only MIT or Apache‑2.0. This makes the problem visible before the SBOM is generated.

Tie the licence report to SBOM verification

An SBOM should never contain a component whose licence conflicts with the project policy. The workflow is:

  1. Run the licence scanner.
  2. Fail fast on any conflict (as shown above).
  3. If the scan passes, invoke the SBOM generator (see the related post on Automated SBOM Generation from Build‑Intent Proposals).
  4. Attach the licence CSV as an artefact to the SBOM record.

Because the licence report is produced by a model‑independent tool, the provenance is repeatable and can be audited later. The SBOM verification step simply checks that the attached licence artefact contains no rows marked low confidence or UNKNOWN. If it does, the verification fails and the release is blocked.

Monday’s concrete step

  1. Add the scan script to the repository root.
  2. Add a CI job that runs the script on the vendor/ folder, aborts on non‑zero exit, and publishes the CSV as a build artefact.
  3. Run the job locally on Monday to confirm it produces a clean licence report for your current vendored code.

Doing this turns a folder of hope into a verifiable provenance record. It eliminates the hidden license conflict risk before the SBOM is created, and it gives you a repeatable audit trail for every line of vendored code. For a deeper dive into why missing provenance is a liability, see the pillar post /blog/vendoring-code-without-a-paper-trail-is-unsecured-debt.

Keep reading

Next in the log

The Strategic Master Library · written and reviewed under the house's own epistemic rules: nothing claimed that we cannot show.