Skip to content

chore(ci): add security-audit tooling scripts (workflow files pushed separately) - #414

Merged
ChuckBuilds merged 4 commits into
mainfrom
chore/security-audit-tooling
Jul 15, 2026
Merged

chore(ci): add security-audit tooling scripts (workflow files pushed separately)#414
ChuckBuilds merged 4 commits into
mainfrom
chore/security-audit-tooling

Conversation

@ChuckBuilds

@ChuckBuilds ChuckBuilds commented Jul 14, 2026

Copy link
Copy Markdown
Owner

Summary

Split out of PR #412 (chore/dead-code-removal), which had accidentally bundled this in alongside unrelated dead-code deletions.

  • scripts/prove_security.py, audit_plugins.py, generate_report.py -- automated checks (dangerous eval()/exec() calls, dependency scanning, report generation) for plugins.
  • bandit.yaml -- bandit static-analysis config.

⚠️ Missing from this PR: .github/workflows/security-audit.yml and .github/workflows/tests.yml -- my git credentials here only have repo scope, not workflow, which GitHub requires to push changes under .github/workflows/. Those two files still need to be added (I have them ready if you want the diff, or can push once scope is granted).

Fixes 1 Codacy finding while these files are freshly landing:

  • prove_security.py: dropped a pointless f-string prefix (no placeholders).
  • (The gitleaks-action SHA-pinning finding is in security-audit.yml, not yet pushed -- still needs applying when that file lands.)

Test plan

  • python3 -m py_compile scripts/*.py passes
  • Full test suite green (same pre-existing failures as main, no new regressions)
  • Add the two workflow files separately once workflow scope is available

🤖 Generated with Claude Code

https://claude.ai/code/session_01KEZK1P1Q1fu5pcuVrkrCFZ

Summary by CodeRabbit

  • New Features
    • Added plugin security auditing for CI, flagging missing manifest/schema files, unsafe code patterns, and scan failures (syntax/unreadable files), with optional JSON output.
    • Added “security proof” checks covering zip-slip/path traversal protections, dangerous plugin usage, secret/password heuristics, auth-bypass patterns, API/CSRF intent, and Docker hardening.
    • Added a consolidated Markdown report generator that merges multiple scan artifacts, summarizes critical findings, and marks reports as passed/incomplete/action-required.
    • Added Bandit configuration to skip selected findings and exclude common non-source directories.

ChuckBuilds and others added 2 commits July 14, 2026 16:30
- scripts/prove_security.py, audit_plugins.py, generate_report.py --
  automated checks (dangerous eval()/exec() calls, dependency scanning,
  report generation) for plugins.
- .github/workflows/security-audit.yml + bandit.yaml -- CI wiring for
  the above plus gitleaks secret scanning and bandit static analysis.
- .github/workflows/tests.yml -- pytest matrix across Python 3.10-3.12.

Also fixes two Codacy findings while these files are freshly landing:
- prove_security.py: dropped a pointless f-string prefix with no
  placeholders.
- security-audit.yml: pinned gitleaks/gitleaks-action to a full commit
  SHA (matching this repo's existing pinning convention in test.yml)
  instead of the floating v2 tag.

Split out of the original chore/dead-code-removal commit, which had
accidentally bundled this in alongside unrelated dead-code deletions.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KEZK1P1Q1fu5pcuVrkrCFZ
@coderabbitai

coderabbitai Bot commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

@ChuckBuilds, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 44 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: b7a9398b-7ce6-4206-a1cd-afce4fc76ce4

📥 Commits

Reviewing files that changed from the base of the PR and between d2c500e and 7aa2ef2.

📒 Files selected for processing (3)
  • scripts/audit_plugins.py
  • scripts/generate_report.py
  • scripts/prove_security.py
📝 Walkthrough

Walkthrough

Added Bandit configuration, plugin security auditing, repository security proof checks, and a report generator that combines scan artifacts into a Markdown security report with aggregated critical findings.

Changes

Security tooling

Layer / File(s) Summary
Plugin audit and scanner configuration
bandit.yaml, scripts/audit_plugins.py
Bandit exclusions and justified rule skips were configured, and plugin directories can now be scanned for required files, dangerous AST patterns, syntax errors, and unreadable files.
Repository security proofs
scripts/prove_security.py
CI checks were added for archive traversal, dangerous plugin calls, API/CSRF annotations, secrets, plaintext passwords, path traversal, authentication bypass patterns, and Docker hardening.
Security report aggregation
scripts/generate_report.py
Bandit, dependency, secret, proof, and plugin audit JSON artifacts are summarized into a Markdown report with combined critical counts and overall status.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant CI
  participant prove_security
  participant audit_plugins
  participant ArtifactDirectory
  participant generate_report
  CI->>prove_security: Run repository security proofs
  prove_security->>ArtifactDirectory: Write security-proofs-results.json
  CI->>audit_plugins: Audit plugin directories
  audit_plugins->>ArtifactDirectory: Write plugin-audit-results.json
  CI->>generate_report: Generate report from scan artifacts
  generate_report->>ArtifactDirectory: Load tool JSON results
  generate_report-->>CI: Write combined Markdown report
Loading
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the main change: adding CI security-audit tooling scripts, with workflows noted as separate.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch chore/security-audit-tooling

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@codacy-production

codacy-production Bot commented Jul 14, 2026

Copy link
Copy Markdown

Up to standards ✅

🟢 Issues 0 issues

Results:
0 new issues

View in Codacy

🟢 Metrics 251 complexity · 0 duplication

Metric Results
Complexity 251
Duplication 0

View in Codacy

NEW Get contextual insights on your PRs based on Codacy's metrics, along with PR and Jira context, without leaving GitHub. Enable AI reviewer
TIP This summary will be updated as you push new changes.

@ChuckBuilds

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 15, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 12

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@bandit.yaml`:
- Around line 13-22: Remove the repo-wide B607 exclusion from the Bandit
configuration and narrow it to the reviewed subprocess call sites in api_v3.py,
wifi_manager.py, and the affected scripts, or replace the sensitive bare-name
invocations of systemctl, sudo, and git with absolute executable paths. Keep
B603 suppression unchanged.
- Around line 30-33: Remove scripts/prove_security.py from the file-level
exclusion list in bandit.yaml so Bandit scans it normally. Add targeted # nosec
annotations only to the specific detection-pattern lines that produce false
positives, leaving all other findings enabled.

In `@scripts/audit_plugins.py`:
- Around line 169-186: Update the exception handling in scripts/audit_plugins.py
at lines 169-186 to classify SyntaxError and OSError findings as blocking scan
errors rather than WARNING or INFO results. Update the handling in
scripts/prove_security.py at lines 114-124 to preserve and propagate these
failures as CRITICAL or infrastructure-error results instead of discarding them;
use the existing Finding and security-result symbols and retain the captured
exception details.
- Around line 216-235: Update the plugin traversal around audit_plugin and
plugins_scanned to track whether the requested args.plugin was found; after all
PLUGIN_BASE_DIRS have been scanned, reject an unmatched plugin name with a clear
error and non-success exit status, while preserving normal behavior when no
plugin filter is provided or the plugin exists.
- Around line 70-107: Update the audit visitor’s import tracking and call-target
normalization so dangerous API checks cover aliased imports and from-imports,
including subprocess.run, os.system, and builtins.eval forms. Add alias handling
in visit_Import and visit_ImportFrom, then reuse the normalized targets in
visit_Call while preserving the existing PLUGIN-001 through PLUGIN-005
classifications and shell=True validation.

In `@scripts/generate_report.py`:
- Around line 58-59: Update _md_table_row() to sanitize each cell before joining
the row: escape pipe characters and normalize newline characters so
scanner-controlled content cannot alter Markdown table structure or finding
boundaries. Preserve the existing row formatting and support all cell values
handled by the current str conversion.
- Around line 50-55: Update _load and the report aggregation flow to distinguish
missing or malformed scan artifacts from valid empty results. Track each
unavailable tool, report INCOMPLETE instead of PASSED, return a non-zero exit
code, and provide a clear error message identifying the unavailable or invalid
artifact while preserving normal results for valid scans.
- Around line 136-143: Update the finding filter in the report-generation flow
to remove broad substring checks against _GITLEAKS_SUPPRESS. Suppress only exact
known placeholder values or findings whose paths match explicitly approved
template paths, while preserving genuine credentials in real_findings and the
critical count.

In `@scripts/prove_security.py`:
- Around line 417-424: Update the base-image validation loop in the FROM parsing
logic to require an immutable `@sha256`: digest in every image reference. Continue
reporting missing or latest tags as unpinned, and reject tag-only references
such as python:3.12 while allowing valid digest-pinned images.
- Around line 146-185: Update test_t2a_api_surface_inventory so unauthenticated
access with disabled CSRF is not reported as INFO by default. Add
environment-aware validation of the deployment boundary, using the project’s
configuration mechanism to recognize when local-only access is explicitly
enforced; return at least WARNING when that enforcement is absent, while
preserving the existing route inventory and documentation checks.
- Around line 74-89: Replace the file-wide substring checks in
scripts/prove_security.py at lines 74-89 with validation that confirms the
is_relative_to() guard and “Zip-slip detected” handling directly protect the
relevant zipfile.extractall() operation. Update the extractall() inspection at
lines 324-344 to evaluate each extraction call independently and establish that
its preceding validation path enforces the guard; preserve the existing
TestResult outcomes and severity semantics.
- Around line 228-251: Update the violation construction in the secret-scanning
loop over _SECRET_PATTERNS so result details never include line_content or the
matched credential. Report only the relative file, line number, pattern
type/severity, and a redacted fingerprint derived from the match, while
preserving the existing critical and warning aggregation in the T3a result
handling.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: fc66f736-6213-4364-bfd0-3a8152d99a10

📥 Commits

Reviewing files that changed from the base of the PR and between 14a59c8 and acaa11f.

📒 Files selected for processing (4)
  • bandit.yaml
  • scripts/audit_plugins.py
  • scripts/generate_report.py
  • scripts/prove_security.py

Comment thread bandit.yaml
Comment thread bandit.yaml Outdated
Comment thread scripts/audit_plugins.py Outdated
Comment thread scripts/audit_plugins.py
Comment thread scripts/audit_plugins.py
Comment thread scripts/generate_report.py
Comment thread scripts/prove_security.py Outdated
Comment thread scripts/prove_security.py Outdated
Comment thread scripts/prove_security.py Outdated
Comment thread scripts/prove_security.py Outdated
… audit_plugins.py, generate_report.py, prove_security.py

bandit.yaml:
- Removed scripts/prove_security.py's file-level exclusion. Ran bandit
  directly to get ground truth: the real false positive is B105 (dict key
  "PASS" misread as password-like), not the eval/exec pattern the old
  comment claimed. Added a targeted # nosec B105 there, and found+fixed
  the identical pattern already present in generate_report.py.
- Left the repo-wide B607 skip as-is: confirmed via AST scan that properly
  narrowing it touches 100+ bare-name subprocess call sites across
  wifi_manager.py, store_manager.py, permission_utils.py, app.py, and
  start.py -- none of which are part of this PR. Out of proportion to fix
  here; flagged as a dedicated follow-up.

scripts/audit_plugins.py:
- SyntaxError/OSError while scanning a plugin file now report CRITICAL
  (blocking) instead of WARNING/INFO -- a file that couldn't be parsed or
  read was never actually checked for danger, so it must not silently
  pass the audit.
- --plugin <name> now tracks whether the requested plugin was found across
  all PLUGIN_BASE_DIRS and exits 1 with a clear error if not, instead of
  silently scanning zero plugins and reporting success.
- The AST visitor now tracks import aliases (import subprocess as sp;
  from builtins import eval as e) and resolves them before checking
  against dangerous APIs, closing a straightforward evasion of every
  PLUGIN-001 through PLUGIN-005 check. Verified against both aliased and
  unaliased evasion patterns.

scripts/generate_report.py:
- _md_table_row now escapes pipe characters and normalizes newlines in
  every cell, so scanner-controlled content (a matched secret, a bandit
  issue_text) can't corrupt the Markdown table structure.
- _load now distinguishes "artifact missing/malformed" from "valid empty
  result": each summarizer returns an availability flag, and main() now
  reports INCOMPLETE (not PASSED) with exit code 1 when any artifact is
  unavailable, instead of silently folding it in as 0 findings.
- Gitleaks suppression now uses exact-match placeholder values (pulled
  from the actual config_secrets.template.json) plus a template-path
  allowlist, replacing broad substring checks that could hide a real
  secret containing something like "example.com" as part of its value.

scripts/prove_security.py:
- T1b (dangerous plugin calls): a file that fails to parse/read now
  reports CRITICAL with the exception details instead of being silently
  swallowed by `except (SyntaxError, OSError): pass`.
- T6 (Docker hardening): base images must now be pinned to an @sha256
  digest; a specific tag like python:3.12 is mutable and is now correctly
  flagged as unpinned, not just missing tags or :latest.
- T2a (API surface): no config mechanism for enforcing local-only access
  exists in this codebase today (app.py hardcodes host='0.0.0.0'), so the
  "environment-aware" check as described isn't buildable without adding
  new config infrastructure -- out of scope here. Applied the achievable
  part: upgraded from INFO to WARNING, since enforcement can never
  currently be confirmed.
- T1a (zip-slip): replaced the whole-file substring check with an AST
  walk that finds every extract()/extractall() call and confirms an
  is_relative_to() guard + "Zip-slip detected" log precede it in the same
  function. Verified it still passes on the real store_manager.py (both
  the per-member and validate-then-bulk-extract call sites) and correctly
  flags a synthetic unguarded extractall().
- T3a (hardcoded secrets): violation details no longer include the
  matched credential text -- only file, line, pattern type, and a
  redacted SHA-256 fingerprint, so a real finding doesn't get published
  into CI logs/artifacts/PR comments with wider exposure than the
  original leak. Verified with a synthetic secret that no raw content
  reaches the output.

Validated: all four files compile; bandit scans all three scripts clean
(2 legitimate targeted suppressions, 0 unaddressed findings); each new/
changed code path exercised directly (alias evasion, unmatched --plugin,
missing/malformed/valid-empty artifacts, digest-pinning, zip-slip
guard/no-guard, secret redaction); full audit_plugins.py -> prove_security.py
-> generate_report.py pipeline run end-to-end producing a correct report.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KEZK1P1Q1fu5pcuVrkrCFZ

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
scripts/audit_plugins.py (1)

80-117: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Support from-imports for subprocess and os.system.

The current implementation only correctly handles direct names for eval/exec and simple aliased imports (like import subprocess as sp) for subprocess and os. It continues to miss from-import statements (e.g., from subprocess import run) because the target becomes an ast.Name, which skips the isinstance(node.func, ast.Attribute) block entirely.

Consolidate the call path resolution to uniformly check both imported names and attribute accesses against all target rules.

🛠️ Proposed fix to unify target resolution
-        # eval() / exec() / compile() — arbitrary code execution, including
-        # aliased or from-imported forms (from builtins import eval as e; e(...))
-        if isinstance(node.func, ast.Name):
-            target = self._resolve(node.func.id).rsplit(".", 1)[-1]
-            if target == "eval":
-                self._add(node, "CRITICAL", "PLUGIN-001",
-                          "eval() call — arbitrary code execution risk")
-            elif target == "exec":
-                self._add(node, "CRITICAL", "PLUGIN-002",
-                          "exec() call — arbitrary code execution risk")
-            elif target == "compile":
-                self._add(node, "WARNING", "PLUGIN-003",
-                          "compile() call — dynamic code compilation")
-
-        # subprocess.*(shell=True) / os.system(), including aliased imports
-        # (import subprocess as sp; import os as o)
-        if isinstance(node.func, ast.Attribute) and isinstance(node.func.value, ast.Name):
-            base = self._resolve(node.func.value.id)
-
-            is_subprocess = (
-                base == "subprocess" and
-                node.func.attr in ("run", "call", "Popen", "check_call", "check_output")
-            )
-            if is_subprocess:
-                for kw in node.keywords:
-                    if (kw.arg == "shell" and
-                            isinstance(kw.value, ast.Constant) and
-                            kw.value.value is True):
-                        self._add(node, "WARNING", "PLUGIN-004",
-                                  f"subprocess.{node.func.attr}(shell=True) — "
-                                  f"shell injection risk if args include user input")
-
-            # os.system() — shell execution
-            is_os_system = base == "os" and node.func.attr == "system"
-            if is_os_system:
-                self._add(node, "WARNING", "PLUGIN-005",
-                          "os.system() call — prefer subprocess with list args")
+        target_path = ""
+        if isinstance(node.func, ast.Name):
+            target_path = self._resolve(node.func.id)
+        elif isinstance(node.func, ast.Attribute) and isinstance(node.func.value, ast.Name):
+            target_path = f"{self._resolve(node.func.value.id)}.{node.func.attr}"
+
+        func_name = target_path.rsplit(".", 1)[-1]
+
+        # eval() / exec() / compile() — arbitrary code execution
+        if func_name == "eval":
+            self._add(node, "CRITICAL", "PLUGIN-001",
+                      "eval() call — arbitrary code execution risk")
+        elif func_name == "exec":
+            self._add(node, "CRITICAL", "PLUGIN-002",
+                      "exec() call — arbitrary code execution risk")
+        elif func_name == "compile":
+            self._add(node, "WARNING", "PLUGIN-003",
+                      "compile() call — dynamic code compilation")
+
+        # subprocess.*(shell=True) / os.system()
+        is_subprocess = target_path.startswith("subprocess.") and func_name in (
+            "run", "call", "Popen", "check_call", "check_output"
+        )
+        if is_subprocess:
+            for kw in node.keywords:
+                if (kw.arg == "shell" and
+                        isinstance(kw.value, ast.Constant) and
+                        kw.value.value is True):
+                    self._add(node, "WARNING", "PLUGIN-004",
+                              f"{target_path}(shell=True) — "
+                              f"shell injection risk if args include user input")
+
+        if target_path == "os.system":
+            self._add(node, "WARNING", "PLUGIN-005",
+                      "os.system() call — prefer subprocess with list args")
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/audit_plugins.py` around lines 80 - 117, Update visit_Call to resolve
both ast.Name and ast.Attribute callees through the existing import-resolution
mechanism, so from-imported subprocess functions and os.system aliases are
evaluated alongside direct and module-attribute calls. Consolidate rule matching
around the resolved target while preserving the existing PLUGIN-001 through
PLUGIN-005 classifications and shell=True requirement.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@scripts/generate_report.py`:
- Line 74: Update the value parameter annotation in _md_sanitize_cell to specify
the expected input type, while preserving its existing return type and
sanitization behavior.

In `@scripts/prove_security.py`:
- Around line 503-509: Update the FROM-line parsing around from_lines to rename
the comprehension variable l to line, then resolve the image token after any
optional --platform= flag before checking for `@sha256`:. Preserve the existing
issue reporting and ensure platform flags do not trigger false positives.

---

Outside diff comments:
In `@scripts/audit_plugins.py`:
- Around line 80-117: Update visit_Call to resolve both ast.Name and
ast.Attribute callees through the existing import-resolution mechanism, so
from-imported subprocess functions and os.system aliases are evaluated alongside
direct and module-attribute calls. Consolidate rule matching around the resolved
target while preserving the existing PLUGIN-001 through PLUGIN-005
classifications and shell=True requirement.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 7a6d04b6-e6b8-48c1-9a30-f52a03e8f929

📥 Commits

Reviewing files that changed from the base of the PR and between acaa11f and d2c500e.

📒 Files selected for processing (4)
  • bandit.yaml
  • scripts/audit_plugins.py
  • scripts/generate_report.py
  • scripts/prove_security.py
💤 Files with no reviewable changes (1)
  • bandit.yaml

Comment thread scripts/generate_report.py Outdated
Comment thread scripts/prove_security.py Outdated
scripts/generate_report.py:
- Added an explicit `object` type annotation to _md_sanitize_cell's value
  parameter -- it deliberately accepts any stringifiable value (calls
  str(value) unconditionally), so `object` reflects its actual contract
  more accurately than leaving it untyped.

scripts/prove_security.py:
- Dockerfile FROM-line parsing: renamed the comprehension variable `l` to
  `line` (ambiguous single-letter name). More importantly, fixed a real
  false-positive: `FROM --platform=<platform> <image>` was reading the
  --platform= flag itself as the image token, so a properly digest-pinned
  image behind a platform flag was incorrectly reported as unpinned.
  Verified against platform+digest, platform+tag-only, and digest+AS-alias
  Dockerfiles.

scripts/audit_plugins.py:
- Consolidated visit_Call's dangerous-API detection: previously, alias
  resolution only covered ast.Name calls for eval/exec/compile and
  ast.Attribute calls for subprocess/os.system, missing from-imported
  subprocess/os functions called as bare names (from subprocess import
  run as prun; prun(cmd, shell=True) or from os import system as s;
  s(cmd)). Added _resolve_call_target() to resolve both call shapes to a
  single fully-qualified target, then run all five PLUGIN-00x checks
  against that one resolved value. Verified against 10 evasion
  combinations (from-import aliases, direct/attribute calls, aliased
  module imports) and confirmed zero false positives on benign os/
  subprocess usage without shell=True.

Validated: all three files compile, bandit scans clean (same 2 legitimate
suppressions as before, 0 new findings), audit_plugins.py/prove_security.py
re-run against the real repo with no regressions from the prior fix pass.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KEZK1P1Q1fu5pcuVrkrCFZ
@ChuckBuilds
ChuckBuilds merged commit 66f9950 into main Jul 15, 2026
8 checks passed
@ChuckBuilds
ChuckBuilds deleted the chore/security-audit-tooling branch July 15, 2026 18:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant