fix(policy): resolve type errors and add test for isSensitive plumbing

This commit is contained in:
Spencer Tang
2026-02-27 18:55:23 -05:00
parent 99bc45b689
commit e4d1c07294
10 changed files with 2310 additions and 392 deletions
@@ -84,6 +84,7 @@ describe('SubAgentInvocation', () => {
params: {},
getDescription: vi.fn(),
toolLocations: vi.fn(),
isSensitive: false,
};
MockSubagentToolWrapper.prototype.build = vi
-14
View File
@@ -11,20 +11,6 @@ export function escapeRegex(text: string): string {
return text.replace(/[-[\]{}()*+?.,\\^$|#\s"]/g, '\\$&');
}
/**
* Escapes a string for use in a regular expression that matches a JSON-stringified value.
*
* This is necessary because some characters (like backslashes and quotes) are
* escaped twice in the final JSON string representation used for policy matching.
*/
export function escapeJsonRegex(text: string): string {
// 1. Get the JSON-escaped version of the string (e.g. C:\foo -> C:\\foo)
// 2. Remove the surrounding quotes
const jsonEscaped = JSON.stringify(text).slice(1, -1);
// 3. Regex-escape the result (e.g. C:\\foo -> C:\\\\foo)
return escapeRegex(jsonEscaped);
}
/**
* Basic validation for regular expressions to prevent common ReDoS patterns.
* This is a heuristic check and not a substitute for a full ReDoS scanner.
+1
View File
@@ -293,6 +293,7 @@ describe('AskUserTool', () => {
getDescription: vi.fn().mockReturnValue(''),
toolLocations: vi.fn().mockReturnValue([]),
shouldConfirmExecute: vi.fn().mockResolvedValue(false),
isSensitive: false,
};
const buildSpy = vi.spyOn(tool, 'build').mockReturnValue(mockInvocation);
+15
View File
@@ -202,6 +202,21 @@ describe('EditTool', () => {
fs.rmSync(tempDir, { recursive: true, force: true });
});
it('should be marked as sensitive and pass the flag to its invocations', () => {
// Check the tool definition itself
expect(tool.isSensitive).toBe(true);
// Build an invocation and check the instance
const params: EditToolParams = {
file_path: path.join(rootDir, 'test.txt'),
instruction: 'An instruction',
old_string: 'old',
new_string: 'new',
};
const invocation = tool.build(params);
expect(invocation.isSensitive).toBe(true);
});
describe('applyReplacement', () => {
it('should return newString if isNewFile is true', () => {
expect(applyReplacement(null, 'old', 'new', true)).toBe('new');
+1
View File
@@ -16,6 +16,7 @@ class TestToolInvocation implements ToolInvocation<object, ToolResult> {
constructor(
readonly params: object,
private readonly executeFn: () => Promise<ToolResult>,
readonly isSensitive: boolean = false,
) {}
getDescription(): string {
+5
View File
@@ -33,6 +33,11 @@ export interface ToolInvocation<
*/
params: TParams;
/**
* Whether the tool is sensitive and requires specific policy approvals.
*/
isSensitive: boolean;
/**
* Gets a pre-execution description of the tool operation.
*