-
Notifications
You must be signed in to change notification settings - Fork 2.4k
fix(core): strip additional dangerous interpreter rules #4371
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. Weβll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 4 commits
51e7566
2be2e0b
9a62aa7
d16cab2
cb9661d
1359fe5
e34ee85
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -158,6 +158,46 @@ describe('isDangerousBashRule', () => { | |||||||||||||||||||||||||||||
| expect(isDangerousBashRule(bashRule(interp))).toBe(true); | ||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| it.each(['tsx -e *', 'ssh prod-host -- *', 'bunx -p dangerous-pkg *'])( | ||||||||||||||||||||||||||||||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [Suggestion] Missing test coverage for two code paths with the new tokens:
it.each(['tsx:*', 'ssh:*', 'bunx:*', 'cmd:*'])(
'flags colon-form wildcard %s for new tokens',
(s) => {
expect(isDangerousBashRule(bashRule(s))).toBe(true);
},
);
it.each(['python.exe', 'node.exe', 'tsx.exe', 'bun.exe'])(
'flags bare-name .exe for non-listed interpreters %s',
(s) => {
expect(isDangerousBashRule(bashRule(s))).toBe(true);
},
);β qwen3.7-max via Qwen Code /review |
||||||||||||||||||||||||||||||
| 'flags new interpreter, remote shell, or runner wildcard %s', | ||||||||||||||||||||||||||||||
| (s) => { | ||||||||||||||||||||||||||||||
| expect(isDangerousBashRule(bashRule(s))).toBe(true); | ||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| it.each([ | ||||||||||||||||||||||||||||||
| 'tsx', | ||||||||||||||||||||||||||||||
| 'ssh', | ||||||||||||||||||||||||||||||
| 'bunx', | ||||||||||||||||||||||||||||||
| 'bash.exe', | ||||||||||||||||||||||||||||||
| 'cmd', | ||||||||||||||||||||||||||||||
| 'cmd.exe', | ||||||||||||||||||||||||||||||
| 'pwsh.exe', | ||||||||||||||||||||||||||||||
| 'powershell.exe', | ||||||||||||||||||||||||||||||
| ])('flags new interpreter, remote shell, or runner bare name %s', (s) => { | ||||||||||||||||||||||||||||||
| expect(isDangerousBashRule(bashRule(s))).toBe(true); | ||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| it.each([ | ||||||||||||||||||||||||||||||
| 'python.exe -c *', | ||||||||||||||||||||||||||||||
| 'node.exe -e *', | ||||||||||||||||||||||||||||||
| 'tsx.exe -e *', | ||||||||||||||||||||||||||||||
| 'bunx.exe -p dangerous-pkg *', | ||||||||||||||||||||||||||||||
| 'C:\\Python\\python.exe -c *', | ||||||||||||||||||||||||||||||
| ])('flags Windows executable suffix wildcard %s', (s) => { | ||||||||||||||||||||||||||||||
| expect(isDangerousBashRule(bashRule(s))).toBe(true); | ||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| it.each([ | ||||||||||||||||||||||||||||||
| 'cmd /c *', | ||||||||||||||||||||||||||||||
| 'cmd.exe /c *', | ||||||||||||||||||||||||||||||
| 'bash.exe -c *', | ||||||||||||||||||||||||||||||
| 'powershell.exe -Command *', | ||||||||||||||||||||||||||||||
| 'pwsh.exe -Command *', | ||||||||||||||||||||||||||||||
| ])('flags Windows shell wildcard %s', (s) => { | ||||||||||||||||||||||||||||||
| expect(isDangerousBashRule(bashRule(s))).toBe(true); | ||||||||||||||||||||||||||||||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [Suggestion] Missing negative tests for concrete commands using new tokens β The existing test file has negative tests verifying that concrete commands like it('does NOT flag concrete commands using new interpreters', () => {
expect(isDangerousBashRule(bashRule('tsx script.tsx'))).toBe(false);
expect(isDangerousBashRule(bashRule('ssh prod-host -- ls'))).toBe(false);
expect(isDangerousBashRule(bashRule('cmd /c script.bat'))).toBe(false);
expect(isDangerousBashRule(bashRule('powershell.exe -File deploy.ps1'))).toBe(false);
});β qwen-latest-series-invite-beta-v38 via Qwen Code /review |
||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| it.each([ | ||||||||||||||||||||||||||||||
| 'bun run *', | ||||||||||||||||||||||||||||||
| 'deno run *', | ||||||||||||||||||||||||||||||
|
|
@@ -176,6 +216,20 @@ describe('isDangerousBashRule', () => { | |||||||||||||||||||||||||||||
| expect(isDangerousBashRule(bashRule(s))).toBe(true); | ||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| it.each([ | ||||||||||||||||||||||||||||||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [Suggestion] Missing negative test for Windows absolute-path + concrete command β The concrete-command negative tests cover bare
Suggested change
β qwen3.7-max via Qwen Code /review |
||||||||||||||||||||||||||||||
| 'tsx script.tsx', | ||||||||||||||||||||||||||||||
| 'ssh prod-host -- ls', | ||||||||||||||||||||||||||||||
| 'cmd /c script.bat', | ||||||||||||||||||||||||||||||
| 'cmd.exe /c script.bat', | ||||||||||||||||||||||||||||||
| 'pwsh.exe -File script.ps1', | ||||||||||||||||||||||||||||||
| 'powershell.exe -File script.ps1', | ||||||||||||||||||||||||||||||
| 'python.exe script.py', | ||||||||||||||||||||||||||||||
| 'node.exe script.js', | ||||||||||||||||||||||||||||||
| 'bunx eslint .', | ||||||||||||||||||||||||||||||
| ])('does NOT flag concrete commands using new tokens %s', (s) => { | ||||||||||||||||||||||||||||||
| expect(isDangerousBashRule(bashRule(s))).toBe(false); | ||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| it('flags Monitor allow rules with the same interpreter logic', () => { | ||||||||||||||||||||||||||||||
| // Monitor is a long-running shell-command runner; broad allow rules | ||||||||||||||||||||||||||||||
| // on it bypass the AUTO classifier just like Bash(...) ones. | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -18,12 +18,14 @@ import type { PermissionRule } from './types.js'; | |||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||
| * Tokens that, when used as the leading command of a Bash allow rule, let the | ||||||||||||||||||||||||||||
| * model execute arbitrary code under the AUTO classifier's nose. Covers | ||||||||||||||||||||||||||||
| * shell interpreters, scripting-language interpreters, and build/package | ||||||||||||||||||||||||||||
| * tools that themselves run arbitrary scripts (`cargo run`, `npm run`, β¦). | ||||||||||||||||||||||||||||
| * Mirrors and extends ClaudeCode's `DANGEROUS_BASH_PATTERNS`. | ||||||||||||||||||||||||||||
| * Unix and Windows shell interpreters, scripting-language interpreters, | ||||||||||||||||||||||||||||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [Suggestion] The JSDoc claims this list "Mirrors Claude Code's shell and scripting-interpreter checks" but the referenced upstream identifier ( Consider replacing with a self-contained description: * Originally inspired by Claude Code's dangerous-command checks.
* Maintained independently β add any token that lets the leading
* command of an allow rule execute arbitrary code.β qwen-latest-series-invite-beta-v38 via Qwen Code /review |
||||||||||||||||||||||||||||
| * remote shells, and build/package tools that themselves run arbitrary | ||||||||||||||||||||||||||||
| * scripts (`cargo run`, `npm run`, β¦). The exact token set is intentionally | ||||||||||||||||||||||||||||
| * self-contained so AUTO-mode stripping does not depend on an external | ||||||||||||||||||||||||||||
| * upstream identifier. | ||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||
| const DANGEROUS_BASH_INTERPRETERS: readonly string[] = Object.freeze([ | ||||||||||||||||||||||||||||
| // Shells | ||||||||||||||||||||||||||||
| // Unix shells | ||||||||||||||||||||||||||||
| 'bash', | ||||||||||||||||||||||||||||
| 'sh', | ||||||||||||||||||||||||||||
| 'zsh', | ||||||||||||||||||||||||||||
|
|
@@ -32,14 +34,21 @@ const DANGEROUS_BASH_INTERPRETERS: readonly string[] = Object.freeze([ | |||||||||||||||||||||||||||
| 'tcsh', | ||||||||||||||||||||||||||||
| 'dash', | ||||||||||||||||||||||||||||
| 'ksh', | ||||||||||||||||||||||||||||
| // Windows shells | ||||||||||||||||||||||||||||
| 'bash.exe', | ||||||||||||||||||||||||||||
| 'cmd', | ||||||||||||||||||||||||||||
| 'cmd.exe', | ||||||||||||||||||||||||||||
| 'pwsh', | ||||||||||||||||||||||||||||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [Suggestion] Dead Consider removing the four redundant entries and adding a note: // Windows shells (bare names only β .exe variants are matched
// by stripWindowsExecutableSuffix in isInterpreterToken)
'cmd',
'pwsh',
'powershell',β qwen3.7-max via Qwen Code /review |
||||||||||||||||||||||||||||
| 'pwsh.exe', | ||||||||||||||||||||||||||||
| 'powershell', | ||||||||||||||||||||||||||||
| 'powershell.exe', | ||||||||||||||||||||||||||||
| // Scripting-language interpreters | ||||||||||||||||||||||||||||
| 'python', | ||||||||||||||||||||||||||||
| 'python3', | ||||||||||||||||||||||||||||
| 'python2', | ||||||||||||||||||||||||||||
| 'node', | ||||||||||||||||||||||||||||
| 'deno', | ||||||||||||||||||||||||||||
| 'tsx', | ||||||||||||||||||||||||||||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [Critical] Consider stripping a trailing let segment = (beforeColon ?? '').split('/').pop() ?? '';
if (segment.toLowerCase().endsWith('.exe')) {
segment = segment.slice(0, -4);
}
return DANGEROUS_BASH_INTERPRETERS.includes(segment);β qwen-latest-series-invite-beta-v38 via Qwen Code /review |
||||||||||||||||||||||||||||
| 'bun', | ||||||||||||||||||||||||||||
| 'ruby', | ||||||||||||||||||||||||||||
| 'perl', | ||||||||||||||||||||||||||||
|
|
@@ -69,10 +78,13 @@ const DANGEROUS_BASH_INTERPRETERS: readonly string[] = Object.freeze([ | |||||||||||||||||||||||||||
| // that without this list would be the cleanest way to bypass the | ||||||||||||||||||||||||||||
| // classifier in AUTO mode. | ||||||||||||||||||||||||||||
| 'npx', | ||||||||||||||||||||||||||||
| 'bunx', | ||||||||||||||||||||||||||||
| 'pnpx', | ||||||||||||||||||||||||||||
| 'uvx', | ||||||||||||||||||||||||||||
| 'pipx', | ||||||||||||||||||||||||||||
| 'dlx', | ||||||||||||||||||||||||||||
| // Remote shells | ||||||||||||||||||||||||||||
| 'ssh', | ||||||||||||||||||||||||||||
| // Generic eval-y commands | ||||||||||||||||||||||||||||
| 'eval', | ||||||||||||||||||||||||||||
| 'exec', | ||||||||||||||||||||||||||||
|
|
@@ -96,6 +108,7 @@ const SHELL_LIKE_TOOLS: readonly string[] = Object.freeze([ | |||||||||||||||||||||||||||
| * - absolute-path forms (`/usr/bin/python3` β trailing segment `python3`) | ||||||||||||||||||||||||||||
| * - trailing-wildcard forms (`python3*`) | ||||||||||||||||||||||||||||
| * - colon form (`python:`) | ||||||||||||||||||||||||||||
| * - Windows executable suffixes (`python.exe`) | ||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||
| function isInterpreterToken(rawToken: string): boolean { | ||||||||||||||||||||||||||||
| if (!rawToken) return false; | ||||||||||||||||||||||||||||
|
|
@@ -108,10 +121,18 @@ function isInterpreterToken(rawToken: string): boolean { | |||||||||||||||||||||||||||
| end--; | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
| const noWildcard = rawToken.slice(0, end); | ||||||||||||||||||||||||||||
| const beforeColon = noWildcard.split(':')[0]; | ||||||||||||||||||||||||||||
| const beforeColon = /^[a-z]:[\\/]/i.test(noWildcard) | ||||||||||||||||||||||||||||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [Critical] Windows drive-letter + matcher-colon bypass (Layer 1 of 2) β When content starts with a drive letter (e.g. Verified:
Suggested change
β qwen3.7-max via Qwen Code /review |
||||||||||||||||||||||||||||
| ? noWildcard | ||||||||||||||||||||||||||||
| : noWildcard.split(':')[0]; | ||||||||||||||||||||||||||||
| // Last path segment so `/usr/bin/python3` β `python3` | ||||||||||||||||||||||||||||
| const lastSegment = (beforeColon ?? '').split('/').pop() ?? ''; | ||||||||||||||||||||||||||||
| return DANGEROUS_BASH_INTERPRETERS.includes(lastSegment); | ||||||||||||||||||||||||||||
| const lastSegment = (beforeColon ?? '').split(/[\\/]/).pop() ?? ''; | ||||||||||||||||||||||||||||
| const withoutExe = lastSegment.endsWith('.exe') | ||||||||||||||||||||||||||||
| ? lastSegment.slice(0, -'.exe'.length) | ||||||||||||||||||||||||||||
| : lastSegment; | ||||||||||||||||||||||||||||
| return ( | ||||||||||||||||||||||||||||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [Suggestion]
The current list avoids this by duplicating every entry in both forms ( Consider one of: (a) make the fallback bidirectional (check both β DeepSeek/deepseek-v4-pro via Qwen Code /review |
||||||||||||||||||||||||||||
| DANGEROUS_BASH_INTERPRETERS.includes(lastSegment) || | ||||||||||||||||||||||||||||
| DANGEROUS_BASH_INTERPRETERS.includes(withoutExe) | ||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||
|
|
@@ -140,12 +161,12 @@ export function isDangerousBashRule(rule: PermissionRule): boolean { | |||||||||||||||||||||||||||
| // dangerous when it appears as the first token of either form | ||||||||||||||||||||||||||||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [Suggestion] Stale comment β says "Treat both whitespace and
Suggested change
β qwen3.7-max via Qwen Code /review |
||||||||||||||||||||||||||||
| // (`python -c *` or `python:*`). For colon-form, the part after `:` is | ||||||||||||||||||||||||||||
| // the specifier β we'll separately check whether it's concrete below. | ||||||||||||||||||||||||||||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [Suggestion] Windows paths with spaces bypass detection β Verified: Consider extracting the leading path up to (and including) the β qwen3.7-max via Qwen Code /review |
||||||||||||||||||||||||||||
| const firstToken = content.split(/[\s:]/)[0] ?? ''; | ||||||||||||||||||||||||||||
| const firstToken = content.split(/\s/)[0] ?? ''; | ||||||||||||||||||||||||||||
| if (!isInterpreterToken(firstToken)) return false; | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [Critical] Windows drive-letter + matcher-colon bypass (Layer 2 of 2) β The same drive-letter regex negates the colon check here, setting Even if Layer 1 (
Suggested change
β qwen3.7-max via Qwen Code /review |
||||||||||||||||||||||||||||
| // Bare interpreter name (`python`, `/usr/bin/python3`) β caller decides | ||||||||||||||||||||||||||||
| // what to do, classifier never sees it. Dangerous. | ||||||||||||||||||||||||||||
| if (firstToken === content) return true; | ||||||||||||||||||||||||||||
| if (firstToken === content && !content.includes(':')) return true; | ||||||||||||||||||||||||||||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [Critical] Windows drive-letter colon bypass β Bare Windows interpreter paths like Root cause: Trace for Compare Unix: Also: the
Suggested change
β qwen3.7-max via Qwen Code /review |
||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| // Wildcard anywhere paired with an interpreter defeats the classifier: | ||||||||||||||||||||||||||||
| // `python *`, `python -c *`, `bun run *`, `/usr/bin/python3 *`, | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[Suggestion] Missing bare-name tests for the 8 new tokens β The new tokens are only tested in wildcard forms (e.g.,
tsx -e *,cmd /c *). The bare-name branch (firstToken === contentatdangerousRules.ts:163) is a distinct code path that is not exercised for any of:tsx,ssh,bunx,bash.exe,cmd,cmd.exe,pwsh.exe,powershell.exe. Every pre-existing interpreter token has bare-name coverage.β qwen-latest-series-invite-beta-v38 via Qwen Code /review