Skip to content

feat(node-audit): migrate npm audit to bulk advisories endoint#8471

Closed
BerSomBen wants to merge 2 commits intodependency-check:mainfrom
BerSomBen:main
Closed

feat(node-audit): migrate npm audit to bulk advisories endoint#8471
BerSomBen wants to merge 2 commits intodependency-check:mainfrom
BerSomBen:main

Conversation

@BerSomBen
Copy link
Copy Markdown

Description of Change

use the bulk adversery endpoint for npm audit

Related issues

fixes #7292

Have test cases been added to cover the new functionality?

yes

@boring-cyborg boring-cyborg Bot added core changes to core documentation site documentation maven changes to the maven plugin tests test cases utils changes to utils labels Apr 30, 2026
@marcelstoer
Copy link
Copy Markdown
Collaborator

In #8422 we floated the idea of using the npm CLI directly like the pnpm analyzer does.

@chadlwilson
Copy link
Copy Markdown
Collaborator

chadlwilson commented Apr 30, 2026

if we were going to have a direct API integration and maintain it I wonder if we'd be putting it in open-vulnerability-clients instead.

Personally I'm not really in favour of this approach (unless there is no other choice), especially as NPM have shown they don't care about any clients other than npm CLI itself in how they have treated the removal of the quick API. (undocumented deprecation, no warning unannounced rolling brownouts, no public announcement of EOL date)

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR updates the Node/NPM audit integration to use npm’s bulk advisories API endpoint (used by npm v7+), and adjusts related configuration and tests to align with the new behavior.

Changes:

  • Switch default/configured Node Audit URL to /-/npm/v1/security/advisories/bulk across runtime, docs, and test resources.
  • Add bulk request payload builder and a new bulk response parser; update analyzers/searcher to use the new flow.
  • Make test updates for localization/assumptions and add a unit test for bulk payload sorting/deduping.

Reviewed changes

Copilot reviewed 12 out of 12 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
utils/src/test/resources/dependencycheck.properties Updates test resource URL to bulk endpoint.
utils/src/test/java/org/owasp/dependencycheck/utils/XmlUtilsTest.java Makes schema validation assertions tolerant to localized quoting.
maven/src/site/markdown/configuration.md Updates documented default Node Audit URL.
core/src/test/resources/dependencycheck.properties Updates core test resource URL to bulk endpoint.
core/src/test/java/org/owasp/dependencycheck/data/nodeaudit/NpmPayloadBuilderTest.java Adds unit test for bulk payload dedupe/sort.
core/src/test/java/org/owasp/dependencycheck/analyzer/YarnAuditAnalyzerIT.java Skips IT when yarn initialization fails instead of failing setup.
core/src/main/resources/dependencycheck.properties Updates runtime default Node Audit URL to bulk endpoint.
core/src/main/java/org/owasp/dependencycheck/data/nodeaudit/NpmPayloadBuilder.java Adds buildBulkPayload and version normalization helper.
core/src/main/java/org/owasp/dependencycheck/data/nodeaudit/NpmBulkAuditParser.java New parser for bulk advisories response mapped to installed versions.
core/src/main/java/org/owasp/dependencycheck/data/nodeaudit/NodeAuditSearch.java Submits bulk payload; parses with bulk parser; handles 410 Gone messaging.
core/src/main/java/org/owasp/dependencycheck/analyzer/YarnAuditAnalyzer.java Switches Yarn Classic path to bulk submission using dependency map.
core/src/main/java/org/owasp/dependencycheck/analyzer/NodeAuditAnalyzer.java Switches Node audit analyzer to bulk submission using dependency map.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +56 to +85
public List<Advisory> parse(JSONObject bulkResponse, MultiValuedMap<String, String> dependencyMap)
throws JSONException {
final List<Advisory> out = new ArrayList<>();
final Iterator<?> pkgKeys = bulkResponse.keys();
while (pkgKeys.hasNext()) {
final String moduleName = (String) pkgKeys.next();
final JSONArray advisoriesForPkg = bulkResponse.optJSONArray(moduleName);
if (advisoriesForPkg == null) {
continue;
}
for (int i = 0; i < advisoriesForPkg.length(); i++) {
final JSONObject raw = advisoriesForPkg.getJSONObject(i);
for (String installedVersion : new LinkedHashSet<>(dependencyMap.get(moduleName))) {
final String normalized = NpmPayloadBuilder.normalizeVersion(installedVersion);
if (normalized == null || normalized.isEmpty()) {
continue;
}
final String range = raw.optString("vulnerable_versions", "");
if (range.isEmpty()) {
continue;
}
if (!versionSatisfiesRange(normalized, range)) {
continue;
}
out.add(toAdvisory(moduleName, normalized, raw));
}
}
}
return out;
}
Comment on lines +184 to +189
// Walk the lockfile to populate dependencyMap (used for the bulk advisory request body and response mapping)
NpmPayloadBuilder.build(lockJson, packageJson, dependencyMap,
getSettings().getBoolean(Settings.KEYS.ANALYZER_NODE_AUDIT_SKIPDEV, false));

// Submits the package payload to the nsp check service
return getSearcher().submitPackage(payload);
// Submits package versions to the npm bulk advisory API
return getSearcher().submitPackage(dependencyMap);
Comment on lines +323 to +326
NpmPayloadBuilder.build(lockJson, packageJson, dependencyMap, skipDevDependencies);

// Submits the package payload to the nsp check service
return getSearcher().submitPackage(payload);
// Submits package versions to the npm bulk advisory API
return getSearcher().submitPackage(dependencyMap);
Comment on lines +61 to +74
public static JsonObject buildBulkPayload(MultiValuedMap<String, String> dependencyMap) {
final JsonObjectBuilder root = Json.createObjectBuilder();
for (String name : new TreeSet<>(dependencyMap.keySet())) {
final Set<String> versions = new TreeSet<>();
for (String raw : dependencyMap.get(name)) {
final String v = normalizeVersion(raw);
if (v == null || v.isEmpty()) {
continue;
}
if (NodePackageAnalyzer.shouldSkipDependency(name, v)) {
continue;
}
versions.add(v);
}
Comment on lines +97 to +103
private Advisory toAdvisory(String moduleName, String resolvedVersion, JSONObject object) throws JSONException {
final Advisory advisory = new Advisory();
if (object.has("github_advisory_id")) {
advisory.setGhsaId(object.getString("github_advisory_id"));
} else if (object.has("id")) {
advisory.setGhsaId(String.valueOf(object.get("id")));
}
@chadlwilson
Copy link
Copy Markdown
Collaborator

chadlwilson commented Apr 30, 2026

With a quick look through, this PR looks pretty "AI sloppy" - includes unrelated changes (why is it touching XmlUtils? Why is it disabling yarn tests?), overly complex string hacking for "normalizing versions", incomplete CVSS handling, alongside various approaches inconsistent with the rest of the codebase.

@BerSomBen are you planning to document your design decisions, approach and testing performed?

@BerSomBen
Copy link
Copy Markdown
Author

In #8422 we floated the idea of using the npm CLI directly like the pnpm analyzer does.

ough.... I did not notice this. For sure, this is the bettter way since npm does not care about its own endpoints.

/**
* Trims and strips JSON-string-style surrounding quotes from lockfile-derived versions.
*/
static String normalizeVersion(String raw) {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks very brittle/dodgy.

try {
analyzer = createAndPrepareYarnAnalyzer(engine);
} catch (InitializationException e) {
assumeTrue(false, "Yarn not on PATH or Yarn Audit analyzer failed to initialize (install yarn to run this IT): " + e.getMessage());
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why?

assertThat(t.getMessage(), containsString("cvc-elt.1.a"));
assertThat(t.getMessage(), containsString("'items'"));
// XSD error text is localized; element name may be quoted with ' or " depending on JVM locale
assertThat(t.getMessage(), anyOf(containsString("'items'"), containsString("\"items\"")));
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this necessary?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

builds on non english os's could get a different quotation style. on my german system, I got ''" and a failing test.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok but that's separate to an npm change. Better to create separate PR or at least mention in your description why you changed seemingly unrelated things in your PR.

@BerSomBen
Copy link
Copy Markdown
Author

With a quick look through, this PR looks pretty "AI sloppy" - includes unrelated changes (why is it touching XmlUtils? Why is it disabling yarn tests?), overly complex string hacking for "normalizing versions", incompletely CVSS handling, alongside various approaches inconsistent with the rest of the codebase.

@BerSomBen are you planning to document your design decisions, approach and testing performed?

@chadlwilson since my CI is crying all the day, I was pushing an approach fixing the issue. But as U wrote prior: it would be much easier, cleaner and better to maintain if we use the npm cli.
therefore: no, I will not invest any further here.

@BerSomBen BerSomBen closed this Apr 30, 2026
@chadlwilson
Copy link
Copy Markdown
Collaborator

chadlwilson commented Apr 30, 2026

In #8422 we floated the idea of using the npm CLI directly like the pnpm analyzer does.

ough.... I did not notice this. For sure, this is the bettter way since npm does not care about its own endpoints.

I think we probably need to decide in general on approach. There are some downsides to just using the npm cli directly (again, I am assuming it is possible and has an appropriate JSON output from npm audit) but maintaining a lockfile parser (and coupling to direct API integration) just seems not a good idea to me, especially given the "value add" ODC brings for all of these is not high - but difficulty of maintaining the code and keeping it working over time seems high. Our chance of keeping up with pnpm, yarn, npm cli, npm registry, GHSA just seems....low... to me..

Probably close to zero if you start including support for workspaces and other things like that.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

core changes to core documentation site documentation maven changes to the maven plugin tests test cases utils changes to utils

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Update NpmAuditParser to Handle Changes in NPM Audit v7+ Responses to Retrieve github_advisory_id

4 participants