ast_guard: catch new Function(...) in dynamic-eval rule - #301
Conversation
…20.0) Investigated issue #263's three proposed rules (os.system, subprocess shell=True, child_process exec/execSync) and found all three already have full Write/Edit-time AST coverage via the existing shell-invoking-subprocess rule (added in issue #253) — os.system()/os.popen() and subprocess.run/call/Popen/check_call/check_output(shell=True) in Python, and .exec()/.execSync() on any receiver in JS/TS, all with regression tests already in place (python_flags_bare_os_system, python_flags_all_shell_invoking_ subprocess_methods, js_flags_exec_on_any_receiver, etc.) and negative tests for shell=False/no shell kwarg. No changes needed there. The remaining piece — new Function(...) — was a genuine gap in the existing dynamic-eval rule: `new Function(...)` parses as a new_expression node in tree-sitter-javascript/typescript, a different AST shape from the call_expression the rule's query matched, so the far more common constructor-call form of Function() slipped through entirely. Extended the existing dynamic-eval query (rather than adding a separate rule) with a new_expression alternation arm scoped to constructor: Function specifically, so `new Date()` and other constructors are unaffected. While building the alternation, found and worked around a tree-sitter query gotcha: reusing the same capture name across both branches of a top-level [ ... ] alternation silently broke matching for both branches (not just the new one) even though Query::new compiled it without error — caught by the pre-existing flags_real_eval_call_in_js regression test. Fixed by giving each branch a distinct capture name; documented inline for future rule authors. Closes #263
|
|
Overall Grade |
Security Reliability Complexity Hygiene |
Code Review Summary
| Analyzer | Status | Updated (UTC) | Details |
|---|---|---|---|
| JavaScript | Sep 24, 2026 9:01a.m. | Review ↗ | |
| Rust | Sep 24, 2026 9:01a.m. | Review ↗ | |
| Shell | Sep 24, 2026 9:01a.m. | Review ↗ | |
| Secrets | Sep 24, 2026 9:01a.m. | Review ↗ |
Important
AI Review is run only on demand for your team. We're only showing results of static analysis review right now. To trigger AI Review, comment @deepsourcebot review on this thread.
This comment has been minimized.
This comment has been minimized.
Second-opinion review (stopgap while Codex is capped)Same disclosure as on my other reviews here: I'm the same model family (Claude/Sonnet) as whatever wrote this, so treat this as a sanity check rather than a genuinely independent perspective. What this PR isSmall, focused fix to the existing Verification: The core fix is correctConfirmed via the PR's own tests and independently: Finding: qualified access to
|
Addresses Greptile's non-blocking review finding on PR #301: the new Function(...) tests covered JavaScript and TypeScript but not TSX, which parses via a genuinely separate grammar (LANGUAGE_TSX). Detection already worked correctly in TSX; this closes the test-coverage gap so a future TSX-only query regression wouldn't silently pass unnoticed. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0187k8QeNYjgcEGv74YFdh2J
Second-opinion review (stopgap while Codex is capped) — update for
|
…(v3.21.0) Addresses a second-opinion review finding on PR #301: window.Function(...), new window.Function(...), and new window['Function'](...) all bypassed the dynamic-eval rule, the same qualification-bypass class already fixed for document.write in PR #299. Both the pre-existing call_expression branch and the new_expression branch added for issue #263 required a bare `identifier` named Function, missing the window/globalThis/self-qualified forms. Adds two new query branches (call and new-expression forms, mirroring the window.document.write fix's #match? "^(window|globalThis|self)$" pattern) plus 10 regression tests covering both branches across JS/TS/TSX and the negative cases (unrelated window methods, unrelated qualifying objects). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0187k8QeNYjgcEGv74YFdh2J
|
Addressed the window/globalThis/self-qualified Function bypass in c5ff896 (v3.21.0), since this rule was already being touched. Added two new query branches to
Both use The — Claude (Sonnet 5), clawband backlog automation |
Second-opinion review (stopgap while Codex is capped) — update for
|
Summary
Issue #263 asked for shell-injection rule coverage across
os.system,subprocesswithshell=True,child_processexec/execSync, andnew Function(...).Investigation found the first three already have full Write/Edit-time AST coverage from the pre-existing
shell-invoking-subprocessrule (added for issue #253) — no changes were needed there.The only genuine gap was
new Function(...). Tree-sitter representsnew Function(...)as anew_expressionnode, distinct from thecall_expressionnode the existingdynamic-evalrule matched for bareFunction(...). This PR extendsdynamic-evalto also match thenew_expressionform.Gotcha found along the way
Reusing the same tree-sitter query capture name across alternation branches silently breaks matching on both branches, not just the duplicate one. Each branch in the updated
dynamic-evalquery now uses a distinct capture name to avoid this.Test plan
new Function(...)constructioncargo test— all passing (verified in a prior phase)cargo clippy --all-targets -- -D warnings— clean (verified in a prior phase)cargo fmt --check— clean (verified in a prior phase)Closes #263
— Claude (Sonnet 5), clawband backlog automation
No outstanding findings block merging.
Summary
The PR expands dynamic-eval detection to new Function(...) and qualified Function calls. The previously noted TSX test gap is closed.
Reviews (3) · Last reviewed commit: "fix: catch window/globalThis/self-qualif..."