Skip to content

Add audit command to scan packages for security advisories#19

Open
exo-nikita wants to merge 2 commits into
masterfrom
claude/implement-audit-command-BnwJz
Open

Add audit command to scan packages for security advisories#19
exo-nikita wants to merge 2 commits into
masterfrom
claude/implement-audit-command-BnwJz

Conversation

@exo-nikita
Copy link
Copy Markdown
Collaborator

Summary

This PR adds a new audit command to the stasis CLI that scans packages from lockfiles and bundles for security vulnerabilities using the npm advisories API.

Key Changes

  • New audit module (src/audit.js): Core functionality for auditing packages

    • collectPackagesFromFile(): Extracts package name/version from stasis lockfiles or brotli-compressed bundles
    • collectPackages(): Aggregates packages from multiple files with deduplication
    • flattenAdvisories(): Transforms advisory results into sortable rows, ordered by severity then package name
    • formatTable(): Generates aligned tabular output for advisory reports
    • audit(): Main function that collects packages and queries npm advisories API
    • printAuditReport(): Formats and outputs audit results to stdout/stderr
  • CLI integration (bin/stasis.js): Added stasis audit command

    • Accepts one or more file paths (lockfiles or bundles)
    • Exits with code 0 if no vulnerabilities found, 1 if advisories exist
    • Provides helpful error messages for missing files or invalid formats
  • Comprehensive test suite (tests/audit.test.js): 173 lines of tests covering

    • Package extraction from lockfiles and bundles
    • Deduplication logic (exact matches and cross-file duplicates)
    • Advisory sorting and formatting
    • CLI integration and error handling

Implementation Details

  • Supports both JSON lockfiles and brotli-compressed bundles transparently
  • Deduplicates packages by name+version across multiple input files
  • Sorts advisories by severity (critical → info) then by package name for consistent output
  • Cleans environment variables in CLI tests to avoid interference from stasis configuration

https://claude.ai/code/session_012AEjpn3Kc2i8WfHw5ZgPEv

claude added 2 commits May 16, 2026 06:57
Collects package name/version pairs from stasis lockfiles and brotli
bundles, deduplicates them, queries the npm bulk advisories endpoint,
and prints results as a table sorted by severity.
Copy link
Copy Markdown

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

Adds a new stasis audit CLI command intended to collect package versions from stasis artifacts and query npm security advisories.

Changes:

  • Added src/audit.js for package extraction, advisory flattening, table formatting, and reporting.
  • Added stasis audit command wiring in the CLI.
  • Added audit-focused tests for package collection, formatting, and basic CLI errors.

Reviewed changes

Copilot reviewed 2 out of 3 changed files in this pull request and generated 7 comments.

File Description
bin/stasis.js Adds usage text and command dispatch for audit.
src/audit.js Implements package collection, npm advisory lookup, and audit report formatting.
tests/audit.test.js Adds tests for audit helpers and basic CLI error handling.
Comments suppressed due to low confidence (2)

src/audit.js:30

  • stasis bundles generated by State.sourceData contain loaded source files, formats, and imports, but they do not persist package metadata or package.json files unless those package.json files were themselves imported. As a result, auditing a normal generated bundle will collect zero packages and falsely print "No advisories found", which defeats the bundle-audit path.
  for (const [path, source] of Object.entries(json.sources)) {
    if (!isPackageJsonPath(path)) continue
    const { name, version } = JSON.parse(source)
    if (name && version) out.push({ name, version })

src/audit.js:36

  • The lockfile shape check is loose enough to accept malformed objects such as { version: 0, sources: {} } that do not contain the required modules section, leading to a successful audit with zero packages. Since generated lockfiles always include modules, this should reject incomplete lockfiles instead of treating them as clean.
function isLockfileShape(json) {
  return Boolean(json && (json.modules || (json.sources && Object.values(json.sources).every((v) => v && typeof v === 'object' && 'name' in v))))

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

Comment thread tests/audit.test.js
Comment on lines +50 to +53
sources: {
'src/entry.js': "export const x = 1\n",
'node_modules/foo/package.json': JSON.stringify({ name: 'foo', version: '2.0.0' }),
'node_modules/baz/package.json': JSON.stringify({ name: 'baz', version: '0.0.1' }),
Comment thread src/audit.js
function extractFromBundle(json) {
assert.equal(json.version, 0, 'unsupported stasis bundle version')
const out = []
if (!json.sources) return out
Comment thread src/audit.js
Comment on lines +111 to +116
export async function audit(files) {
const packages = collectPackages(files)
if (packages.length === 0) {
return { packages, advisories: {}, rows: [] }
}
const result = await advisories(packages)
Comment thread src/audit.js
if (packages.length === 0) {
return { packages, advisories: {}, rows: [] }
}
const result = await advisories(packages)
Comment thread src/audit.js
if (packages.length === 0) {
return { packages, advisories: {}, rows: [] }
}
const result = await advisories(packages)
Comment thread src/audit.js
Comment on lines +12 to +18
for (const section of [json.sources, json.modules]) {
if (!section) continue
assert.equal(typeof section, 'object')
for (const entry of Object.values(section)) {
const { name, version } = entry
if (name && version) out.push({ name, version })
}
Comment thread src/audit.js
Comment on lines +77 to +81
rows.push({
package: pkg,
vulnerable: adv.vulnerable_versions ?? '',
severity: adv.severity ?? '',
title: adv.title ?? '',
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants