Fix: wrong package coordinates sent to NCM API#253
Conversation
# Fix: wrong package coordinates sent to NCM API ## Summary | # | Report | Status | Where | |---|---|---|---| | 1 | "sending odd packages versions to the API like this one jwt version 0.0.0" | Fixed | `lib/ncm-analyze-tree.js`, `commands/report.js` | | 2 | `NCM_TOKEN=… NCM_API=… ncm` "with no success" | Not a bug; documented below | — | | 3 | "still a diff on npm audit and ncm report" | Fixed (root cause shared with #1) | `lib/ncm-analyze-tree.js` | | 4 | "we would need to investigate if our vulnDB has all the vulns" | Now meaningfully testable | see below | Tests: **24 / 24** pass (`npm run test-only`). --- ## Root cause (issues 1 & 3) `lib/ncm-analyze-tree.js` had been rewritten to use [`dependency-tree`](https://www.npmjs.com/package/dependency-tree) — a **source-code import analyzer** — instead of [`universal-module-tree`](https://www.npmjs.com/package/universal-module-tree), which walks the real npm dependency graph (`package-lock.json` → `yarn.lock` → `node_modules`). Three concrete defects in that code produced the payloads the user saw: 1. **Filename-as-package-name with placeholder version.** `dependency-tree` returns resolved file paths. The replacement code turned each path into a "package": ```js const name = path.basename(dep, path.extname(dep)); const childNode = { data: { name, version: '0.0.0' }, // hard-coded children: [] }; ``` So `jsonwebtoken.js` → `jwt @ 0.0.0`, `index.js` → `index @ 0.0.0`, and similar. That is the exact `jwt version 0.0.0` entry the user saw going out. 2. **Range-string fallback also produced `0.0.0`.** The auxiliary `getNpmDependencies` helper only read direct deps from `package.json` and did: ```js const cleanVersion = version.replace(/[^\d.]/g, '') || version; ``` For `"latest"`, `"file:…"`, `"git+https://…"`, `"workspace:*"`, etc. the strip leaves `''` and the `||` fallback is still non-semver — so those deps were emitted as `name @ 0.0.0` too. 3. **Missing transitive deps.** `dependency-tree` only surfaces files the entry point actually `require()`s, and the `package.json` fallback only listed direct dependencies. The full transitive graph was never walked. This is the direct mechanical cause of the diff vs `npm audit`. 4. **`commands/report.js` had been patched to hide the fallout.** Any `null` / `undefined` `version` returned by the NCM service was coerced to `'0.0.0'` and still included in the rendered report, so bogus rows showed up in the UI too. ## Fix ### `lib/ncm-analyze-tree.js` — rewritten (550 → 131 lines) - Reverted to `universal-module-tree` (already in `dependencies`, and the same library `commands/details.js` uses). - Removed, in full: - the `dependency-tree` invocation and entry-point discovery logic (guessing `index.js`, `app.js`, `bin/*.js`, …), - filename-as-package-name logic, - `getNpmDependencies` and the duplicate `readPackagesFromPackageJson` that stripped `^` / `~` and fell back to `'0.0.0'`, - dead counters. The new file gets a tree from `universalModuleTree(dir)`, walks it keeping dedup + `paths` info, and POSTs `{ name, version }` pairs to the GraphQL endpoint — same shape that already worked in `commands/details.js`. ### `commands/report.js` - Removed the `effectiveVersion = '0.0.0'` coercion. Packages the NCM service returns with `version === null` (unpublished / unknown) are now **skipped** instead of rendered as `@ 0.0.0`: ```js if (version === null || version === undefined) continue; ``` - Removed unused `includedCount` / `skippedCount` counters and the dead `getLicenseScore` helper. ### `package.json` / `package-lock.json` - Removed the unused `dependency-tree` dependency (`npm uninstall dependency-tree`). `universal-module-tree` was already listed and is now the single source of truth for the dependency graph. --- ## Verification ### Empirical: correct coordinates go out ``` $ node -e "const umt=require('universal-module-tree'); (async()=>{ const t=await umt('./test/fixtures/mock-project'); const f=umt.flatten(t); console.log('total:', f.length); console.log('zero-versions:', f.filter(p=>p.version==='0.0.0')); console.log('filename-like:', f.filter(p=>/\\.|\\//.test(p.name))); })()" total: 37 zero-versions: [] filename-like: [] ``` No `@ 0.0.0`, no `jwt`-style filename names — just real installed packages from `package-lock.json`. ### Tests: `npm run test-only` → 24 / 24 pass Notable guards: - `report › report output matches snapshot` — asserts **"36 packages checked"**, which only holds when the transitive graph is walked correctly. - `report › report with poisoned project` — asserts a mock package returned with `version: null` is **skipped**, not rendered as `@ 0.0.0`. - All `--filter=*`, `--compliance`, `--security`, `--long` snapshot tests pass, plus `details`, `whitelist`, `install`, `github-actions`, and `proxied` suites. --- ## Issue 2: bare `ncm` "with no success" `ncm` with no subcommand shows help and exits 0 — same as `git` or `npm` without args. `bin/ncm-cli.js`: ```js let [command = 'help', ...subargs] = argv._ if (!Object.keys(commands).includes(command)) command = 'help' ``` No API request is ever made, which is why the debug session looked like "nothing happening". Verified post-fix: `node bin/ncm-cli.js` prints help and exits 0. To actually exercise the analyzer against the local API: ```sh NCM_TOKEN=6c044113-c485-40cb-915b-cbc9d3a26730 \ NCM_API=http://localhost:3000 \ ncm report --dir=/path/to/some/project ``` --- ## Issue 4: vulnDB coverage investigation Previously impossible to do meaningfully — the payload was garbage (issue 1) and incomplete (issue 3). Now that real coordinates for the full transitive tree are sent, a direct diff is valid: ```sh cd /path/to/project npm install # ensure lockfile + node_modules npm audit --json > /tmp/npm-audit.json # npm's view NCM_TOKEN=… NCM_API=http://localhost:3000 \ ncm report --long --json > /tmp/ncm-report.json # ncm's view ``` Compare the `{name, version}` sets: - advisories in `npm-audit` but not `ncm-report` → real gaps in the vulnDB, feed back to the data team; - advisories in `ncm-report` but not `npm-audit` → extra NCM coverage (or a false positive to review). --- ## Files changed - `lib/ncm-analyze-tree.js` — rewritten (550 → 131 lines). - `commands/report.js` — `0.0.0` coercion and dead code removed. - `package.json`, `package-lock.json` — `dependency-tree` removed.
WalkthroughRefactors dependency-tree construction to use universal-module-tree, adds npm audit v7+ vulnerability handling, and removes unused counters and placeholder-version normalization in report generation and package listing. Changes
Sequence Diagram(s)sequenceDiagram
participant CLI as CLI (commands/report)
participant Analyzer as Analyzer (ncm-analyze-tree)
participant UMT as universal-module-tree
participant Audit as npm audit JSON
participant Reporter as pkgScores / report.js
CLI->>Analyzer: request package read (dir)
Analyzer->>UMT: universalModuleTree(dir)
UMT-->>Analyzer: module tree (children)
Analyzer->>Analyzer: build package set (name,version) / lockJson fast-path
CLI->>Audit: obtain npm audit JSON
Audit-->>Reporter: vulnerabilities array (v7+)
Analyzer-->>Reporter: package set + versionByName map
Reporter->>Reporter: filter out packages with missing versions
Reporter->>Reporter: push pkgScores entries (including vulnerabilities)
Reporter-->>CLI: final report output
Estimated Code Review Effort🎯 4 (Complex) | ⏱️ ~45 minutes Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
# Fix: wrong package coordinates sent to NCM API ## Summary | # | Report | Status | Where | |---|---|---|---| | 1 | "sending odd packages versions to the API like this one jwt version 0.0.0" | Fixed | `lib/ncm-analyze-tree.js`, `commands/report.js` | | 2 | `NCM_TOKEN=… NCM_API=… ncm` "with no success" | Not a bug; documented below | — | | 3 | "still a diff on npm audit and ncm report" | Fixed (root cause shared with #1) | `lib/ncm-analyze-tree.js` | | 4 | "we would need to investigate if our vulnDB has all the vulns" | Now meaningfully testable | see below | Tests: **24 / 24** pass (`npm run test-only`). --- ## Root cause (issues 1 & 3) `lib/ncm-analyze-tree.js` had been rewritten to use [`dependency-tree`](https://www.npmjs.com/package/dependency-tree) — a **source-code import analyzer** — instead of [`universal-module-tree`](https://www.npmjs.com/package/universal-module-tree), which walks the real npm dependency graph (`package-lock.json` → `yarn.lock` → `node_modules`). Three concrete defects in that code produced the payloads the user saw: 1. **Filename-as-package-name with placeholder version.** `dependency-tree` returns resolved file paths. The replacement code turned each path into a "package": ```js const name = path.basename(dep, path.extname(dep)); const childNode = { data: { name, version: '0.0.0' }, // hard-coded children: [] }; ``` So `jsonwebtoken.js` → `jwt @ 0.0.0`, `index.js` → `index @ 0.0.0`, and similar. That is the exact `jwt version 0.0.0` entry the user saw going out. 2. **Range-string fallback also produced `0.0.0`.** The auxiliary `getNpmDependencies` helper only read direct deps from `package.json` and did: ```js const cleanVersion = version.replace(/[^\d.]/g, '') || version; ``` For `"latest"`, `"file:…"`, `"git+https://…"`, `"workspace:*"`, etc. the strip leaves `''` and the `||` fallback is still non-semver — so those deps were emitted as `name @ 0.0.0` too. 3. **Missing transitive deps.** `dependency-tree` only surfaces files the entry point actually `require()`s, and the `package.json` fallback only listed direct dependencies. The full transitive graph was never walked. This is the direct mechanical cause of the diff vs `npm audit`. 4. **`commands/report.js` had been patched to hide the fallout.** Any `null` / `undefined` `version` returned by the NCM service was coerced to `'0.0.0'` and still included in the rendered report, so bogus rows showed up in the UI too. ## Fix ### `lib/ncm-analyze-tree.js` — rewritten (550 → 131 lines) - Reverted to `universal-module-tree` (already in `dependencies`, and the same library `commands/details.js` uses). - Removed, in full: - the `dependency-tree` invocation and entry-point discovery logic (guessing `index.js`, `app.js`, `bin/*.js`, …), - filename-as-package-name logic, - `getNpmDependencies` and the duplicate `readPackagesFromPackageJson` that stripped `^` / `~` and fell back to `'0.0.0'`, - dead counters. The new file gets a tree from `universalModuleTree(dir)`, walks it keeping dedup + `paths` info, and POSTs `{ name, version }` pairs to the GraphQL endpoint — same shape that already worked in `commands/details.js`. ### `commands/report.js` - Removed the `effectiveVersion = '0.0.0'` coercion. Packages the NCM service returns with `version === null` (unpublished / unknown) are now **skipped** instead of rendered as `@ 0.0.0`: ```js if (version === null || version === undefined) continue; ``` - Removed unused `includedCount` / `skippedCount` counters and the dead `getLicenseScore` helper. ### `package.json` / `package-lock.json` - Removed the unused `dependency-tree` dependency (`npm uninstall dependency-tree`). `universal-module-tree` was already listed and is now the single source of truth for the dependency graph. --- ## Verification ### Empirical: correct coordinates go out ``` $ node -e "const umt=require('universal-module-tree'); (async()=>{ const t=await umt('./test/fixtures/mock-project'); const f=umt.flatten(t); console.log('total:', f.length); console.log('zero-versions:', f.filter(p=>p.version==='0.0.0')); console.log('filename-like:', f.filter(p=>/\\.|\\//.test(p.name))); })()" total: 37 zero-versions: [] filename-like: [] ``` No `@ 0.0.0`, no `jwt`-style filename names — just real installed packages from `package-lock.json`. ### Tests: `npm run test-only` → 24 / 24 pass Notable guards: - `report › report output matches snapshot` — asserts **"36 packages checked"**, which only holds when the transitive graph is walked correctly. - `report › report with poisoned project` — asserts a mock package returned with `version: null` is **skipped**, not rendered as `@ 0.0.0`. - All `--filter=*`, `--compliance`, `--security`, `--long` snapshot tests pass, plus `details`, `whitelist`, `install`, `github-actions`, and `proxied` suites. --- ## Issue 2: bare `ncm` "with no success" `ncm` with no subcommand shows help and exits 0 — same as `git` or `npm` without args. `bin/ncm-cli.js`: ```js let [command = 'help', ...subargs] = argv._ if (!Object.keys(commands).includes(command)) command = 'help' ``` No API request is ever made, which is why the debug session looked like "nothing happening". Verified post-fix: `node bin/ncm-cli.js` prints help and exits 0. To actually exercise the analyzer against the local API: ```sh NCM_TOKEN=6c044113-c485-40cb-915b-cbc9d3a26730 \ NCM_API=http://localhost:3000 \ ncm report --dir=/path/to/some/project ``` --- ## Issue 4: vulnDB coverage investigation Previously impossible to do meaningfully — the payload was garbage (issue 1) and incomplete (issue 3). Now that real coordinates for the full transitive tree are sent, a direct diff is valid: ```sh cd /path/to/project npm install # ensure lockfile + node_modules npm audit --json > /tmp/npm-audit.json # npm's view NCM_TOKEN=… NCM_API=http://localhost:3000 \ ncm report --long --json > /tmp/ncm-report.json # ncm's view ``` Compare the `{name, version}` sets: - advisories in `npm-audit` but not `ncm-report` → real gaps in the vulnDB, feed back to the data team; - advisories in `ncm-report` but not `npm-audit` → extra NCM coverage (or a false positive to review). --- ## Files changed - `lib/ncm-analyze-tree.js` — rewritten (550 → 131 lines). - `commands/report.js` — `0.0.0` coercion and dead code removed. - `package.json`, `package-lock.json` — `dependency-tree` removed.
There was a problem hiding this comment.
🧹 Nitpick comments (3)
lib/ncm-analyze-tree.js (3)
74-74: Missing error handling foruniversalModuleTreecall.If
universalModuleTree(dir)throws (e.g., directory doesn't exist, invalid package.json), the error propagates unhandled. Consider whether a fallback or more descriptive error message would improve the user experience.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@lib/ncm-analyze-tree.js` at line 74, The call to universalModuleTree(dir) can throw and is not caught; wrap the await universalModuleTree(dir) invocation in a try/catch (in lib/ncm-analyze-tree.js around the const tree assignment), capture the thrown error, and handle it by logging or rethrowing a new Error with contextual information (include dir and the original error message) or returning a sensible fallback (e.g., empty tree) depending on desired behavior; ensure the rest of the function uses the caught result (tree) only after successful resolution.
61-72: Consider handling optionalintegrity/resolvedfields or malformed entries more defensively.The fast path reads
package-lock.jsonand extracts packages. While it correctly skips entries withoutnameorversion, there's no validation thatpkgDatais an object. IflockJson.packagescontains malformed entries (e.g.,nullvalues),pkgData.versioncould throw.🛡️ Proposed defensive check
for (const [pkgPath, pkgData] of Object.entries(lockJson.packages)) { if (!pkgPath) continue + if (!pkgData || typeof pkgData !== 'object') continue const parts = pkgPath.split('node_modules/') const name = parts[parts.length - 1] const version = pkgData.version🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@lib/ncm-analyze-tree.js` around lines 61 - 72, The fast-path loop over lockJson.packages lacks defensive checks for malformed pkgData (it assumes pkgData is an object and accesses pkgData.version); update the loop in lib/ncm-analyze-tree.js that iterates over lockJson.packages to first ensure pkgData is a non-null object (e.g., typeof pkgData === 'object' && pkgData !== null), validate that pkgData.version is present and a string, and tolerate optional fields like integrity/resolved; if checks fail, skip that entry instead of accessing properties, then continue calling set.add({ name, version, paths: [[]] }) only for validated entries.
26-32: Unhandled rejection if any page fetch fails.If
fetchDatathrows for one page in a batch,Promise.allwill reject immediately but other concurrent requests continue executing. Their results are lost, and errors may be unhandled. Consider usingPromise.allSettledif partial results are acceptable, or adding explicit error handling.💡 Example with error handling
for (const batch of batches) { - await Promise.all([...batch].map(async page => { - const fetchedData = await fetchData({ pkgs: page, token, url }) - for (const datum of fetchedData) { - data.add(datum) - } - })) + const results = await Promise.allSettled([...batch].map(async page => { + return fetchData({ pkgs: page, token, url }) + })) + for (const result of results) { + if (result.status === 'fulfilled') { + for (const datum of result.value) { + data.add(datum) + } + } + // Optionally log or collect rejected results + } }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@lib/ncm-analyze-tree.js` around lines 26 - 32, The current batch concurrency uses Promise.all([...batch].map(async page => { const fetchedData = await fetchData({ pkgs: page, token, url }) ... })) which will reject immediately on the first fetchData error and lose other results; change this to use Promise.allSettled for each batch, iterate the settled results, add values from fulfilled results into data (using data.add) and explicitly handle/rethrow or log rejections from the rejected entries so errors are not left unhandled and partial results are preserved.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@lib/ncm-analyze-tree.js`:
- Line 74: The call to universalModuleTree(dir) can throw and is not caught;
wrap the await universalModuleTree(dir) invocation in a try/catch (in
lib/ncm-analyze-tree.js around the const tree assignment), capture the thrown
error, and handle it by logging or rethrowing a new Error with contextual
information (include dir and the original error message) or returning a sensible
fallback (e.g., empty tree) depending on desired behavior; ensure the rest of
the function uses the caught result (tree) only after successful resolution.
- Around line 61-72: The fast-path loop over lockJson.packages lacks defensive
checks for malformed pkgData (it assumes pkgData is an object and accesses
pkgData.version); update the loop in lib/ncm-analyze-tree.js that iterates over
lockJson.packages to first ensure pkgData is a non-null object (e.g., typeof
pkgData === 'object' && pkgData !== null), validate that pkgData.version is
present and a string, and tolerate optional fields like integrity/resolved; if
checks fail, skip that entry instead of accessing properties, then continue
calling set.add({ name, version, paths: [[]] }) only for validated entries.
- Around line 26-32: The current batch concurrency uses
Promise.all([...batch].map(async page => { const fetchedData = await fetchData({
pkgs: page, token, url }) ... })) which will reject immediately on the first
fetchData error and lose other results; change this to use Promise.allSettled
for each batch, iterate the settled results, add values from fulfilled results
into data (using data.add) and explicitly handle/rethrow or log rejections from
the rejected entries so errors are not left unhandled and partial results are
preserved.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 4ff009c5-b393-41a5-be05-bd53683368c9
📒 Files selected for processing (3)
commands/report.jslib/ncm-analyze-tree.jslib/report/util.js
🚧 Files skipped from review as they are similar to previous changes (1)
- commands/report.js
Fix: wrong package coordinates sent to NCM API
Summary
lib/ncm-analyze-tree.js,commands/report.jsNCM_TOKEN=… NCM_API=… ncm"with no success"lib/ncm-analyze-tree.js,commands/report.jsncm reportcrashes withCannot read properties of undefined (reading '@actions/core')on npm v7+lib/ncm-analyze-tree.jscommands/report.jsTests: 24 / 24 pass (
npm run test-only).Root cause (issues 1 & 3)
lib/ncm-analyze-tree.jshad been rewritten to usedependency-tree— a source-code import analyzer — instead ofuniversal-module-tree, which walks the real npm dependency graph (package-lock.json→yarn.lock→node_modules).Four concrete defects in that code produced the payloads the user saw:
Filename-as-package-name with placeholder version.
dependency-treereturns resolved file paths. The replacement code turned each path into a "package":So
jsonwebtoken.js→jwt @ 0.0.0,index.js→index @ 0.0.0, and similar. That is the exactjwt version 0.0.0entry the user saw going out.Range-string fallback also produced
0.0.0. The auxiliarygetNpmDependencieshelper only read direct deps frompackage.jsonand did:For
"latest","file:…","git+https://…","workspace:*", etc. the strip leaves''and the||fallback is still non-semver — so those deps were emitted asname @ 0.0.0too.Missing transitive deps.
dependency-treeonly surfaces files the entry point actuallyrequire()s, and thepackage.jsonfallback only listed direct dependencies. The full transitive graph was never walked. This is the direct mechanical cause of the diff vsnpm audit.commands/report.jshad been patched to hide the fallout. Anynull/undefinedversionreturned by the NCM service was coerced to'0.0.0'and still included in the rendered report, so bogus rows showed up in the UI too.Root cause (issues 5 & 6) — npm v7+ compatibility
Two additional breakages affecting projects using npm v7+ (lockfileVersion 3):
Issue 5:
universal-module-treeonly handlespackage-lock.jsonv1/v2 format, which has a top-leveldependenciesfield. npm v7+ generates v3 lockfiles with only apackagesfield. CallinguniversalModuleTree(dir)on a v3 lockfile hitspackageLock.dependencies['@actions/core']wherepackageLock.dependenciesisundefined, throwingCannot read properties of undefined (reading '@actions/core').Issue 6:
npm audit --jsonchanged its output format in npm v7+ fromauditReportVersion: 1(advisoriesfield) toauditReportVersion: 2(vulnerabilitiesfield). The existing code only checkednpmAuditJson.advisories, so all npm audit results were silently ignored on npm v7+, causing the report to show zero risks regardless of actual vulnerabilities.Fix
lib/ncm-analyze-tree.js— rewritten (550 → 95 lines)Reverted to
universal-module-tree(already independencies, and the same librarycommands/details.jsuses).Removed, in full:
dependency-treeinvocation and entry-point discovery logic (guessingindex.js,app.js,bin/*.js, …),getNpmDependenciesand the duplicatereadPackagesFromPackageJsonthat stripped^/~and fell back to'0.0.0',npm v7+ lockfile fix (issue 5): Reads
package-lock.jsonbefore callinguniversalModuleTree. IflockfileVersion >= 3(no.dependenciesfield), builds the package set directly fromlock.packages, bypassinguniversalModuleTreeentirely:commands/report.jsRemoved the
effectiveVersion = '0.0.0'coercion. Packages the NCM service returns withversion === null(unpublished / unknown) are now skipped instead of rendered as@ 0.0.0:Removed unused
includedCount/skippedCountcounters and the deadgetLicenseScorehelper.npm audit v7+ fix (issue 6): Added handling for
auditReportVersion: 2format alongside the existing v6 handler. Versions are resolved from the NCM API result set:lib/report/util.jspkg.version != null ? String(pkg.version) : 'N/A'to handle entries from npm audit where the version is not available in the audit output.package.json/package-lock.jsondependency-treedependency (npm uninstall dependency-tree).universal-module-treewas already listed and is now the single source of truth for the dependency graph.Verification
Empirical: correct coordinates go out
No
@ 0.0.0, nojwt-style filename names — just real installed packages frompackage-lock.json.npm v7+ projects:
ncm reportnow works end-to-endBefore: crash with
Cannot read properties of undefined (reading '@actions/core').After (on a project with lockfileVersion 3):
npm audit vulnerabilities are now correctly surfaced in the risk summary counts.
Tests:
npm run test-only→ 24 / 24 passNotable guards:
report › report output matches snapshot— asserts "36 packages checked", which only holds when the transitive graph is walked correctly.report › report with poisoned project— asserts a mock package returned withversion: nullis skipped, not rendered as@ 0.0.0.--filter=*,--compliance,--security,--longsnapshot tests pass, plusdetails,whitelist,install,github-actions, andproxiedsuites.Issue 2: bare
ncm"with no success"ncmwith no subcommand shows help and exits 0 — same asgitornpmwithout args.bin/ncm-cli.js:No API request is ever made, which is why the debug session looked like "nothing happening". Verified post-fix:
node bin/ncm-cli.jsprints help and exits 0. To actually exercise the analyzer against the local API:Issue 4: vulnDB coverage — investigation done and fixed
The root cause of the previously reported diff was twofold:
universal-module-tree.vulnerabilitiesformat.Files changed
lib/ncm-analyze-tree.js— rewritten (550 → 95 lines); added npm v7+ lockfile v3 support.commands/report.js—0.0.0coercion and dead code removed; added npm audit v7+ format support.lib/report/util.js— null-safe version display.package.json,package-lock.json—dependency-treeremoved.