Skip to content

Commit 62474e7

Browse files
vyahelloCopilot
andauthored
security: apply all audit fixes (H-01 through I-04) (#13)
* security: apply all audit fixes (H-01 through I-04) H-01: Pin GitHub Actions to immutable commit SHAs - actions/checkout@v1 → @11bd71901bbe5b1630ceea73d27597364c9af683 (v4.2.2) - actions/setup-python@v1 → @42375524bfc43e60c680de71eb3fd7bc68ee5416 (v5.4.0) H-02: Remove phantom astpretty==2.1.0 production dependency (unused) M-01: Fix detection bypass — add generic_visit(node) to DebugVisitor.visit_Call so nested debug calls (e.g. foo(print(x))) are no longer silently missed M-02: Fix false positives on arbitrary objects — restrict attribute-based detection to known debugger modules (pdb, ipdb) only for set_trace M-03: Update outdated dev dependencies - pytest-cov 3.0.0 → 7.1.0 - coverage 5.3 → 7.13.5 - coveralls 2.1.2 → 4.1.0 - wheel 0.45.1 → 0.46.3 L-01: Restrict CI trigger to master branch; add least-privilege permissions block Use COVERALLS_REPO_TOKEN instead of broad GITHUB_TOKEN for coveralls L-02: Move Meta import inside if __name__ == '__main__' in setup.py to prevent source package execution at pip install time L-03: Add RecursionError guard in NoDebug.run() for pathologically nested ASTs L-04: Add .github/SECURITY.md with vulnerability disclosure policy I-01: Remove unused self._filename attribute from NoDebug.__init__ I-03: Inherit Error from abc.ABC for proper abstract base class semantics I-04: Remove committed __pycache__ and .DS_Store files from git index Tests: add 5 regression tests covering nested call detection and false-positive elimination for arbitrary object method calls Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * revert: restore analysis.yml to original state Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * ci: remove Python 3.9 from test matrix Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix: shorten comment in plugin.py to satisfy E501 line length Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * docs: update minimum Python version to 3.10 in README Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * remove: delete redundant SECURITY.md Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 0a71322 commit 62474e7

8 files changed

Lines changed: 58 additions & 15 deletions

File tree

.github/workflows/analysis.yml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ jobs:
77
max-parallel: 4
88
matrix:
99
python-version:
10-
- "3.9"
1110
- "3.10"
1211
- "3.11"
1312
- "3.12"

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
## Tools
1919

2020
### Production
21-
- python 3.9+
21+
- python 3.10+
2222
- [flake8](http://flake8.pycqa.org/en/latest/)
2323

2424
### Development

flake8_debug/errors.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1-
class Error:
1+
from abc import ABC
2+
3+
4+
class Error(ABC):
25
code: str
36
func_name: str
47

flake8_debug/plugin.py

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,17 @@
11
import ast
2+
import sys
23
from typing import List, Tuple, Generator, Type, Any, Optional
34

45
from flake8_debug.errors import ERRORS, Error
56
from flake8_debug.meta import Meta
67

78
TDebug = Generator[Tuple[int, int, str, Type[Any]], None, None]
89

10+
# Func names meaningful only as attribute calls (e.g. pdb.set_trace())
11+
_ATTR_DETECTABLE: frozenset = frozenset({'set_trace'})
12+
# Only flag attribute calls when the object looks like a known debugger module
13+
_DEBUGGER_MODULES: frozenset = frozenset({'pdb', 'ipdb'})
14+
915

1016
class DebugVisitor(ast.NodeVisitor):
1117
def __init__(self, errors: Tuple[Type[Error], ...]) -> None:
@@ -14,14 +20,20 @@ def __init__(self, errors: Tuple[Type[Error], ...]) -> None:
1420

1521
def visit_Call(self, node: ast.Call) -> None:
1622
for error in self._errors:
17-
if (
23+
is_bare_call = (
1824
isinstance(node.func, ast.Name)
1925
and node.func.id == error.func_name
20-
) or (
26+
)
27+
is_attr_call = (
2128
isinstance(node.func, ast.Attribute)
2229
and node.func.attr == error.func_name
23-
):
30+
and error.func_name in _ATTR_DETECTABLE
31+
and isinstance(node.func.value, ast.Name)
32+
and node.func.value.id in _DEBUGGER_MODULES
33+
)
34+
if is_bare_call or is_attr_call:
2435
self.issues.append((node.lineno, node.col_offset, error().msg))
36+
self.generic_visit(node)
2537

2638

2739
class NoDebug:
@@ -32,10 +44,16 @@ def __init__(
3244
self, tree: ast.Module, filename: Optional[str] = None
3345
) -> None:
3446
self._tree = tree
35-
self._filename = filename
3647

3748
def run(self) -> TDebug:
3849
debug = DebugVisitor(ERRORS)
39-
debug.visit(self._tree)
50+
old_limit = sys.getrecursionlimit()
51+
try:
52+
sys.setrecursionlimit(max(old_limit, 2000))
53+
debug.visit(self._tree)
54+
except RecursionError:
55+
pass
56+
finally:
57+
sys.setrecursionlimit(old_limit)
4058
for lineno, column, msg in debug.issues: # type: int, int, str
4159
yield lineno, column, msg, type(self)

requirements-dev.txt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
black==25.9.0
22
flake8>=4.0.1
33
pytest==8.4.2
4-
pytest-cov==3.0.0
5-
coverage==5.3
6-
coveralls==2.1.2
4+
pytest-cov==7.1.0
5+
coverage==7.13.5
6+
coveralls==4.1.0
77
setuptools==80.9.0
8-
wheel==0.45.1
8+
wheel==0.46.3

requirements.txt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1 @@
11
flake8>=4.0.1
2-
astpretty==2.1.0

setup.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,6 @@
33

44
from setuptools import find_packages, setup
55

6-
from flake8_debug.meta import Meta
7-
86

97
def __load_readme() -> str:
108
"""Returns project description."""
@@ -19,6 +17,8 @@ def __load_requirements() -> Sequence[str]:
1917

2018

2119
if __name__ == '__main__':
20+
from flake8_debug.meta import Meta
21+
2222
setup(
2323
name=Meta.name,
2424
version=Meta.version,

tests/flake8_debug_test.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,3 +94,27 @@ def test_present_bare_set_trace():
9494
) == (
9595
_out(line=3, column=5, err=PdbError()),
9696
)
97+
98+
99+
def test_nested_print_is_detected():
100+
assert _plugin_results('foo(print(0))') == (
101+
_out(line=1, column=5, err=PrintError()),
102+
)
103+
104+
105+
def test_nested_breakpoint_is_detected():
106+
assert _plugin_results('foo(breakpoint())') == (
107+
_out(line=1, column=5, err=BreakpointError()),
108+
)
109+
110+
111+
def test_no_false_positive_on_arbitrary_object_print():
112+
assert not _plugin_results('logger.print("msg")')
113+
114+
115+
def test_no_false_positive_on_arbitrary_object_breakpoint():
116+
assert not _plugin_results('self.breakpoint()')
117+
118+
119+
def test_no_false_positive_on_arbitrary_object_set_trace():
120+
assert not _plugin_results('cursor.set_trace()')

0 commit comments

Comments
 (0)