fix(permissions): normalize Windows absolute file-rule matching (#973)

Co-authored-by: Letta <noreply@letta.com>
This commit is contained in:
Charles Packer
2026-02-15 22:46:23 -08:00
committed by GitHub
parent 72d43c8a43
commit d038882efe
3 changed files with 210 additions and 7 deletions

View File

@@ -272,6 +272,53 @@ test("Allow exact Bash command", () => {
expect(result2.decision).toBe("ask"); // Doesn't match exact
});
test("Issue #969: legacy Windows Edit allow rule matches memory project file", () => {
const permissions: PermissionRules = {
allow: [
"Edit(/C:\\Users\\Aaron\\.letta\\agents\\agent-7dcc\\memory\\system\\project/**)",
],
deny: [],
ask: [],
};
const result = checkPermission(
"Edit",
{
file_path:
"C:\\Users\\Aaron\\.letta\\agents\\agent-7dcc\\memory\\system\\project\\tech_stack.md",
},
permissions,
"C:\\Users\\Aaron\\repo",
);
expect(result.decision).toBe("allow");
expect(result.matchedRule).toBe(
"Edit(/C:\\Users\\Aaron\\.letta\\agents\\agent-7dcc\\memory\\system\\project/**)",
);
});
test("Issue #969 guardrail: Windows legacy Edit rule does not over-match sibling subtree", () => {
const permissions: PermissionRules = {
allow: [
"Edit(/C:\\Users\\Aaron\\.letta\\agents\\agent-7dcc\\memory\\system\\project/**)",
],
deny: [],
ask: [],
};
const result = checkPermission(
"Edit",
{
file_path:
"C:\\Users\\Aaron\\.letta\\agents\\agent-7dcc\\memory\\system\\other\\x.md",
},
permissions,
"C:\\Users\\Aaron\\repo",
);
expect(result.decision).toBe("ask");
});
// ============================================================================
// Ask Rule Tests
// ============================================================================

View File

@@ -349,3 +349,73 @@ test("File pattern: Windows absolute path in working directory", () => {
),
).toBe(true);
});
test("File pattern: Windows absolute variants are equivalent", () => {
const query =
"Edit(C:\\Users\\Aaron\\.letta\\agents\\agent-1\\memory\\system\\project\\tech_stack.md)";
const workingDir = "C:\\Users\\Aaron\\repo";
expect(
matchesFilePattern(
query,
"Edit(/C:/Users/Aaron/.letta/agents/agent-1/memory/system/project/**)",
workingDir,
),
).toBe(true);
expect(
matchesFilePattern(
query,
"Edit(//C:/Users/Aaron/.letta/agents/agent-1/memory/system/project/**)",
workingDir,
),
).toBe(true);
expect(
matchesFilePattern(
query,
"Edit(C:/Users/Aaron/.letta/agents/agent-1/memory/system/project/**)",
workingDir,
),
).toBe(true);
});
test("File pattern: Windows drive-letter matching is case-insensitive", () => {
const query = "Edit(c:\\users\\aaron\\repo\\src\\file.ts)";
const workingDir = "C:\\Users\\Aaron\\repo";
expect(
matchesFilePattern(query, "Edit(C:/Users/Aaron/repo/src/**)", workingDir),
).toBe(true);
});
test("File pattern: UNC absolute path matches normalized UNC pattern", () => {
const query = "Edit(\\\\server\\share\\folder\\file.md)";
const workingDir = "C:\\Users\\Aaron\\repo";
expect(
matchesFilePattern(query, "Edit(//server/share/folder/**)", workingDir),
).toBe(true);
});
test("File pattern: extended Windows drive path matches canonical drive pattern", () => {
const query = String.raw`Edit(\\?\C:\Users\Aaron\folder\file.md)`;
const workingDir = String.raw`C:\Users\Aaron\repo`;
expect(
matchesFilePattern(query, "Edit(C:/Users/Aaron/folder/**)", workingDir),
).toBe(true);
});
test("File pattern: extended UNC pattern matches UNC query path", () => {
const query = String.raw`Edit(\\server\share\folder\file.md)`;
const workingDir = String.raw`C:\Users\Aaron\repo`;
expect(
matchesFilePattern(
query,
"Edit(//?/UNC/server/share/folder/**)",
workingDir,
),
).toBe(true);
});