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
48 changes: 40 additions & 8 deletions src/vuln_analysis/tools/configuration_scanner.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,14 +53,35 @@ def format_context_snippet(lines: list[str], match_line: int, context_lines: int
"config.yaml", "config.yml", "config.xml",
"settings.toml", "settings.yaml", "settings.yml",
"web.xml", "beans.xml",
"Dockerfile", "Dockerfile.*", "docker-compose*.yml",
"Dockerfile", "Dockerfile.*", "docker-compose*.yml", "docker-compose*.yaml",
# Build/tool configuration — contain compiler flags, tool settings, or build options
"pyproject.toml", "setup.cfg", "tox.ini",
"tsconfig.json", ".eslintrc.json",
"Makefile", "CMakeLists.txt", "meson.build",
# Config-specific extensions — safe to match anywhere
"*.properties", "*.env", "*.conf", "*.ini",
]

# Directory names that typically contain configuration files
CONFIG_DIR_PATTERNS = ["config", "conf", "conf.d", "etc", "resources"]

_BINARY_EXTENSIONS = frozenset({
".jar", ".war", ".ear", ".class", ".zip", ".tar", ".gz", ".bz2", ".xz",
".png", ".jpg", ".jpeg", ".gif", ".ico", ".svg", ".woff", ".woff2",
".ttf", ".eot", ".pdf", ".so", ".dylib", ".dll", ".exe", ".pyc", ".pyo",
})

# Extensions accepted for files matched only by directory (e.g. under resources/).
# Files matched by _is_config_file (name pattern like application.yml) bypass this.
# Extensionless files (Caddyfile, Makefile, etc.) are also accepted.
# Ecosystem-specific named files (go.mod, requirements.txt) belong in
# CONFIG_FILE_PATTERNS instead — adding .txt here would re-admit theme/doc noise.
_CONFIG_DIR_ALLOWED_EXTENSIONS = frozenset({
".xml", ".yml", ".yaml", ".json",
".toml", ".cfg", ".ini", ".conf",
".properties", ".env",
})

# Avoids per-file regex compilation
_CONFIG_EXTENSIONS = []
_CONFIG_EXACT_NAMES = []
Expand Down Expand Up @@ -93,7 +114,7 @@ def _is_config_file(file_path: str) -> bool:
return True
if lower_name in _CONFIG_EXACT_NAMES:
return True
if any(p.match(lower_name) for p in _CONFIG_WILDCARD_PATTERNS):
if any(p.fullmatch(lower_name) for p in _CONFIG_WILDCARD_PATTERNS):
return True

return False
Expand All @@ -117,7 +138,14 @@ def _collect_config_files(repo_path: str) -> list[tuple[str, str]]:
for fname in files:
full_path = os.path.join(root, fname)
rel_path = os.path.relpath(full_path, repo_path)
if _is_config_file(rel_path) or _is_in_config_dir(rel_path):
if any(fname.lower().endswith(ext) for ext in _BINARY_EXTENSIONS):
continue
is_named_config = _is_config_file(rel_path)
if not is_named_config and _is_in_config_dir(rel_path):
ext = os.path.splitext(fname)[1].lower()
if ext and ext not in _CONFIG_DIR_ALLOWED_EXTENSIONS:
continue
if is_named_config or _is_in_config_dir(rel_path):
try:
with open(full_path, "r", errors="ignore") as f:
content = f.read()
Expand Down Expand Up @@ -149,6 +177,8 @@ def search_config_content(
source_label: str = "unknown",
) -> list[str]:
"""Match keywords against config file contents, returning formatted snippets."""
if max_results <= 0:
return []
matches = []
for rel_path, content in config_files:
lines = content.split("\n")
Expand Down Expand Up @@ -193,21 +223,23 @@ async def _arun(query: str) -> str:
continue

repo_key = (si.git_repo, si.ref)
if repo_key in _config_files_cache:
async with _repo_locks_guard:
async with _repo_locks_guard:
if repo_key in _config_files_cache:
_config_files_cache.move_to_end(repo_key)
else:
async with _repo_locks_guard:
cached = True
else:
if repo_key not in _repo_locks:
_repo_locks[repo_key] = asyncio.Lock()
repo_lock = _repo_locks[repo_key]
cached = False
if not cached:
async with repo_lock:
if repo_key not in _config_files_cache:
_config_files_cache[repo_key] = _collect_config_files(str(repo_path))
if len(_config_files_cache) > _CONFIG_CACHE_MAX_SIZE:
_config_files_cache.popitem(last=False)

for cfg in _config_files_cache[repo_key]:
for cfg in _config_files_cache.get(repo_key, []):
if is_dependency_path(cfg[0]):
all_dep_configs.append(cfg)
else:
Expand Down
Loading