fix: skip SBOM components with UNKNOWN version instead of raising exception#5646
fix: skip SBOM components with UNKNOWN version instead of raising exception#5646nancysangani wants to merge 2 commits intoossf:mainfrom
Conversation
…eption Signed-off-by: Nancy <9d.24.nancy.sangani@gmail.com>
ab77d60 to
a16a99b
Compare
There was a problem hiding this comment.
Pull request overview
This PR fixes a crash when scanning SBOM components whose version is "UNKNOWN" by handling UnknownVersion during version parsing and adding regression coverage around unknown/empty versions.
Changes:
- Catch
UnknownVersionwhen parsingproduct_info.versioninCVEScanner.get_cves()and skip such components with a debug log. - Add regression tests covering
"UNKNOWN","unknown", empty string, and a valid version.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
cve_bin_tool/cve_scanner.py |
Prevents UnknownVersion from terminating scans by skipping unknown/unparseable versions. |
test/test_cve_scanner_unknown_version.py |
Adds regression tests to ensure unknown/empty versions don’t crash scanning. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| except UnknownVersion: | ||
| self.logger.debug( | ||
| f"Skipping {product_info.product} due to unknown version: {product_info.version!r}" | ||
| ) |
There was a problem hiding this comment.
When UnknownVersion is caught, get_cves() returns before updating all_product_data / all_cve_data and before recording triage_data['paths']. That means components with unknown versions effectively disappear from downstream outputs (e.g., SBOM generation uses all_product_data), rather than being treated as “detected but skipped”. Consider adding the same minimal bookkeeping used in the vendor == 'UNKNOWN' / no_scan early-return paths (populate all_product_data and update paths) before returning.
| ) | |
| ) | |
| # Even when skipping due to unknown version, record that this product was detected | |
| # so that downstream consumers (e.g., SBOM generation) can see it as "detected but skipped". | |
| paths = set(triage_data.get("paths", [])) | |
| # Update all_product_data with the observed paths, if this structure is present. | |
| if hasattr(self, "all_product_data"): | |
| product_entry = self.all_product_data.setdefault( | |
| product_info, {"paths": set()} | |
| ) | |
| product_entry.setdefault("paths", set()).update(paths) | |
| # Ensure all_cve_data has an entry with no CVEs but with the correct paths. | |
| if hasattr(self, "all_cve_data"): | |
| cve_entry = self.all_cve_data.setdefault( | |
| product_info, {"cves": [], "paths": set()} | |
| ) | |
| cve_entry.setdefault("paths", set()).update(paths) |
| try: | ||
| parsed_version = Version(product_info.version) |
There was a problem hiding this comment.
UnknownVersion in version_compare.parse_version() is detected before .strip(), so inputs like ' UNKNOWN ' (leading/trailing whitespace) won’t raise and can lead to incorrect range comparisons / false positives. Consider normalizing product_info.version (e.g., stripping whitespace and applying case-insensitive 'unknown' handling) before constructing Version() so the skip logic is applied consistently.
| try: | |
| parsed_version = Version(product_info.version) | |
| version_value = product_info.version | |
| if isinstance(version_value, str): | |
| version_value = version_value.strip() | |
| if version_value.lower() == "unknown": | |
| self.logger.debug( | |
| f"Skipping {product_info.product} due to unknown version: {product_info.version!r}" | |
| ) | |
| return | |
| try: | |
| parsed_version = Version(version_value) |
| try: | ||
| scanner.get_cves(product_info, TRIAGE_DATA_EMPTY) | ||
| except Exception as e: | ||
| pytest.fail( | ||
| f"get_cves raised unexpected exception for valid version: {e}" | ||
| ) No newline at end of file |
There was a problem hiding this comment.
In test_valid_version_still_processed, the try/except Exception + pytest.fail() is redundant—pytest will already fail the test if get_cves() raises. Removing the wrapper (or using an explicit does_not_raise context) will simplify the test and preserve the original exception traceback.
| try: | |
| scanner.get_cves(product_info, TRIAGE_DATA_EMPTY) | |
| except Exception as e: | |
| pytest.fail( | |
| f"get_cves raised unexpected exception for valid version: {e}" | |
| ) | |
| scanner.get_cves(product_info, TRIAGE_DATA_EMPTY) |
…eption Signed-off-by: Nancy <9d.24.nancy.sangani@gmail.com>
Fixes #5302
When scanning a CycloneDX SBOM containing a component with version "UNKNOWN",
cve_scanner.py raised an unhandled UnknownVersion exception that terminated
the entire scan. Components with unknown versions cannot be meaningfully
checked for CVEs and should be skipped with a debug log message.
Changes
cve_bin_tool/cve_scanner.py: ImportUnknownVersion, wrapVersion()call in try/except, return early with debug log if version is unknown
test/test_cve_scanner_unknown_version.py: Regression tests for UNKNOWN,unknown (lowercase), empty string, and valid version cases