Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 34 additions & 10 deletions src/vuln_analysis/functions/cve_checker_report.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
checker_context and produces the final ExploitIqOutput.
"""

import os
import re
import warnings
from dataclasses import dataclass, field
Expand Down Expand Up @@ -508,16 +509,39 @@ def _format_target_build_check_md(blocks: "ReportBlocks") -> str:
return "\n".join(lines).strip()


def _is_source_code_file(file_path: str) -> bool:
"""Check if a file is RPM source code.

Only RPM source code files, excluding tests and build files.
"""
basename = os.path.basename(file_path)
if basename in ("CMakeLists.txt", "Makefile", "Makefile.am", "Makefile.in"):
return False

if file_path.startswith("test/") or "/test/" in file_path:
return False

return file_path.endswith((".c", ".h", ".cpp", ".cc", ".cxx"))


def _filter_to_source_code_files(file_paths: list[str]) -> list[str]:
filtered = [f for f in file_paths if _is_source_code_file(f)]
if not filtered and file_paths:
logger.warning(
f"All {len(file_paths)} affected file(s) were tests/build files (filtered out). "
f"No actual source files affected - report will show 'not determined'"
)
return filtered


def _filter_review_snippets(snippets: list[CodeSnippet]) -> list[CodeSnippet]:
"""Prefer primary source files over build-system noise when both are present."""
primary = [
s for s in snippets
if s.file_path.endswith((".c", ".h", ".cpp", ".cc", ".cxx"))
and "/test/" not in s.file_path
and "CMakeLists" not in s.file_path
and "Makefile" not in s.file_path
]
return primary if primary else snippets
filtered = [s for s in snippets if _is_source_code_file(s.file_path)]
if not filtered and snippets:
logger.warning(
f"All {len(snippets)} code snippet(s) were from tests/build files (filtered out). "
f"No actual source code snippets - report will show 'not shown'"
)
return filtered


def _is_reference_tree_path(file_path: str) -> bool:
Expand Down Expand Up @@ -831,7 +855,7 @@ def _build_report_blocks(
justification_label=code_agent_report.justification_label,
executive_summary=code_agent_report.executive_summary,
evidence_chain=list(code_agent_report.evidence_chain),
affected_files=list(code_agent_report.affected_files),
affected_files=_filter_to_source_code_files(list(code_agent_report.affected_files)),
patch_file_name=patch_file_name,
spec_patch_directives=spec_patch_directives,
build_log_evidence=build_log_evidence,
Expand Down
163 changes: 163 additions & 0 deletions tests/test_cve_checker_report_filtering.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
# SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0

"""Tests for RPM report file filtering."""

import pytest


# Import helper to avoid sys.path mutation
def _import_filter_functions():
"""Import filter functions from local source."""
import sys
import os
from pathlib import Path

# Add src to path temporarily for this import
src_path = Path(__file__).parent.parent / "src"
sys.path.insert(0, str(src_path))

try:
from vuln_analysis.functions.cve_checker_report import (
_is_source_code_file,
_filter_to_source_code_files,
)
return _is_source_code_file, _filter_to_source_code_files
finally:
# Remove from path to avoid leaking to other tests
sys.path.pop(0)


@pytest.fixture(scope="module")
def filter_funcs():
"""Provide filter functions to all tests."""
return _import_filter_functions()


def test_c_source_files_included(filter_funcs):
"""C source files should pass the filter."""
is_source, _ = filter_funcs
assert is_source("crypto/pkcs7/pk7_smime.c") is True
assert is_source("src/parser.c") is True
assert is_source("lib/util.c") is True


def test_c_header_files_included(filter_funcs):
"""C header files should pass the filter."""
is_source, _ = filter_funcs
assert is_source("include/ssl.h") is True
assert is_source("crypto/internal.h") is True


def test_cpp_files_included(filter_funcs):
"""C++ files should be included."""
is_source, _ = filter_funcs
assert is_source("src/engine.cpp") is True
assert is_source("lib/parser.cc") is True
assert is_source("util/helper.cxx") is True


def test_top_level_test_files_excluded(filter_funcs):
"""Top-level test/*.c files should be filtered out."""
is_source, _ = filter_funcs
# This is the critical case the reviewer pointed out
assert is_source("test/bad_dtls_frag.c") is False
assert is_source("test/unit/parser.c") is False
assert is_source("test/foo.c") is False


def test_nested_test_files_excluded(filter_funcs):
"""Nested /test/ directory files should be filtered out."""
is_source, _ = filter_funcs
assert is_source("src/test/helper.c") is False
assert is_source("lib/test/mock.cpp") is False


def test_non_c_test_files_excluded(filter_funcs):
"""Non-C/C++ test files should be filtered out."""
is_source, _ = filter_funcs
assert is_source("test/recipes/80-test_cms.t") is False
assert is_source("test/smime-eml/pkcs7-digest.eml") is False


def test_cmakelists_excluded(filter_funcs):
"""CMakeLists files should be filtered out."""
is_source, _ = filter_funcs
assert is_source("CMakeLists.txt") is False
assert is_source("src/CMakeLists.txt") is False
assert is_source("build/CMakeLists.txt") is False


def test_makefiles_excluded(filter_funcs):
"""Makefile files should be filtered out."""
is_source, _ = filter_funcs
assert is_source("Makefile") is False
assert is_source("src/Makefile") is False
assert is_source("Makefile.am") is False
assert is_source("build/Makefile.in") is False


def test_build_file_names_in_path_not_excluded(filter_funcs):
"""Files with 'Makefile' or 'CMakeLists' in path but not basename should be included."""
is_source, _ = filter_funcs
# These should NOT be excluded - only basename matters
assert is_source("src/MakefileParser.c") is True
assert is_source("util/CMakeListsWriter.cpp") is True
assert is_source("CMakeLists/generator.c") is True


def test_filter_to_source_code_files_openssl_case(filter_funcs):
"""Test filtering with mixed source and test files."""
_, filter_func = filter_funcs

affected_files = [
"crypto/pkcs7/pk7_smime.c",
"test/recipes/80-test_cms.t",
"test/smime-eml/pkcs7-empty-digest-set.eml",
]

result = filter_func(affected_files)
assert len(result) == 1
assert result == ["crypto/pkcs7/pk7_smime.c"]


def test_filter_mixed_list(filter_funcs):
"""Filter should correctly handle a mixed list of files."""
_, filter_func = filter_funcs

file_list = [
"crypto/ssl.c", # Keep
"test/bad_dtls.c", # Remove - top-level test
"src/parser.cpp", # Keep
"CMakeLists.txt", # Remove - build file
"lib/test/mock.c", # Remove - nested test
"include/api.h", # Keep
]

result = filter_func(file_list)
assert len(result) == 3
assert "crypto/ssl.c" in result
assert "src/parser.cpp" in result
assert "include/api.h" in result


def test_all_filtered_returns_empty(filter_funcs):
"""When all files are tests/build files, should return empty list."""
_, filter_func = filter_funcs

# All test files - none are actual source files
file_list = [
"test/unit_test.c",
"test/integration.c",
"lib/test/mock.c",
]

result = filter_func(file_list)
# Should return empty - no actual source files affected
assert result == []


def test_empty_list_returns_empty(filter_funcs):
"""Empty input should return empty output."""
_, filter_func = filter_funcs
assert filter_func([]) == []