d89a19192443e21d18441195fce5c7e7853eb089

Author
TheEdgeOfRage <git@theedgeofrage.com>
Committer
TheEdgeOfRage <git@theedgeofrage.com>
Date

Message

Allow base64 commands

Diff

 1diff --git a/src/core/rules.ts b/src/core/rules.ts
 2index d52252ebc1307a47cdd4e7b566c0d51b26abe1ea..050896f9812f5ec5903a6dcb60b14ca622ab74d7 100644
 3--- a/src/core/rules.ts
 4+++ b/src/core/rules.ts
 5@@ -1,7 +1,7 @@
 6 import { DECISION_CATEGORIES } from "./types";
 7 
 8 // Bump to invalidate all cached decisions when rules change.
 9-export const POLICY_VERSION = 29;
10+export const POLICY_VERSION = 30;
11 
12 // Trailing stderr redirections that are safe to strip before pattern matching.
13 // `2>&1` and `2>/dev/null` have no security implication but would otherwise
14@@ -52,6 +52,8 @@ export const HARD_ALLOW_PATTERNS: RegExp[] = [
15   /^ls(?:\s+-[A-Za-z]+)*(?:\s+(?!.*\/\.)(?!.*\.\.)\/?[A-Za-z0-9][A-Za-z0-9._/-]*)*\s*$/,
16   // cat with relative paths only
17   /^cat\s+(?!.*\/\.)(?!.*\.\.)[A-Za-z0-9][A-Za-z0-9._/-]*(?:\s+(?!.*\/\.)(?!.*\.\.)[A-Za-z0-9][A-Za-z0-9._/-]*)*\s*$/,
18+  // base64 with relative paths only
19+  /^base64\s+(?!.*\/\.)(?!.*\.\.)[A-Za-z0-9][A-Za-z0-9._/-]*(?:\s+(?!.*\/\.)(?!.*\.\.)[A-Za-z0-9][A-Za-z0-9._/-]*)*\s*$/,
20   // head/tail with relative paths and line limits
21   /^(head|tail)(?:\s+-(?:n\s*)?\d+)?(?:\s+(?!.*\/\.)(?!.*\.\.)[A-Za-z0-9][A-Za-z0-9._/-]*)*\s*$/,
22   // find read-only on relative paths
23diff --git a/test/pi/extension.test.ts b/test/pi/extension.test.ts
24index 3c0e1bfefd6aa7143c3dad3e6c1db45b0c9bf505..ab482509567c6a3c5d1ddf903b47520d87dc37dd 100644
25--- a/test/pi/extension.test.ts
26+++ b/test/pi/extension.test.ts
27@@ -84,6 +84,7 @@ describe("Pi policy extension", () => {
28     await expect(extension.toolCall({ toolName: "custom_tool", input: {} }, context())).resolves.toBeUndefined()
29     await expect(extension.toolCall({ toolName: "blocked_tool", input: {} }, context())).resolves.toMatchObject({ block: true })
30     await expect(extension.toolCall({ toolName: "bash", input: { command: "git status" } }, context())).resolves.toBeUndefined()
31+    await expect(extension.toolCall({ toolName: "bash", input: { command: "base64 file.txt" } }, context())).resolves.toBeUndefined()
32     await expect(extension.toolCall({ toolName: "bash", input: { command: "sudo id" } }, context())).resolves.toMatchObject({ block: true })
33     await expect(extension.toolCall({ toolName: "mcp", input: {} }, context())).resolves.toBeUndefined()
34     await expect(extension.toolCall({ toolName: "read", input: { path: join(externalDirectory, "file") } }, context())).resolves.toBeUndefined()