Skip to content

feat: add --debug flag for verbose network and runtime logging#471

Open
coder-Yash886 wants to merge 8 commits into
OWASP:mainfrom
coder-Yash886:feature/issue-393-debug-flag
Open

feat: add --debug flag for verbose network and runtime logging#471
coder-Yash886 wants to merge 8 commits into
OWASP:mainfrom
coder-Yash886:feature/issue-393-debug-flag

Conversation

@coder-Yash886
Copy link
Copy Markdown
Contributor

Closes #393

Summary

Adds a --debug flag to cve-lite for maintainer-focused diagnostics
without affecting normal CLI output behavior.

When --debug is enabled, the CLI emits verbose runtime/network logs
to stderr, including:

  • Lockfile/source parsing details and package counts
  • Cache hit/miss decisions for package queries
  • OSV request/response lifecycle (URL, method, status, timing, payload metadata)
  • Caught fetch/runtime errors with full stack traces

Debug output is routed to stderr so JSON/SARIF/CDX output streams
remain clean and machine-readable on stdout.

Motivation

Intermittent or machine-specific OSV/network failures are currently
invisible from CLI output alone. This flag gives maintainers a
structured trace to identify exactly where in the request lifecycle
a failure occurred, without impacting normal users.

Changes

  • src/output/debug.ts — reusable debug logger (writes to stderr)
  • src/cli/args.ts--debug flag parsing
  • src/cli/help.ts — help text for --debug
  • src/types.ts — debug option type definition
  • src/scanner.ts — debug logging for lockfile parsing and cache decisions
  • src/advisory/osv-advisory-source.ts — debug logging for OSV requests/responses
  • src/index.ts — wire debug flag through runtime

Test Plan

  • npm test -- tests/helpers.test.ts tests/output.test.ts
  • npm test -- tests/cli-integration.test.ts
  • Full suite: 283/283 passed

No behavior changes for users who do not pass --debug.

@coder-Yash886
Copy link
Copy Markdown
Contributor Author

@sonukapoor please review the PR when you have free time

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

Adds a maintainer-focused --debug flag to emit verbose runtime/network diagnostics to stderr while keeping normal CLI output (including JSON/SARIF/CDX) clean on stdout.

Changes:

  • Introduces a reusable debug logger (createDebugLogger) and wires --debug through CLI args/options and the main runtime.
  • Adds debug tracing for cache hit/miss decisions and OSV request/response/error lifecycle.
  • Adds/updates Jest coverage to verify debug logging behavior and arg parsing.

Reviewed changes

Copilot reviewed 9 out of 9 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
tests/output.test.ts Adds a test ensuring debug logs write to stderr only when enabled.
tests/helpers.test.ts Extends parseArgs tests to assert --debug parsing.
src/types.ts Adds debug?: boolean to parsed CLI options typing.
src/scanner.ts Creates debug logger per scan and logs cache hit/miss; passes logger into advisory source.
src/output/debug.ts New debug logger utility that writes formatted debug lines to console.error.
src/index.ts Wires --debug into runtime logging (package parsing summary + stack trace on fatal errors).
src/cli/help.ts Documents --debug (and additionally updates help text for other commands/options).
src/cli/args.ts Adds --debug flag parsing.
src/advisory/osv-advisory-source.ts Adds debug logging around OSV requests/responses/errors (including timing).

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

Comment thread src/cli/help.ts
Comment thread src/cli/help.ts
Comment thread src/cli/help.ts
Comment thread src/advisory/osv-advisory-source.ts Outdated
@coder-Yash886 coder-Yash886 force-pushed the feature/issue-393-debug-flag branch from 5807734 to ef2b1e4 Compare May 27, 2026 19:31
Copy link
Copy Markdown
Collaborator

@sonukapoor sonukapoor left a comment

Choose a reason for hiding this comment

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

See below comments.

Comment thread src/cli/help.ts Outdated
Comment thread src/index.ts Outdated
@coder-Yash886 coder-Yash886 force-pushed the feature/issue-393-debug-flag branch from 1e609d6 to ec46ef1 Compare May 28, 2026 01:16
@coder-Yash886
Copy link
Copy Markdown
Contributor Author

@sonukapoor please review the PR when you have free time

Copy link
Copy Markdown
Collaborator

@sonukapoor sonukapoor left a comment

Choose a reason for hiding this comment

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

Both issues from the last round are fixed — duplicate --ca-cert is gone and pluralize is used correctly. The implementation is solid: typed DebugLogger, no-op for disabled mode, comprehensive OSV request/response/error coverage, and good test coverage. Two small things before this merges.

Comment thread src/scanner.ts Outdated
Comment thread src/scanner.ts
@sonukapoor
Copy link
Copy Markdown
Collaborator

Great start — the DebugLogger abstraction is clean and the OSV request/response entries are exactly what you want for network diagnosis. Before we merge, here are some extensions that would make the feature genuinely useful in production.

Output should go to a file, not stderr

Right now --debug writes to stderr, which means the output gets mixed in with the normal scan output and is hard to redirect cleanly. Instead, debug output should be written to a timestamped log file in the current directory:

./cve-lite-debug-2026-05-27T14-30-00.log

When --debug is passed, print one line to stderr so the user knows where to look:

[debug] Writing debug log to ./cve-lite-debug-2026-05-27T14-30-00.log

Everything else goes into the file only. This keeps the terminal output clean and gives you a persistent artefact you can share when reporting an issue.

What's currently missing

The debug output has two other problems today:

  1. Per-package cache lines are extremely noisy (1,600+ lines for a mid-sized project) and crowd out the entries that actually matter
  2. Several important code paths are completely dark — network failures in fix command generation, subprocess execution in --fix mode, and the npm transitive graph build outcome are all invisible

Proposed log format

Every entry in the file should be a single JSON line with a timestamp prefix:

2026-05-27T14:30:00.012Z [debug] CLI started {"version":"1.18.1","args":["examples/nest","--debug","--no-cache"]}
2026-05-27T14:30:00.013Z [debug] Config loaded {"caCert":null}
2026-05-27T14:30:00.014Z [debug] Advisory source {"mode":"online","url":"https://api.osv.dev"}
2026-05-27T14:30:00.015Z [debug] Cache loaded {"path":"~/.cache/cve-lite/osv-vulns.json","queryEntries":11101,"detailEntries":260}
2026-05-27T14:30:00.021Z [debug] Lockfile selected {"source":"package-lock","path":"examples/nest/package-lock.json"}
2026-05-27T14:30:00.089Z [debug] Packages parsed {"count":1623,"source":"package-lock"}
2026-05-27T14:30:00.090Z [debug] Cache check {"hits":0,"misses":1623,"reason":"no-cache mode","uncachedBatches":17}
2026-05-27T14:30:00.091Z [debug] npm transitive graph {"status":"built","nodes":1623}
2026-05-27T14:30:00.092Z [debug] Workspace map {"type":"npm","workspaces":0}
2026-05-27T14:30:00.093Z [debug] OSV request  {"batchId":"b-01","queryCount":100,"sample":["@apollo/cache-control-types@1.0.3","@apollo/server@5.5.0",...]}
2026-05-27T14:30:00.094Z [debug] OSV request  {"batchId":"b-02","queryCount":100,"sample":["@fastify/forwarded@3.0.0","@fastify/middie@9.3.1",...]}
2026-05-27T14:30:00.095Z [debug] OSV request  {"batchId":"b-03","queryCount":100,"sample":["@nodelib/fs.scandir@2.1.5",...]}
2026-05-27T14:30:00.489Z [debug] OSV response {"batchId":"b-03","status":200,"durationMs":394}
2026-05-27T14:30:00.581Z [debug] OSV response {"batchId":"b-01","status":200,"durationMs":488}
2026-05-27T14:30:00.582Z [debug] OSV response {"batchId":"b-02","status":200,"durationMs":488}
2026-05-27T14:30:01.843Z [debug] OSV scan complete {"totalBatches":17,"totalDurationMs":1831,"packagesWithVulns":42}
2026-05-27T14:30:01.850Z [debug] Findings classified {"total":42,"direct":10,"transitive":32,"critical":3,"high":11,"medium":25,"low":3}
2026-05-27T14:30:01.851Z [debug] Registry fetch {"batchId":"r-01","package":"@fastify/middie"}
2026-05-27T14:30:02.103Z [debug] Registry response {"batchId":"r-01","status":200,"durationMs":252,"versionsCount":48}
2026-05-27T14:30:02.104Z [debug] Fix validation {"package":"@fastify/middie","installed":"9.3.1","resolvedFixVersion":"9.3.2","verified":true}
2026-05-27T14:30:02.890Z [debug] Parent upgrade resolution {"package":"form-data","version":"2.3.3","result":"upgrade-parent","parent":"coveralls","targetVersion":"3.1.0"}
2026-05-27T14:30:03.201Z [debug] Scan finished {"totalDurationMs":3189,"findings":42}

Why batchId?

CVE Lite sends up to 5 OSV requests concurrently via runWithConcurrency. This means request and response log entries for different batches will naturally interleave in the file — you might see request b-01, b-02, b-03 fire in quick succession, then responses arrive in a different order depending on which batch the OSV API answered first.

Without a correlation ID, a failed or hanging batch is impossible to diagnose — you can see a request was sent but you can't tell which packages were in it. With batchId, you grep for b-03 and immediately get both the request (which packages were included) and the response (status code, duration, or error message).

The same pattern applies to registry calls in fix command generation — each fetchPackument call gets its own batchId (prefixed r-) so a 429 or timeout can be traced back to the specific package that triggered it.

A simple counter is enough — no need for UUIDs. Just increment an integer per session and format it as b-01, b-02, etc.

Performance — debug mode must be a true no-op when disabled

This is important. Every new log call added to hot paths like fetchPackument, the cache check loop, and runWithConcurrency must be completely skipped when --debug is not passed. The current createDebugLogger(false) already returns () => {}, so the call overhead is minimal — but be careful with anything computed before the call:

// Wrong — JSON.stringify runs on every scan regardless of debug mode
debugLog('OSV request', JSON.stringify(payload));

// Right — the object literal is only evaluated if debugLog is not a no-op
debugLog('OSV request', { batchId, queryCount, sample });

Similarly, opening the log file, incrementing the batchId counter, and any Date.now() calls for timing should all be gated behind if (enabled) inside debug.ts. A normal scan with no --debug flag should have zero file I/O and zero string formatting overhead from this feature.

Files that need changes

src/output/debug.ts - Switch from console.error to writing to a timestamped file. Print the file path to stderr once on startup.

src/scanner.ts - Replace per-package cache hit/miss lines with a single Cache check summary. Add: npm transitive graph build outcome (currently catch { return null } with no log), workspace map outcomes, findings classification summary, and scan finished summary.

src/advisory/osv-advisory-source.ts - Add batchId to existing request/response entries.

src/remediation/npm-registry.ts - This file currently has no debug logging at all. fetchPackument has a catch {} that silently returns null — if the npm registry is unreachable or returns 5xx, fix commands silently fail to generate with no indication in the log. Thread debugLog through fetchPackument, resolvePublishedFixVersion, and resolveLowestKnownNonVulnerableVersion and log each registry call and its outcome.

src/utils/fix-runner.ts - runInstallCommand discards all stdout/stderr. In debug mode, log the exact command being run and its exit code. The debugLog isn't currently passed to applyFixesIfRequested at all — thread it through from index.ts.

src/osv/cache.ts - Log cache file path and entry counts on load. Log when the cache falls back to empty due to a parse error (currently catch { return createEmptyCache() } is completely silent).

src/index.ts - Log when NODE_EXTRA_CA_CERTS is set (the CA cert path). This is the first thing to check when diagnosing SSL proxy failures and it is currently invisible.


Happy to discuss any of this — the goal is a debug log that fits on one screen for a typical scan and gives you a clear timeline of exactly where time was spent and where something went wrong.

@coder-Yash886
Copy link
Copy Markdown
Contributor Author

@sonukapoor please review the PR when you have free time

@sonukapoor
Copy link
Copy Markdown
Collaborator

@sonukapoor please review the PR when you have free time

I will review this later today.

Copy link
Copy Markdown
Collaborator

@sonukapoor sonukapoor left a comment

Choose a reason for hiding this comment

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

Good progress — the file-only output is exactly right, the batchId correlation on OSV requests/responses is clean, and the cache check summary (instead of per-package lines) is the correct call. The foundation is solid.

Before we merge, the log format is still missing a significant portion of what I outlined in my earlier comment. Comparing the proposed format against what the file produces today:

src/index.ts

The [debug] Writing debug log to ... line currently appears before the banner because createDebugLogger is called outside main(). Move it inside main(), after printBanner(), so it appears after "Fast. Local. Developer-first." in the output.

Also missing: the CLI started, Config loaded, and Advisory source entries from the proposed format. And when NODE_EXTRA_CA_CERTS is set (line 123), that should be logged — it's the first thing to check when diagnosing SSL proxy failures and right now it's completely invisible in the debug output.

The stack trace on error (line 341) goes to console.error which prints to the terminal. That should go to the log file instead — same as everything else.

src/scanner.ts

Missing: npm transitive graph, Workspace map, Findings classified, and Scan finished entries. These are all in the proposed format and give the timing and classification picture that makes the log useful for performance diagnosis.

src/remediation/npm-registry.ts

This file has no debug logging at all. fetchPackument has a catch {} that silently returns null — if the npm registry is unreachable or rate-limits, fix commands fail to generate with no trace in the log. Thread debugLog through fetchPackument, resolvePublishedFixVersion, and resolveLowestKnownNonVulnerableVersion and log each registry call, its outcome, and any errors.

src/utils/fix-runner.ts

runInstallCommand discards all stdout/stderr from the install process and debugLog isn't threaded through from index.ts at all. In debug mode, log the exact command being run and its exit code.

src/osv/cache.ts

Missing the Cache loaded entry with path and entry counts. Also the catch { return createEmptyCache() } fallback is completely silent — a corrupted or unreadable cache file produces no trace.

The goal is a log that covers the full lifecycle from startup to scan finished, with enough signal to diagnose network failures, cache issues, and fix command generation problems without having to add temporary console.log calls. The proposed format in the original comment covers exactly that — worth getting all of it in before merge.

@coder-Yash886 coder-Yash886 force-pushed the feature/issue-393-debug-flag branch from 16a7649 to defb907 Compare May 29, 2026 13:21
@coder-Yash886
Copy link
Copy Markdown
Contributor Author

@sonukapoor please review the PR When you have free time

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.

feat: add --debug flag for verbose network and runtime logging

3 participants