Compare commits

...

2 Commits

Author SHA1 Message Date
Abhijit Balaji 11eadac9af test(core): update scheduler policy tests for new argsPattern format 2026-05-05 14:41:40 -07:00
Abhijit Balaji 5290c51451 fix(core): resolve policy engine bugs affecting tool approvals
Fixes #24772, #16970.

- Fixes regex null-byte mismatch in `buildParamArgsPattern` so "always allow" works.
- Removes sandbox requirement from `shouldDowngradeForRedirection` so YOLO/AUTO_EDIT modes behave correctly.
- Enhances `stripShellWrapper` to recognize generic shell scripts (e.g., `sh script.sh`).
2026-05-05 14:41:01 -07:00
4 changed files with 23 additions and 13 deletions
+8 -6
View File
@@ -288,12 +288,14 @@ export class PolicyEngine {
if (allowRedirection) return false;
if (!hasRedirection(command)) return false;
// Do not downgrade (do not ask user) if in AUTO_EDIT or YOLO mode.
// These modes trust the agent's actions (YOLO) or specific task (AUTO_EDIT).
if (
this.approvalMode === ApprovalMode.AUTO_EDIT ||
this.approvalMode === ApprovalMode.YOLO
) {
// In YOLO mode, never downgrade.
if (this.approvalMode === ApprovalMode.YOLO) {
return false;
}
// In AUTO_EDIT mode, only bypass downgrade if sandboxing is enabled.
const sandboxEnabled = !(this.sandboxManager instanceof NoopSandboxManager);
if (this.approvalMode === ApprovalMode.AUTO_EDIT && sandboxEnabled) {
return false;
}
+2 -2
View File
@@ -102,10 +102,10 @@ export function buildParamArgsPattern(
value: unknown,
): string {
const encodedValue = JSON.stringify(value);
// We wrap the JSON string in escapeRegex and prepend/append \\0 to explicitly
// We wrap the JSON string in escapeRegex and prepend/append \\x00 to explicitly
// match top-level JSON properties generated by stableStringify, preventing
// argument injection bypass attacks.
return `\\\\0${escapeRegex(`"${paramName}":${encodedValue}`)}\\\\0`;
return `\\x00${escapeRegex(`"${paramName}":${encodedValue}`)}\\x00`;
}
/**
+1 -1
View File
@@ -770,7 +770,7 @@ describe('policy.ts', () => {
expect.objectContaining({
toolName: 'write_file',
argsPattern:
'\\\\0' + escapeRegex('"file_path":"src/foo.ts"') + '\\\\0',
'\\x00' + escapeRegex('"file_path":"src/foo.ts"') + '\\x00',
}),
);
});
+12 -4
View File
@@ -809,11 +809,11 @@ export function getCommandRoots(command: string): string[] {
}
export function stripShellWrapper(command: string): string {
const pattern =
const cFlagPattern =
/^\s*(?:(?:(?:\S+\/)?(?:sh|bash|zsh))\s+-c|cmd\.exe\s+\/c|powershell(?:\.exe)?\s+(?:-NoProfile\s+)?-Command|pwsh(?:\.exe)?\s+(?:-NoProfile\s+)?-Command)\s+/i;
const match = command.match(pattern);
if (match) {
let newCommand = command.substring(match[0].length).trim();
const cFlagMatch = command.match(cFlagPattern);
if (cFlagMatch) {
let newCommand = command.substring(cFlagMatch[0].length).trim();
if (
(newCommand.startsWith('"') && newCommand.endsWith('"')) ||
(newCommand.startsWith("'") && newCommand.endsWith("'"))
@@ -822,6 +822,14 @@ export function stripShellWrapper(command: string): string {
}
return newCommand;
}
const scriptPattern =
/^\s*(?:(?:\S+\/)?(?:sh|bash|zsh))\s+([a-zA-Z0-9_\-./]+\.sh)\s*$/i;
const scriptMatch = command.match(scriptPattern);
if (scriptMatch) {
return scriptMatch[1];
}
return command.trim();
}