Status: Draft for review — not to be committed to Git. Target release: v5.0.0 (semantic-release, breaking changes) Date drafted: 2026-05-13
This document captures the full modernisation scope for AuditJS targeting a v5.0.0 release. The driving goals are:
- Replace OSS Index with Sonatype Guide (deprecate
ossi, introduceguide) - Reflect the Nexus IQ Server → Sonatype Lifecycle rebrand (
iqkept,lifecycleadded as canonical) - Remediate applicable security findings from issue #287
- Update all stale/vulnerable dependencies
- Migrate test framework to Vitest
- Migrate from Yarn to npm
- Enforce TypeScript strict syntax throughout
- Update all documentation (README, CONTRIBUTING, inline docs, CI examples)
- Produce a dedicated v4 to v5 migration guide (MIGRATION.md)
- Align with Sonatype Community Standards
- Achieve zero Sonatype Lifecycle policy violations
| Area | Change | Migration path |
|---|---|---|
| New command | guide replaces ossi as the primary public-facing scanner |
Use auditjs guide instead of auditjs ossi |
| Deprecated command | ossi retained but prints a deprecation warning |
Will be removed in v6 |
| New command alias | lifecycle added as canonical alias for iq |
Use auditjs lifecycle going forward |
| Deprecated alias | iq retained, prints a deprecation warning |
Will be removed in v6 |
| Node.js engine | Minimum bumped from >=16.13.0 to >=20.0.0 |
Upgrade Node runtime |
node-fetch removed |
All HTTP now uses Node native fetch |
No user-facing change |
Note on item 1 of issue #287 (hardcoded credentials): This is not a valid security finding. The
admin/admin123values referenced in the issue are the publicly-documented default credentials for an out-of-the-box Sonatype Lifecycle installation — they are not present in auditjs source code. The currentsrc/index.tssets no default values for--useror--password. A comment will be posted on issue #287 to clarify this and confirm no code change will be made.
The --insecure flag is a required feature, not a vulnerability. Some users operate Sonatype Lifecycle behind corporate infrastructure with self-signed or internal CA certificates.
Two changes are required:
- Add a runtime warning when the flag is used:
public static getAgent(insecure = false): Agent | undefined {
if (insecure) {
console.warn(
'WARNING: --insecure disables TLS certificate validation. ' +
'Do not use in production environments.'
);
return new HttpsAgent({ rejectUnauthorized: false });
}
return this.getHttpAgent();
}- Restrict to Sonatype Lifecycle only. Do not expose
--insecureon theguidecommand. Sonatype Guide is a cloud service with a valid publicly-trusted TLS certificate; there is no legitimate use case for bypassing certificate validation against it.
File: src/Config/Config.ts:49 — saveFile() method
After writing the config file, apply restrictive permissions:
import { chmodSync } from 'fs';
// ...
writeFileSync(this.getConfigLocation(), dump(objectToSave, { skipInvalid: true }));
chmodSync(this.getConfigLocation(), 0o600);File: src/Whitelist/VulnerabilityExcluder.ts
Validate that the user-supplied allowlist/whitelist path resolves to a regular file using path.resolve() and fs.statSync(). Keep it simple.
File: src/Config/Config.ts:7 — safeDump is removed in js-yaml v4.
// Replace:
import { safeDump } from 'js-yaml';
// With (after upgrading js-yaml to ^4.x):
import { dump } from 'js-yaml';Add a new top-level command guide [options] to src/index.ts that mirrors the current ossi option surface but targets the Sonatype Guide API.
CLI interface:
auditjs guide [options]
Audit this application using Sonatype Guide
Options:
--token, -t Specify Sonatype Guide API token [string]
--server, -h Specify Sonatype Guide server url [string]
--quiet, -q Only print out vulnerable dependencies [boolean]
--json, -j Set output to JSON [boolean]
--xml, -x Set output to JUnit XML format [boolean]
--allowlist, -w Set path to allowlist file [string]
--whitelist Deprecated alias for --allowlist [string]
--bower Force explicit scan for Bower [boolean]
--dev, -d Include Development Dependencies [boolean]
--allowlist is the canonical flag. --whitelist is accepted as a deprecated alias and emits a warning directing users to --allowlist. The deprecated ossi command retains --whitelist unchanged.
Thanks to the OSSIndexCompatibilityApi discovery (see §13, Q2), the new surface is much smaller than originally anticipated. The existing formatters and result types are reused unchanged.
| File | Purpose |
|---|---|
src/Services/GuideRequestService.ts |
Thin wrapper over OSSIndexCompatibilityApi; handles chunking + 24hr cache via node-persist |
src/Config/GuideServerConfig.ts |
Config persistence for Guide credentials at ~/.sonatype-guide/ (same structure as OssIndexServerConfig) |
src/Services/GuideRequestService.spec.ts |
Vitest tests |
src/Config/GuideServerConfig.spec.ts |
Vitest tests |
No new Audit class or result types needed — AuditOSSIndex, OssIndexServerResult, and Vulnerability are reused directly by the guide command path. The Guide API response shape is identical.
"@sonatype/sonatype-guide-api-client": "^0.1.0"Auth: HTTP Basic (username + apiToken) via Configuration({ username, password }). Default base: https://api.guide.sonatype.com. The --server flag overrides basePath.
In src/index.ts, wrap the existing ossi command handler to emit a deprecation warning at runtime before delegating:
console.warn(
'DEPRECATION: `auditjs ossi` is deprecated and will be removed in v6. ' +
'Please migrate to `auditjs guide`.'
);The ossi command must still function correctly in v5. Its underlying implementation (OssIndexRequestService, AuditOSSIndex, etc.) is not removed in this release.
The auditjs config interactive setup must present the following options:
- Sonatype Lifecycle — host URL, username, token/password
- Sonatype Guide (Bearer Token) — server URL, long-lived user token (created via Settings > API Tokens in the Guide UI); stored as
accessTokeninGuideServerConfig - Sonatype Guide (OSS Index Compatibility — Username and Token) — server URL, username, API token; stored as
username/passwordand passed as HTTP Basic auth toOSSIndexCompatibilityApi
The deprecated OSS Index config option is retained but prints a deprecation notice directing users to option 3 above.
In src/index.ts, register lifecycle as a yargs command that internally delegates to the same handler as iq. The iq command emits a deprecation warning:
// iq handler — add at top:
console.warn(
'DEPRECATION: `auditjs iq` is deprecated and will be removed in v6. ' +
'Please migrate to `auditjs lifecycle`.'
);The lifecycle command should have an updated description:
Audit this application using Sonatype Lifecycle
Update all occurrences of "Nexus IQ Server" throughout:
README.md— command documentation sections, requirements section, config sectionsrc/index.ts— command descriptionssrc/Application/Application.ts— log messages and spinner text- Any inline comments in
src/Services/IqRequestService.ts
| Package | Current | Action | Notes |
|---|---|---|---|
@sonatype/sonatype-guide-api-client |
— | ADD ^0.1.0 |
New Guide API client |
@xmldom/xmldom |
^0.8.5 |
Update to ^0.9.x |
Check latest |
chalk |
^3.0.0 |
Update to ^4.1.2 |
v5+ is ESM-only; v4 is the last CJS-compatible major |
colors |
1.4.0 |
REMOVE | Protestware incident; chalk already covers colour needs |
figlet |
^1.2.4 |
Update to latest | |
https-proxy-agent |
^7.0.6 |
Keep/update | Already reasonably current |
js-yaml |
3.14.2 |
Update to ^4.x |
safeDump → dump; breaking API change, see §3.5 |
log4js |
^6.4.0 |
Update to latest | |
node-fetch |
^2.6.8 |
REMOVE | Replace with Node 20 native fetch throughout |
node-persist |
^3.1.0 |
Evaluate | Cache mechanism; check if still maintained |
ora |
^4.0.3 |
Update to ^5.4.1 |
v6+ is ESM-only; v5 is the last CJS-compatible major |
read-installed |
~4.0.3 |
Evaluate | Potentially unmaintained; assess alternatives (e.g. @npmcli/arborist) |
spdx-license-ids |
^3.0.5 |
Update to latest | |
ssri |
^10.0.6 |
Keep/update | |
uuid |
^9.0.1 |
Update to latest | |
xmlbuilder |
^13.0.2 |
Replace with @cyclonedx/cyclonedx-library |
See §8 |
yargs |
^16.1.0 |
Update to ^17.x |
Note on
chalk/oraESM: Bothchalk@5+andora@6+are ESM-only. auditjs compiles to CommonJS, so the correct targets arechalk@^4.1.2andora@^5.4.1— the last CJS-compatible majors (confirmed by inspecting theirpackage.json"type"fields). A future ESM build migration would unlock the latest majors.
Note on
read-installed: This is a core dependency for theNpmListmuncher. It reads the installed package tree fromnode_modules. Before removing, validate that a drop-in replacement exists.@npmcli/arboristis the official npm successor. This evaluation should happen early in the implementation phase.
| Package | Current | Action | Notes |
|---|---|---|---|
@types/chai |
4.2.0 |
REMOVE | Replaced by Vitest |
@types/chai-as-promised |
7.1.1 |
REMOVE | Replaced by Vitest |
@types/mocha |
5.2.7 |
REMOVE | Replaced by Vitest |
@types/mock-fs |
^4.10.0 |
Keep or update | Evaluate with Vitest |
@types/node |
^18.0.0 |
Update to ^20.0.0 |
Match engine requirement |
@types/node-fetch |
^2.5.4 |
REMOVE | node-fetch removed |
@types/sinon |
^7.5.1 |
REMOVE | Replaced by Vitest mocks |
@types/ssri |
^7.1.5 |
Update to match ssri version |
|
@typescript-eslint/eslint-plugin |
^6.13.2 |
Update to ^8.x |
Matches ESLint v9 |
@typescript-eslint/parser |
^6.13.2 |
Update to ^8.x |
|
chai |
4.2.0 |
REMOVE | Replaced by Vitest |
chai-as-promised |
7.1.1 |
REMOVE | Replaced by Vitest |
eslint |
^7.1.0 |
Update to ^9.x |
Flat config format (eslint.config.js) |
eslint-config-prettier |
^6.10.0 |
Update to ^10.x |
|
eslint-plugin-prettier |
^3.1.2 |
Update to ^5.x |
|
mocha |
^8.3.1 |
REMOVE | Replaced by Vitest |
mocha-junit-reporter |
^1.23.3 |
REMOVE | Replaced by Vitest JUnit reporter |
nock |
11.7.0 |
REMOVE | Replaced by msw or Vitest vi.spyOn(globalThis, 'fetch') |
nyc |
^15.0.0 |
REMOVE | Replaced by @vitest/coverage-v8 |
prettier |
^1.19.1 |
Update to ^3.x |
|
rimraf |
^4.0.0 |
Update to ^6.x or use node --rm |
|
sinon |
^8.0.2 |
REMOVE | Replaced by Vitest |
ts-node |
^8.5.4 |
REMOVE | No longer needed with Vitest |
typescript |
^5.3.3 |
Update to ^5.4+ |
|
vitest |
— | ADD ^2.x |
Test runner |
@vitest/coverage-v8 |
— | ADD | Coverage |
msw |
— | ADD ^2.x |
HTTP mocking for Vitest (replaces nock) |
"engines": {
"node": ">=20.0.0"
},
"scripts": {
"test": "vitest run",
"test:watch": "vitest",
"test-ci": "vitest run --reporter=junit --outputFile=reports/test-results.xml",
"coverage": "vitest run --coverage",
"build": "tsc -p tsconfig.json",
"lint": "eslint src/**/*.ts",
"start": "node ./bin/index.js",
"prepare": "yarn build",
"prepublishOnly": "yarn test"
}Remove the iq-scan npm script (references npx auditjs@latest iq).
Vitest is TypeScript-native (no ts-node shim required), has a Jest-compatible API, uses native ES modules, provides built-in coverage via V8, and supports concurrent test execution. It eliminates the mocha + sinon + nock + ts-node + nyc stack with a single dependency.
Vitest requires "module": "esnext" or "moduleResolution": "bundler" in the tsconfig used for tests. Create a tsconfig.test.json that extends the main config:
{
"extends": "./tsconfig.json",
"compilerOptions": {
"module": "esnext",
"moduleResolution": "bundler"
}
}The production tsconfig.json keeps "module": "commonjs" for the CLI output.
Update tsconfig.json compiler target while here:
"target": "es2020",
"lib": ["es2020"]import { defineConfig } from 'vitest/config';
export default defineConfig({
test: {
globals: true,
environment: 'node',
coverage: {
provider: 'v8',
reporter: ['text', 'lcov'],
reportsDirectory: './coverage',
thresholds: {
lines: 70,
functions: 70,
},
},
},
});Each *.spec.ts file needs migration. The changes are mechanical:
| Before | After |
|---|---|
import { expect } from 'chai' |
import { expect, vi, describe, it, beforeEach } from 'vitest' |
import sinon from 'sinon' |
Use vi.fn(), vi.spyOn() |
import nock from 'nock' |
Use msw handlers or vi.spyOn(globalThis, 'fetch') |
import mock from 'mock-fs' |
Evaluate — mock-fs may still work under Vitest |
chai-as-promised async assertions |
Vitest native async support (await expect(promise).rejects.toThrow()) |
sinon.stub(obj, 'method') |
vi.spyOn(obj, 'method') |
stub.returns(val) |
.mockReturnValue(val) |
stub.resolves(val) |
.mockResolvedValue(val) |
Affected spec files:
src/Application/Application.spec.tssrc/Audit/AuditIQServer.spec.tssrc/Audit/AuditOSSIndex.spec.tssrc/Config/IqServerConfig.spec.tssrc/Config/OssIndexServerConfig.spec.tssrc/CycloneDX/CycloneDXSbomCreator.spec.tssrc/Services/IqRequestService.spec.tssrc/Services/OssIndexRequestService.spec.tssrc/Services/RequestHelpers.spec.tssrc/Whitelist/VulnerabilityExcluder.spec.ts
Plus new spec files for Guide components (§4.2).
Replace the coverage upload steps in CI workflows:
- name: Run tests with coverage
run: yarn coverage
- name: Upload coverage
if: always()
uses: actions/upload-artifact@v4
with:
name: coverage
path: coverage/lcov.infoIssue #273 (fix coverage targets in CI) is resolved by this migration.
There are two distinct reasons SBOM generation exists in auditjs, and they serve different purposes:
1. Sonatype Lifecycle (iq/lifecycle) integration — internal, required
The Third-Party Scan REST API that Sonatype Lifecycle exposes expects a CycloneDX XML payload. Submitting a SBOM is not optional — it is the entire wire protocol for the iq command. CycloneDXSbomCreator generates this payload internally via NpmList.getSbomFromCommand(), which Application.populateCoordinatesForIQ() calls before POSTing to IQ Server. Without this, Lifecycle integration does not work.
2. auditjs sbom subcommand — user-facing, convenience
The sbom sub-command (nested under ossi in the CLI) pipes the same CycloneDX SBOM to stdout. This lets users export their dependency bill of materials for use in other tools (e.g., uploading to a different SCA scanner, archiving for compliance). It is entirely optional but a useful standalone feature.
The OSS Index / Guide path does not use SBOM at all — it extracts PURLs directly from node_modules and POSTs them to the batch component-report endpoint.
- The hand-rolled implementation targets CycloneDX BOM spec 1.1 (released ~2019). The current spec is 1.6. The SBOM submitted to Lifecycle is therefore outdated.
xmlbuilderis used for XML serialisation — a dev tool not designed for spec-compliant SBOM generation.read-installedis used to traversenode_modules— same unmaintained dependency noted in §6.1.- The
NpmShrinkwrapmuncher class (src/Munchers/NpmShrinkwrap.ts) is a stub — all methods throw'Method not implemented.'. It should either be implemented or removed.
Replace src/CycloneDX/CycloneDXSbomCreator.ts with a wrapper over @cyclonedx/cyclonedx-library. This library is the official, actively-maintained CycloneDX TypeScript implementation that tracks the current spec, handles both XML and JSON serialisation, and is already used by the official @cyclonedx/cyclonedx-npm CLI.
Changes:
- Add
@cyclonedx/cyclonedx-libraryas a production dependency - Refactor
CycloneDXSbomCreatorto use the library's model + serialiser - Remove
xmlbuilderfrom production dependencies - Remove
src/Munchers/NpmShrinkwrap.ts(dead stub) — or implement it properly as a follow-up (#289 scope) - Update SBOM schema version in Lifecycle submission from 1.1 to the library's current default
- The
auditjs sbomsubcommand output improves automatically as a side-effect
Reference: https://contribute.sonatype.com/docs/
Note on
SECURITY.mdandCODE_OF_CONDUCT.md: Neither file is required at the individual repository level. Both are provided at the organisation level viasonatype-nexus-community/.github, which GitHub surfaces automatically for all repos in the org that do not override them. Creating copies here would shadow the org defaults. The published Sonatype Community Standards documentation should be updated to make this org-level inheritance explicit.
All documentation files receive updates in this modernisation — no documentation is treated as out of scope. This includes README.md, CONTRIBUTING.md, CHANGELOG.md, all inline comments affected by renamed commands or deprecated APIs, and CI workflow comments.
Create MIGRATION.md at the repo root documenting the upgrade path from v4 to v5:
- Summary of breaking changes
auditjs ossitoauditjs guide(credentials, config file location)auditjs iqtoauditjs lifecycle(command rename only, no config change)--whitelistto--allowliston theguidecommand- Node.js version requirement change (>=16 to >=20)
- npm replacing yarn for development workflow
Add a "Upgrading from v4" section to README.md that links to MIGRATION.md.
- Remove Gitter references — Gitter is defunct. Replace with the Sonatype Community Forum link (
community.sonatype.com) or GitHub Discussions if enabled. - Remove CircleCI references — CI is now GitHub Actions. Update PR instructions accordingly.
- Remove the CLA URL — The CLA is handled by an interactive GitHub bot; no URL is needed in CONTRIBUTING.md
- Update lint instruction — Replace the manual "Spaces (not tabs)" note with
npm run lint(enforced by Prettier + ESLint)
- Remove all references to "Nexus IQ Server"; replace with "Sonatype Lifecycle"
- Add
auditjs guidecommand to the primary usage section; markauditjs ossias deprecated - Remove hardcoded default credential references in the IQ usage section (see §3.1)
- Update Node.js requirement from "8.x forward" to ">=20"
- Add SonarCloud quality badge — once the SonarCloud project is configured, add:
[](https://sonarcloud.io/summary/new_code?id=<KEY>). Do not add a SECURITY.md badge. - Replace all
yarncommand examples withnpm runequivalents - Update CI section: remove Travis CI / CircleCI references; replace with GitHub Actions examples
- Add "Upgrading from v4" section linking to
MIGRATION.md
Confirm the release workflow matches the current semantic-release version installed globally (@23). Pin the version in the release step rather than relying on a global install:
- name: Install publishing packages
run: npm install -g semantic-release@23 @semantic-release/changelog@6 @semantic-release/git@10 @semantic-release/npm@10
- name: Attempt publish
run: npx semantic-releaseauditjs currently uses Yarn as its package manager (yarn.lock, yarn in all scripts and CI). There is no yarn-specific functionality in the source code — it is used purely for dependency management. Migrating to standard npm removes a toolchain dependency and aligns with the broader npm ecosystem.
| Item | Current | After |
|---|---|---|
| Lock file | yarn.lock |
package-lock.json |
| Install command | yarn install |
npm ci (CI) / npm install (dev) |
| Run script | yarn <script> |
npm run <script> |
| Global install | yarn global add |
npm install -g |
| Dependency resolutions | "resolutions": {} (yarn-only) |
"overrides": {} (npm >=8.3) |
| Prod-only install | yarn install --prod |
npm ci --omit=dev |
- Replace the
"resolutions"block with an"overrides"block (same key-value pairs; npm syntax is identical for most entries) - Remove
@yarnpkg/lockfileand@types/yarnpkg__lockfilefromdevDependencies— these are Yarn internals with no usage in the source code - Update the
iq-scanscript reference fromnpx auditjs@latestto keep as-is (no yarn dependency)
# setup-node cache:
cache: 'npm' # was 'yarn'
# install:
run: npm ci # was 'yarn install'
# scripts:
run: npm run lint # was 'yarn lint'
run: npm run build # was 'yarn build'
run: npm run test-ci # was 'yarn test-ci'
# release workflow only:
run: npm install -g semantic-release@23 ... # was 'yarn global add'
run: npx semantic-release # was 'yarn exec semantic-release'
# Lifecycle scan (prod-only install):
run: npm ci --omit=dev # was 'yarn install --prod'Replace all yarn command examples with npm run equivalents. The README already documents npm install -g auditjs for installation — only the developer workflow examples need updating.
- Delete
yarn.lock - Run
npm installto generatepackage-lock.json - Commit
package-lock.json, removeyarn.lock - Update
.gitignoreif it explicitly excludespackage-lock.json
tsconfig.json has "strict": true, which enables the full strict family of checks. However, one setting explicitly relaxes it:
"useUnknownInCatchVariables": falseThis suppresses the TypeScript 4.4+ behaviour where catch (e) variables have type unknown rather than any. The codebase bypasses this by accessing e.message and e.stack directly without type narrowing throughout Application.ts.
Additionally, while strict: true is declared, the source contains widespread : any usage that the compiler tolerates but which undermines the intent.
Enable useUnknownInCatchVariables:
Remove the explicit false override in tsconfig.json. This is the default under strict: true and is the correct behaviour. All catch blocks that access e.message / e.stack must be updated:
// Pattern to replace throughout Application.ts and others:
} catch (e) {
logMessage('...', ERROR, { title: e.message, stack: e.stack });
}
// Replace with:
} catch (e) {
const err = e instanceof Error ? e : new Error(String(e));
logMessage('...', ERROR, { title: err.message, stack: err.stack });
}Eliminate : any in non-boundary code:
The any usages fall into three categories:
-
Legitimate boundary code (external data from
read-installed, yargs argv, JSON responses) — use typed interfaces orunknownwith a type guard/assertion at the boundary. Key files:CycloneDXSbomCreator.ts,NpmList.ts,IqServerResult.ts,OssIndexServerResult.ts. -
args: anyinApplication.ts— replace with the yargs inferredArgumentstype or a typed interface representing the parsed CLI arguments. -
NpmShrinkwrap.tsstub — thePromise<any>return types are a consequence of it being unimplemented. Remove the file (see §8.3) or implement it properly.
Add noImplicitOverride to tsconfig.json:
"noImplicitOverride": trueAny method in a subclass that overrides a base class method must use the override keyword. This catches accidental overrides early and improves class hierarchy clarity.
Update .eslintrc.js (migrating to eslint.config.js flat format for ESLint v9) to add:
'@typescript-eslint/no-explicit-any': 'warn',
'@typescript-eslint/no-unsafe-member-access': 'warn',
'@typescript-eslint/no-unsafe-call': 'warn',These rules will surface remaining any drift during development rather than requiring a one-time audit.
Currently only CLI flags and the config file are supported. Add environment variable fallbacks:
| Env var | Used for |
|---|---|
AUDITJS_GUIDE_TOKEN |
Sonatype Guide API token |
AUDITJS_LIFECYCLE_URL |
Sonatype Lifecycle host |
AUDITJS_LIFECYCLE_USER |
Sonatype Lifecycle username |
AUDITJS_LIFECYCLE_TOKEN |
Sonatype Lifecycle token |
Priority: CLI flag > env var > config file.
| Issue | Reason deferred |
|---|---|
#289 — package-lock.json scanning |
Requires evaluating @npmcli/arborist; significant muncher rewrite |
#202 — Rename --whitelist → --allowlist on ossi |
ossi is deprecated; the new guide command already uses --allowlist |
| #269 / #215 — Monorepo / workspace support | Large scope; separate feature work |
| #263 — CycloneDX library (full) | Partially in scope (§8); complete rework deferred |
#279 — /platform in IQ report URL |
Needs further investigation before implementation |
| #258 — Time-limited whitelist entries | Deferred |
| #255 — JSONC whitelist support | Deferred |
| #261 — Filter vulnerabilities by score | Deferred |
The following sequence minimises risk by fixing CI first, then doing isolated changes before the large feature work.
CI workflow action versions were fixed manually prior to this sequence. Begin here.
- Delete
yarn.lock; runnpm installto generatepackage-lock.json - Replace
"resolutions"with"overrides"inpackage.json - Remove
@yarnpkg/lockfileand@types/yarnpkg__lockfilefrom devDependencies - Update all CI workflow scripts (
yarn→npm run, cache, prod install) - Update README and CONTRIBUTING dev workflow examples
- Remediate issue #287 items (§3):
--insecurewarning + Lifecycle-only restriction, config file chmod, allowlist path validation - Post comment on issue #287 clarifying item 1 (hardcoded credentials) is not a valid security finding
- Fix
js-yamlsafeDump→dump(required before updating js-yaml to v4) - Remove
colorspackage; replace uses withchalk - Remove
node-fetch; replace with nativefetch(requires Node >=20 engine bump) - Bump
engines.nodeto>=20.0.0
- Add
vitest,@vitest/coverage-v8,msw; createvitest.config.tsandtsconfig.test.json - Rewrite all existing
*.spec.tsfiles for Vitest - Remove
mocha,sinon,nock,nyc,ts-node,chai,chai-as-promisedand their@types - Update CI test and coverage steps
- Verify coverage gates pass (resolves issue #273)
- Update remaining production deps (js-yaml v4, chalk v4, ora v5, yargs v17, log4js, etc.)
- Update remaining dev deps (typescript latest, eslint v9 flat config, prettier v3, @types/node v20)
- Remove
NpmShrinkwrap.tsstub; removexmlbuilder; add@cyclonedx/cyclonedx-library - Refactor
CycloneDXSbomCreatorto use@cyclonedx/cyclonedx-library(resolves #263) - Update
package.jsonoverridesas needed post-install audit
- Remove
"useUnknownInCatchVariables": falsefromtsconfig.json; add"noImplicitOverride": true - Fix all catch blocks to type-narrow
unknownerror variables - Replace
: anywith typed interfaces orunknown+ type guards at data boundaries - Type
argsinApplication.tsusing yargsArgumentstype - Update to
eslint.config.js(ESLint v9 flat config) withno-explicit-anywarnings - Run full lint + build; resolve all new errors
- Implement
GuideRequestService,GuideServerConfig; add Vitest specs - Add
guidecommand tosrc/index.ts; add deprecation warning toossi - Update
configcommand to include all three options (§4.5) - Update CI dogfood scan to use
auditjs guide
- Add
lifecyclecommand alias; add deprecation warning toiq - Rename "Nexus IQ" → "Sonatype Lifecycle" in all log messages, spinner text, comments
- Create
MIGRATION.md(v4 to v5 guide; link from README) - Update
CONTRIBUTING.md(remove Gitter, CircleCI, CLA URL; update lint tonpm run lint) - Full
README.mdupdate (commands, deprecations, npm, Node version, SonarCloud badge, link to MIGRATION.md) - Update
CHANGELOG.mdheader for v5.0.0
- Issue #228 — Environment variable credential support
- Run full test suite; verify zero failures
- Run Sonatype Lifecycle scan; verify zero policy violations
- Create release PR; pass CI; trigger
workflow_dispatchrelease workflow
All five open questions from the initial draft have been answered by inspecting the published @sonatype/sonatype-guide-api-client@0.1.0 package and the OpenAPI spec at sonatype-nexus-community/sonatype-guide-api-client.
Answer: HTTP Basic Authentication using username:apiToken (base64-encoded), identical to OSS Index. The Configuration object accepts username and password fields. There is also a Bearer token (user-token) security scheme for UI-issued long-lived tokens.
Impact on design: GuideServerConfig should store Username and Token fields (same shape as OssIndexServerConfig). CLI flags remain --user / --password. Prefer the term "API Token" in help text to distinguish from a login password.
The Sonatype Guide API client ships an OSSIndexCompatibilityApi that mirrors the OSS Index batch endpoint exactly:
| Aspect | OSS Index (OssIndexRequestService) |
Guide OSSIndexCompatibilityApi |
|---|---|---|
| Batch endpoint | POST /api/v3/component-report |
POST /api/v3/authorized/component-report |
| Request body | { coordinates: string[] } |
PurlRequestPost { coordinates: string[] } |
| Max batch size | 128 | 128 |
| Response shape | { coordinates, description, reference, vulnerabilities[] } |
ComponentReportPost (identical fields) |
| Vulnerability shape | { id, title, description, cvssScore, cvssVector, cve, reference } |
OssiVulnerabilityPost (identical fields, adds displayName, cwe, externalReferences) |
| Base URL | https://ossindex.sonatype.org/ |
https://api.guide.sonatype.com |
| Auth | Basic (user + token) | Basic (user + apiToken) |
Impact on design: GuideRequestService is not a large new service — it is a thin wrapper over OSSIndexCompatibilityApi. The existing AuditOSSIndex formatter, OssIndexServerResult type, and Vulnerability type can be reused with no changes (or minor extension for the new displayName/cwe/externalReferences fields). The OSS Index → Guide migration is primarily a configuration and URL swap, not a full rewrite.
The ossi deprecation path therefore becomes: ossi continues using OssIndexRequestService → ossindex.sonatype.org; guide uses GuideRequestService (wrapping OSSIndexCompatibilityApi) → api.guide.sonatype.com. Both share the same formatters and result types.
Answer: read-installed@4.0.3 was last published over a year ago and is considered unmaintained. @npmcli/arborist@9.5.0 is the official npm successor (222 versions, actively maintained by the npm team).
Impact on plan: The read-installed → @npmcli/arborist migration is a non-trivial rewrite of NpmList.ts (the primary muncher). Since lock-file scanning (#289) is also deferred to v5.x, this replacement is deferred to v5.1 to avoid scope creep. For v5.0, read-installed stays as-is. Add a tracking note in the README known limitations.
Answer: Both chalk@5.x and ora@9.x (latest) carry "type": "module" and are ESM-only. Since auditjs compiles to CommonJS ("module":"commonjs" in tsconfig), the last CJS-compatible majors must be used:
chalk: use^4.1.2(latest v4, full feature parity, no ESM requirement)ora: use^5.4.1(latest v5, last CJS release)
A future ESM build target migration would unlock v5/v9 respectively, but that is out of scope for v5.0.
Answer: Sonatype Guide uses a credit-based model (not time-windowed rate limits). Credits are consumed per component found. Same-day requests for the same component are not re-charged.
Impact on design: The existing node-persist 12-hour cache in OssIndexRequestService is still valuable and should be replicated in GuideRequestService to avoid unnecessary credit consumption. Consider a 24-hour TTL (aligns better with the same-day credit policy). Cache location: ~/.sonatype-guide/auditjs (distinct from the OSS Index cache at ~/.ossindex/auditjs).
Answer: Yes. The Configuration constructor accepts basePath, which overrides the default https://api.guide.sonatype.com. The --server flag maps directly to basePath in GuideRequestService. Include it in the guide command options as --server / -h (same pattern as ossi).