Safeguard
Developer Tools•

How do you diff two dependency scanner exports step by step?

A practical method to compare two scanner exports: pick a shared key, normalize names and versions, then sort differences into causes you can act on.

Safeguard Security Team
4 min read

To diff two scanner exports, convert each to a flat list of package identifier, version and advisory ID, normalize names, join on those three fields, and sort the leftovers into groups: missing package, different version, different advisory, different scope. Each group has a known cause and a known fix.

Step 1: choose a shared key

You need one identifier that both exports can express. A package URL, called a purl, is built for this. The Package-URL project describes it as a standard URL based syntax to identify software packages, independent from their ecosystem or distribution channel. If a tool does not emit purls, build a key from ecosystem, name and version yourself.

Use the advisory ID as a second key. Because one flaw can have several IDs, map them using the aliases field described in the OSV schema, which says aliases link the same vulnerability in other databases.

Step 2: flatten and normalize

Export both reports as JSON or CSV. Reduce each row to four fields: ecosystem, package name, version, advisory ID. Then normalize.

  • Lowercase names where the ecosystem is case insensitive.
  • For Python, collapse runs of dots, dashes and underscores to one dash.
  • Strip build metadata after a plus sign, since the SemVer specification says build metadata must be ignored when determining precedence.
  • Map every advisory ID to a canonical one through its aliases.

A small script is enough. The point is to make equal things look equal before you compare.

Step 3: join and bucket the leftovers

Join on ecosystem, name and version. Rows that match on both sides are agreement. For the rest, assign a bucket.

BucketTestUsual cause
Missing packageName absent from one exportDifferent input files or scope
Different versionSame name, versions differManifest vs lockfile, or different resolution
Different advisorySame package, different IDsAlias not mapped, or different database
Same advisory, one side silentPackage present, no findingDifferent affected range or database timing

Step 4: resolve each bucket

Work the buckets from the top. A missing package is the cheapest fix, because it usually means one tool was pointed at a different file. Align the inputs and rerun.

Version differences come next. Decide which artifact is the source of truth. For deployed software, the built artifact beats the manifest, because it is what runs.

For advisory differences, read the entry in each source. Compare the affected ranges and the fixed version. Note that the OSV schema recommends the fixed event over last affected, because last affected can allow false negatives.

Step 5: keep the result

Save the joined file with the date, both tool versions, and the input commit. Next quarter you can rerun the same procedure and compare the buckets, which tells you whether your gap is shrinking.

Try it yourself

Click through it at your own pace, or open it full screen. Notice how one finding carries package, version and advisory together, which are the three fields you join on.

Quick check

Do I need a script, or can I use a spreadsheet?

A spreadsheet works for a few hundred rows. Past that, a short script is faster and repeatable.

What if the two tools use different advisory IDs?

Map them through aliases, then join on the canonical ID. Without that step, the same flaw appears as two unrelated findings.

Sources

Last reviewed: 19 October 2026.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.

Self-healing security runs on Safeguard.

Your first fix PR is minutes away.

No sales call required, even your agent can complete the purchase over MCP.