diff --git a/vanir/detector_runner.py b/vanir/detector_runner.py
index 7710525..1228b62 100644
--- a/vanir/detector_runner.py
+++ b/vanir/detector_runner.py
@@ -15,6 +15,7 @@
offline_directory_scanner /test/source
"""
import collections
+import copy
import datetime
import functools
import inspect
@@ -23,7 +24,7 @@
import os
import sys
import textwrap
-from typing import Any, Mapping, Sequence, Type, TypeVar
+from typing import Any, Mapping, Sequence, Set, Type, TypeVar
from absl import app
from absl import flags
@@ -33,6 +34,7 @@
from vanir import detector_common_flags
from vanir import osv_client
from vanir import reporter
+from vanir import vulnerability_manager
from vanir.scanners import scanner_base
@@ -160,6 +162,19 @@
+
+ | Ignored CVEs |
+
+ {{ 'Ignored CVEs by OSV ID filter ' if ignored_cves['ignored_by_ID_filter']|length > 0 else '' }}
+ {% for cve in ignored_cves['ignored_by_ID_filter'] %}{{ '%-15s' | format(cve) | replace(' ',' ')}} {% endfor %}
+
+ {{ 'Ignored CVEs by path filter ' if ignored_cves['ignored_by_path_filter']|length > 0 else '' }}
+ {% for path in ignored_cves['ignored_by_path_filter'] %}
+ {{ path }} {% for cve in ignored_cves['ignored_by_path_filter'][path] %}{{ '%-15s' | format(cve) | replace(' ',' ')}} {% endfor %}
+
+ {% endfor %}
+ |
+
@@ -351,11 +366,20 @@ def _get_public_osv_url(osv_id: str) -> str:
return 'Not published.'
return osv_client.get_osv_url(osv_id)
+def _get_cves_from_findings(findings: scanner_base.Findings, vuln_manager: vulnerability_manager)-> Set[str]:
+ """Generate CVE ID set based on findings. """
+ signature_ids = [vuln.signature_id for vuln in findings]
+ cve_ids = set()
+ for signature_id in signature_ids:
+ cve_ids.update(vuln_manager.sign_id_to_cve_ids(signature_id))
+ return cve_ids
+
def _generate_json_report(
report_file_name: str,
report_book: reporter.ReportBook,
covered_cves: Sequence[str],
+ ignored_cves: Mapping[str, any],
) -> None:
"""Generates a JSON report based on the findings.
@@ -363,6 +387,7 @@ def _generate_json_report(
report_file_name: a JSON report file name to create.
report_book: a report book instance containing all reports.
covered_cves: a sequence of CVEs covered by this run.
+ ignored_cves: a mapping of CVEs ignored by this run.
Returns:
None
@@ -392,6 +417,7 @@ def _generate_json_report(
})
json_report['options'] = ' '.join(sys.argv[1:])
json_report['covered_cves'] = covered_cves
+ json_report['ignored_cves'] = ignored_cves
json_report['missing_patches'] = missing_patches
with open(report_file_name, 'w') as report_file:
json.dump(json_report, report_file, indent=4)
@@ -401,6 +427,7 @@ def _generate_html_report(
report_file_name: str,
report_book: reporter.ReportBook,
covered_cves: Sequence[str],
+ ignored_cves: Mapping[str, any],
stats: scanner_base.ScannedFileStats,
) -> None:
"""Generates a HTML file summarizing the report in a human-readable format.
@@ -409,6 +436,7 @@ def _generate_html_report(
report_file_name: a HTML report file name to create.
report_book: a report book instance containing all reports.
covered_cves: a sequence of CVEs covered by this run.
+ ignored_cves: a mapping of CVEs ignored by this run.
stats: |ScannedFileStats| object with scan result stats.
Returns:
@@ -455,6 +483,7 @@ def _generate_html_report(
html_report = template.render(
report_file_name=report_file_name,
covered_cves=covered_cves,
+ ignored_cves=ignored_cves,
unpatched_cves=report_book.unpatched_cves,
target_missing_patches=target_missing_patches,
non_target_missing_patches=non_target_missing_patches,
@@ -530,9 +559,28 @@ def main(argv: Sequence[str]) -> None:
[scanner_base.ShortFunctionFilter()]
+ list(detector_common_flags.generate_finding_filters_from_flags())
)
+
+ # Apply findings filters and collect filtered out CVE IDs
+ ignored_paths = collections.defaultdict(list)
findings = scanner_base.ShortFunctionFilter().filter(findings)
+ all_findings = copy.copy(findings)
+ all_findings_cve_ids = _get_cves_from_findings(all_findings, vuln_manager)
+
for finding_filter in finding_filters:
findings = finding_filter.filter(findings)
+ # Collect CVE IDs for vulnerabilities filtered out by PathPrefixFilter
+ if isinstance(finding_filter, scanner_base.PathPrefixFilter):
+ findings_filtered = finding_filter.filter(all_findings)
+ filtered_cve_ids = _get_cves_from_findings(findings_filtered, vuln_manager)
+ cve_ids_ignored = list(all_findings_cve_ids - filtered_cve_ids)
+ if cve_ids_ignored:
+ excluded_path = finding_filter._prefix
+ for cve_id in cve_ids_ignored:
+ ignored_paths[excluded_path].append(cve_id)
+
+ # sort CVE IDs in ignored paths dict
+ for path in ignored_paths:
+ ignored_paths[path] = sorted(ignored_paths[path])
report_book = reporter.ReportBook(
reporter.generate_reports(findings), vuln_manager
@@ -546,11 +594,25 @@ def main(argv: Sequence[str]) -> None:
)
covered_cves = sorted(set(covered_cves))
+ # Collect CVE IDs for vulnerabilities filtered out by OsvIdFilter
+ filtered_vuln_ids = [vuln.id for vuln in vuln_manager.get_vulnerabilities(ignore_filters=False)]
+ unfiltered_vuln_ids = [vuln.id for vuln in vuln_manager.get_vulnerabilities(ignore_filters=True)]
+ ignored_cve_ids = set()
+ for osv_id in detector_common_flags._OSV_ID_IGNORE_LIST.value:
+ if osv_id in unfiltered_vuln_ids and osv_id not in filtered_vuln_ids:
+ ignored_cve_ids.update(vuln_manager.osv_id_to_cve_ids(osv_id))
+
+ ignored_cves = {}
+ if ignored_cve_ids:
+ ignored_cves["ignored_by_ID_filter"] = sorted(ignored_cve_ids)
+ if ignored_paths:
+ ignored_cves["ignored_by_path_filter"] = ignored_paths
+
# Generate a machine-readable JSON report.
- _generate_json_report(json_output_file_name, report_book, covered_cves)
+ _generate_json_report(json_output_file_name, report_book, covered_cves, ignored_cves)
# Generate a human-readable HTML report.
- _generate_html_report(html_output_file_name, report_book, covered_cves, stats)
+ _generate_html_report(html_output_file_name, report_book, covered_cves, ignored_cves, stats)
# Generate a console output.
scanned_files = stats.analyzed_files + stats.skipped_files
diff --git a/vanir/detector_runner_test.py b/vanir/detector_runner_test.py
index e808fc4..1b5d635 100644
--- a/vanir/detector_runner_test.py
+++ b/vanir/detector_runner_test.py
@@ -506,6 +506,13 @@ def test_main(self):
+
+ | Ignored CVEs |
+
+
+
+ |
+
Missing Patches in Target Files (in 1 vuln)
diff --git a/vanir/scanners/scanner_base.py b/vanir/scanners/scanner_base.py
index 008628a..cc4cd21 100644
--- a/vanir/scanners/scanner_base.py
+++ b/vanir/scanners/scanner_base.py
@@ -136,12 +136,15 @@ def __init__(self, prefix: str):
def filter(self, findings: Findings) -> Findings:
filtered_findings = {}
for sign, chunks in findings.items():
- filtered_findings[sign] = list(
+ filtered_chunks = list(
filter(
lambda chunk: not chunk.target_file.startswith(self._prefix),
chunks,
)
)
+ # only add findings if chunks not empty
+ if filtered_chunks:
+ filtered_findings[sign] = filtered_chunks
return filtered_findings
diff --git a/vanir/vulnerability_manager.py b/vanir/vulnerability_manager.py
index 024c4ed..46180ee 100644
--- a/vanir/vulnerability_manager.py
+++ b/vanir/vulnerability_manager.py
@@ -443,7 +443,7 @@ def add_vulnerability(
vuln: vulnerability.Vulnerability,
overwrite_older_duplicate: bool = False
):
- """Adds a new vulnearbility to the manager.
+ """Adds a new vulnerability to the manager.
Args:
vuln: the Vulnerability object to be added.
@@ -826,7 +826,8 @@ def generate_from_managers(
vulnerabilities = []
vfilters = set()
for manager in managers:
- vulnerabilities.extend(manager.vulnerabilities)
+ # Get all vulnerabilites for tracking purposes. The same filters are applied later anyway.
+ vulnerabilities.extend(manager.get_vulnerabilities(ignore_filters=True))
vfilters.update(manager.vulnerability_filters)
if vulnerability_filters:
vfilters.update(vulnerability_filters)