From ad9d8cdf69b7082d4796dfb402670f0abf8cd5da Mon Sep 17 00:00:00 2001 From: Volodymyr Yahello Date: Mon, 23 Mar 2026 15:32:31 +0200 Subject: [PATCH 1/6] security: apply all audit fixes (H-01 through I-04) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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> --- .github/SECURITY.md | 23 +++++++++++++++++++++++ .github/workflows/analysis.yml | 21 ++++++++++++++++----- flake8_debug/errors.py | 5 ++++- flake8_debug/plugin.py | 28 +++++++++++++++++++++++----- requirements-dev.txt | 8 ++++---- requirements.txt | 1 - setup.py | 4 ++-- tests/flake8_debug_test.py | 24 ++++++++++++++++++++++++ 8 files changed, 96 insertions(+), 18 deletions(-) create mode 100644 .github/SECURITY.md diff --git a/.github/SECURITY.md b/.github/SECURITY.md new file mode 100644 index 0000000..eed7604 --- /dev/null +++ b/.github/SECURITY.md @@ -0,0 +1,23 @@ +# Security Policy + +## Supported Versions + +| Version | Supported | +|---------|-----------| +| 0.3.x | ✅ Yes | +| < 0.3 | ❌ No | + +## Reporting a Vulnerability + +Please **do not** open a public GitHub issue for security vulnerabilities — +doing so discloses the issue to all users before a fix is available. + +Email [vyahello@gmail.com](mailto:vyahello@gmail.com) with the subject line +`[SECURITY] flake8-debug` and include: + +- A description of the vulnerability +- Steps to reproduce it +- The potential impact you see + +You can expect an acknowledgement within **72 hours** and a resolution +timeline within **7 days** for critical issues. diff --git a/.github/workflows/analysis.yml b/.github/workflows/analysis.yml index b3aef4f..66f9795 100644 --- a/.github/workflows/analysis.yml +++ b/.github/workflows/analysis.yml @@ -1,8 +1,19 @@ name: Python source code assessment 🐍 -on: [push] +on: + push: + branches: [master] + pull_request: + branches: [master] + +permissions: + contents: read + jobs: build: runs-on: ubuntu-latest + permissions: + contents: read + checks: write strategy: max-parallel: 4 matrix: @@ -14,9 +25,9 @@ jobs: - "3.13" - "3.14" steps: - - uses: actions/checkout@v1 + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v1 + uses: actions/setup-python@42375524bfc43e60c680de71eb3fd7bc68ee5416 # v5.4.0 with: python-version: ${{ matrix.python-version }} - name: Static code analysis @@ -25,6 +36,6 @@ jobs: ./analyse-source-code.sh - name: Upload coverage data to coveralls.io env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + COVERALLS_REPO_TOKEN: ${{ secrets.COVERALLS_REPO_TOKEN }} run: | - coveralls --service=github + coveralls diff --git a/flake8_debug/errors.py b/flake8_debug/errors.py index 6f75e7e..8c8a66a 100644 --- a/flake8_debug/errors.py +++ b/flake8_debug/errors.py @@ -1,4 +1,7 @@ -class Error: +from abc import ABC + + +class Error(ABC): code: str func_name: str diff --git a/flake8_debug/plugin.py b/flake8_debug/plugin.py index 6636aaf..358e25b 100644 --- a/flake8_debug/plugin.py +++ b/flake8_debug/plugin.py @@ -1,4 +1,5 @@ import ast +import sys from typing import List, Tuple, Generator, Type, Any, Optional from flake8_debug.errors import ERRORS, Error @@ -6,6 +7,11 @@ TDebug = Generator[Tuple[int, int, str, Type[Any]], None, None] +# Only these func names are meaningful as attribute calls (e.g. pdb.set_trace()) +_ATTR_DETECTABLE: frozenset = frozenset({'set_trace'}) +# Only flag attribute calls when the object looks like a known debugger module +_DEBUGGER_MODULES: frozenset = frozenset({'pdb', 'ipdb'}) + class DebugVisitor(ast.NodeVisitor): def __init__(self, errors: Tuple[Type[Error], ...]) -> None: @@ -14,14 +20,20 @@ def __init__(self, errors: Tuple[Type[Error], ...]) -> None: def visit_Call(self, node: ast.Call) -> None: for error in self._errors: - if ( + is_bare_call = ( isinstance(node.func, ast.Name) and node.func.id == error.func_name - ) or ( + ) + is_attr_call = ( isinstance(node.func, ast.Attribute) and node.func.attr == error.func_name - ): + and error.func_name in _ATTR_DETECTABLE + and isinstance(node.func.value, ast.Name) + and node.func.value.id in _DEBUGGER_MODULES + ) + if is_bare_call or is_attr_call: self.issues.append((node.lineno, node.col_offset, error().msg)) + self.generic_visit(node) class NoDebug: @@ -32,10 +44,16 @@ def __init__( self, tree: ast.Module, filename: Optional[str] = None ) -> None: self._tree = tree - self._filename = filename def run(self) -> TDebug: debug = DebugVisitor(ERRORS) - debug.visit(self._tree) + old_limit = sys.getrecursionlimit() + try: + sys.setrecursionlimit(max(old_limit, 2000)) + debug.visit(self._tree) + except RecursionError: + pass + finally: + sys.setrecursionlimit(old_limit) for lineno, column, msg in debug.issues: # type: int, int, str yield lineno, column, msg, type(self) diff --git a/requirements-dev.txt b/requirements-dev.txt index 1583d15..7de1398 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -1,8 +1,8 @@ black==25.9.0 flake8>=4.0.1 pytest==8.4.2 -pytest-cov==3.0.0 -coverage==5.3 -coveralls==2.1.2 +pytest-cov==7.1.0 +coverage==7.13.5 +coveralls==4.1.0 setuptools==80.9.0 -wheel==0.45.1 \ No newline at end of file +wheel==0.46.3 \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index 394f984..2fe82b2 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,2 +1 @@ flake8>=4.0.1 -astpretty==2.1.0 diff --git a/setup.py b/setup.py index 8286828..42a5305 100644 --- a/setup.py +++ b/setup.py @@ -3,8 +3,6 @@ from setuptools import find_packages, setup -from flake8_debug.meta import Meta - def __load_readme() -> str: """Returns project description.""" @@ -19,6 +17,8 @@ def __load_requirements() -> Sequence[str]: if __name__ == '__main__': + from flake8_debug.meta import Meta + setup( name=Meta.name, version=Meta.version, diff --git a/tests/flake8_debug_test.py b/tests/flake8_debug_test.py index c1affe4..cd20124 100644 --- a/tests/flake8_debug_test.py +++ b/tests/flake8_debug_test.py @@ -94,3 +94,27 @@ def test_present_bare_set_trace(): ) == ( _out(line=3, column=5, err=PdbError()), ) + + +def test_nested_print_is_detected(): + assert _plugin_results('foo(print(0))') == ( + _out(line=1, column=5, err=PrintError()), + ) + + +def test_nested_breakpoint_is_detected(): + assert _plugin_results('foo(breakpoint())') == ( + _out(line=1, column=5, err=BreakpointError()), + ) + + +def test_no_false_positive_on_arbitrary_object_print(): + assert not _plugin_results('logger.print("msg")') + + +def test_no_false_positive_on_arbitrary_object_breakpoint(): + assert not _plugin_results('self.breakpoint()') + + +def test_no_false_positive_on_arbitrary_object_set_trace(): + assert not _plugin_results('cursor.set_trace()') From 1454a304cf75d3a409f443d57291de1d1aa21099 Mon Sep 17 00:00:00 2001 From: Volodymyr Yahello Date: Mon, 23 Mar 2026 15:36:51 +0200 Subject: [PATCH 2/6] revert: restore analysis.yml to original state Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .github/workflows/analysis.yml | 21 +++++---------------- 1 file changed, 5 insertions(+), 16 deletions(-) diff --git a/.github/workflows/analysis.yml b/.github/workflows/analysis.yml index 66f9795..b3aef4f 100644 --- a/.github/workflows/analysis.yml +++ b/.github/workflows/analysis.yml @@ -1,19 +1,8 @@ name: Python source code assessment 🐍 -on: - push: - branches: [master] - pull_request: - branches: [master] - -permissions: - contents: read - +on: [push] jobs: build: runs-on: ubuntu-latest - permissions: - contents: read - checks: write strategy: max-parallel: 4 matrix: @@ -25,9 +14,9 @@ jobs: - "3.13" - "3.14" steps: - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - uses: actions/checkout@v1 - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@42375524bfc43e60c680de71eb3fd7bc68ee5416 # v5.4.0 + uses: actions/setup-python@v1 with: python-version: ${{ matrix.python-version }} - name: Static code analysis @@ -36,6 +25,6 @@ jobs: ./analyse-source-code.sh - name: Upload coverage data to coveralls.io env: - COVERALLS_REPO_TOKEN: ${{ secrets.COVERALLS_REPO_TOKEN }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | - coveralls + coveralls --service=github From 46bca82837183a935844fdb3638cbf9556a49a8d Mon Sep 17 00:00:00 2001 From: Volodymyr Yahello Date: Mon, 23 Mar 2026 15:38:06 +0200 Subject: [PATCH 3/6] ci: remove Python 3.9 from test matrix Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .github/workflows/analysis.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/analysis.yml b/.github/workflows/analysis.yml index b3aef4f..6ac8351 100644 --- a/.github/workflows/analysis.yml +++ b/.github/workflows/analysis.yml @@ -7,7 +7,6 @@ jobs: max-parallel: 4 matrix: python-version: - - "3.9" - "3.10" - "3.11" - "3.12" From 764a000664202c361893e830f5765e60bf93c6c7 Mon Sep 17 00:00:00 2001 From: Volodymyr Yahello Date: Mon, 23 Mar 2026 15:39:30 +0200 Subject: [PATCH 4/6] fix: shorten comment in plugin.py to satisfy E501 line length Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- flake8_debug/plugin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flake8_debug/plugin.py b/flake8_debug/plugin.py index 358e25b..1e02107 100644 --- a/flake8_debug/plugin.py +++ b/flake8_debug/plugin.py @@ -7,7 +7,7 @@ TDebug = Generator[Tuple[int, int, str, Type[Any]], None, None] -# Only these func names are meaningful as attribute calls (e.g. pdb.set_trace()) +# Func names meaningful only as attribute calls (e.g. pdb.set_trace()) _ATTR_DETECTABLE: frozenset = frozenset({'set_trace'}) # Only flag attribute calls when the object looks like a known debugger module _DEBUGGER_MODULES: frozenset = frozenset({'pdb', 'ipdb'}) From 7642936d9996d133dc34dafa1c49656ecb13ab66 Mon Sep 17 00:00:00 2001 From: Volodymyr Yahello Date: Mon, 23 Mar 2026 15:41:55 +0200 Subject: [PATCH 5/6] docs: update minimum Python version to 3.10 in README Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 06c1c26..eeb2c6b 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ ## Tools ### Production -- python 3.9+ +- python 3.10+ - [flake8](http://flake8.pycqa.org/en/latest/) ### Development From 8169a0d067e9023b8ef86cd7fed04a86888efc11 Mon Sep 17 00:00:00 2001 From: Volodymyr Yahello Date: Mon, 23 Mar 2026 15:45:01 +0200 Subject: [PATCH 6/6] remove: delete redundant SECURITY.md Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .github/SECURITY.md | 23 ----------------------- 1 file changed, 23 deletions(-) delete mode 100644 .github/SECURITY.md diff --git a/.github/SECURITY.md b/.github/SECURITY.md deleted file mode 100644 index eed7604..0000000 --- a/.github/SECURITY.md +++ /dev/null @@ -1,23 +0,0 @@ -# Security Policy - -## Supported Versions - -| Version | Supported | -|---------|-----------| -| 0.3.x | ✅ Yes | -| < 0.3 | ❌ No | - -## Reporting a Vulnerability - -Please **do not** open a public GitHub issue for security vulnerabilities — -doing so discloses the issue to all users before a fix is available. - -Email [vyahello@gmail.com](mailto:vyahello@gmail.com) with the subject line -`[SECURITY] flake8-debug` and include: - -- A description of the vulnerability -- Steps to reproduce it -- The potential impact you see - -You can expect an acknowledgement within **72 hours** and a resolution -timeline within **7 days** for critical issues.