Skip to content

fix(scan): a source nobody configured is not a source that failed - #57

Merged
maksimtech merged 3 commits into
mainfrom
fix/an-unconfigured-source-is-not-a-failed-one
Oct 17, 2026
Merged

maksimtech merged 3 commits into
mainfrom
fix/an-unconfigured-source-is-not-a-failed-one

Conversation

@maksimtech

Copy link
Copy Markdown
Owner

The defect (audit finding PR-05)

Without VULDB_API_KEY, which is how most installations run, no scan could end in an all-clear:

⚠️  VulDB no_api_key: set VULDB_API_KEY to use VulDB — results for 7-zip are incomplete
… one line per target …
⚠️  VulDB answered for none of the 8 targets scanned — every CVE above comes from NVD, MSRC, CISA KEV.

"no CVEs found" needs every source to have answered, so it never appeared. Over HTTP, VulDB's refusal sat in errors of every scan.

A mock hid it. The autouse fixture vuldb_offline_by_default replaced the VulDB collector, in the CLI and the API, with one returning nothing. Its own docstring said why: otherwise the missing key "breaks every test that asserts errors == []".

Fix

  • CLI _scan_target and API _scan_one query VulDB only when its key is set.
  • The silent-sources summary counts only the sources that were asked, so VulDB is never named as one that answered.
  • A scan without the key says once: ℹ️ VulDB was not asked: set VULDB_API_KEY to add it to the scan.
  • The conftest fixture now only removes VULDB_API_KEY from the environment. This is isolation, not a mock.
  • vuldb.fetch_cves still refuses before any request without a key. The NIS2 evidence test checks this.
  • README: VulDB's status says it needs the key.

Tests (TDD, no mocks in the new file)

  • The mock was removed first. Seven existing tests then failed (test_collector_failures, test_scan_hardening, test_scan_source_outage), as did the two new behaviour tests. That is the defect in the real configuration.
  • New file tests/test_scan_unconfigured_source.py. NVD answers from the recording nvd_no_results.json, MSRC with 404, KEV with conftest's cached empty catalogue. It checks:
    • "no CVEs found" and no "incomplete";
    • VulDB never asked;
    • the information line printed once;
    • the API's errors hold the other sources' 503s and nothing from VulDB;
    • the summary of a 2-target scan with NVD down reads "every CVE above comes from MSRC, CISA KEV."
  • Changed: test_nis2_evidence.py::test_the_web_scan_asks_debian_and_the_command_line_scan_does_not now checks what it is about (no debian_fetch in the CLI scan) instead of the exact old loop line.
  • Mutation check: 4 mutations (CLI fan-out, API fan-out, summary, information line), each caught.
  • Windows: 2095 passed. WSL: 2054 passed. ruff and mypy: clean.
  • Real run: patchradar scan zzqxnonexistentproduct --days 2 now prints no CVEs found in last 2 days and the one information line.

This PR adds an entry at the top of ### Fixed, like #56. Whichever is merged second will need a one-line conflict fixed, which I'll do by merging main into it.

🤖 Generated with Claude Code

Without VULDB_API_KEY the VulDB collector refused every target, and
the scan reported each refusal as a failure: "results … are
incomplete" per target, "VulDB answered for none of the N targets" at
the end, and never "no CVEs found", which needs every source to have
answered. Over HTTP the refusal sat in `errors` of every scan. The
default configuration could never give an all-clear.

A fixture in conftest.py hid it: it replaced the VulDB collector, in
the CLI and the API, with one returning nothing in every test. It now
only removes the key from the environment, which is how most
installations run; seven existing tests failed that way before this
change, as did two of the new ones.

VulDB is now queried only with its key, by the CLI and the API alike,
the silent-sources summary counts only the sources asked, and a scan
says once that VulDB was not asked and how to add it. A real scan of a
product with no CVEs now ends in "no CVEs found". The collector still
refuses before any request without its key, as the NIS2 document says.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@codecov

codecov Bot commented Oct 17, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@codspeed

codspeed Bot commented Oct 17, 2026 •

Copy link
Copy Markdown

Merging this PR will not alter performance

⚠️ 11 benchmarks spent significant time in system calls

System calls cannot be consistently instrumented, so they are not included in the measure, which understates the real cost. Please switch to the Walltime instrument to accurately measure system calls.

Measurement and system calls

✅ 17 untouched benchmarks


Comparing fix/an-unconfigured-source-is-not-a-failed-one (d878419) with main (314e04a)

Open in CodSpeed

maksimtech and others added 2 commits October 17, 2026 15:40
codecov/patch on #57: the API's branch that adds VulDB when its key is
set had no test, since every test runs without the key. A scan with
VULDB_API_KEY set, VulDB answering an empty result, now asks it once
from the CLI and once from the API, and prints no line about the key.
Removing the branch fails it.

`vuldb._real_fetch_cves` and the line of test_vuldb.py that used it go:
they existed only to undo conftest's replacement of the collector,
which is gone.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…ource-is-not-a-failed-one

# Conflicts:
#	CHANGELOG.md
@sonarqubecloud

Copy link
Copy Markdown

@maksimtech
maksimtech merged commit a66861a into main Oct 17, 2026
25 checks passed
@maksimtech
maksimtech deleted the fix/an-unconfigured-source-is-not-a-failed-one branch October 17, 2026 13:46
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.

1 participant