Skip to content
Open
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
11 changes: 9 additions & 2 deletions desloppify/languages/python/_security.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import shutil

from desloppify.base.config import load_config
from desloppify.base.discovery.source import collect_exclude_dirs
from desloppify.base.discovery.source import collect_exclude_dirs, get_exclusions
from desloppify.languages._framework.base.types import DetectorCoverageStatus, LangSecurityResult
from desloppify.languages.python.detectors.bandit_adapter import detect_with_bandit
from desloppify.languages.python._helpers import scan_root_from_files
Expand Down Expand Up @@ -49,7 +49,14 @@ def detect_python_security(files, zone_map) -> LangSecurityResult:
if scan_root is None:
return LangSecurityResult(entries=[], files_scanned=0)

exclude_dirs = collect_exclude_dirs(scan_root)
# Config-level excludes (`desloppify exclude <dir>`) are stored in the project
# config, not the runtime context that get_exclusions() reads here. Without
# loading them explicitly, bandit rescans excluded directories even though every
# other detector honours the exclude.
config_excludes = tuple(p for p in (load_config().get("exclude") or ()) if isinstance(p, str))
exclude_dirs = collect_exclude_dirs(
scan_root, extra_exclusions=tuple(get_exclusions()) + config_excludes
)
skip_tests = _load_bandit_skip_tests()
result = detect_with_bandit(
scan_root, zone_map, exclude_dirs=exclude_dirs, skip_tests=skip_tests,
Expand Down
28 changes: 28 additions & 0 deletions desloppify/tests/detectors/test_external_adapters_exclude_flags.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

from unittest.mock import MagicMock, patch

from desloppify.languages.python._security import detect_python_security
from desloppify.languages.python.detectors.ruff_smells import detect_with_ruff_smells
from desloppify.languages.python.detectors.unused import detect_unused

Expand Down Expand Up @@ -104,3 +105,30 @@ def _capture_run(cmd, **kwargs):
detect_unused(tmp_path)

assert "--exclude" not in captured_cmd


class TestBanditSecurityExcludeFlag:
"""Regression: config-level excludes (`desloppify exclude <dir>`) must reach
bandit's --exclude. Previously only the file-discovery detectors honoured them,
so the security detector kept reporting findings from excluded directories."""

def test_security_passes_config_excludes_to_bandit(self, tmp_path):
(tmp_path / "app.py").write_text("x = 1\n")
captured_cmd: list[str] = []

def _capture_run(cmd, **kwargs):
captured_cmd.extend(cmd)
mock_result = MagicMock()
mock_result.stdout = ""
mock_result.returncode = 0
return mock_result

with patch("subprocess.run", side_effect=_capture_run), patch(
"desloppify.languages.python._security.load_config",
return_value={"exclude": ["vendored_lib"]},
):
detect_python_security([str(tmp_path / "app.py")], zone_map=None)

assert "--exclude" in captured_cmd
exclude_value = captured_cmd[captured_cmd.index("--exclude") + 1]
assert "vendored_lib" in exclude_value