From 8da8756f76df1a76c7af1593c6278619e4c324fe Mon Sep 17 00:00:00 2001 From: User Date: Tue, 3 Mar 2026 04:47:08 +0800 Subject: [PATCH] fix(exec): escape regex literals in allowlist path matching --- src/infra/exec-approvals.test.ts | 24 ++++++++++++++++++------ src/infra/exec-command-resolution.ts | 6 +++++- 2 files changed, 23 insertions(+), 7 deletions(-) diff --git a/src/infra/exec-approvals.test.ts b/src/infra/exec-approvals.test.ts index 1a7a2a7935b..2367ae44027 100644 --- a/src/infra/exec-approvals.test.ts +++ b/src/infra/exec-approvals.test.ts @@ -82,13 +82,25 @@ describe("exec approvals allowlist matching", () => { expect(match?.pattern).toBe("*"); }); - it("requires a resolved path", () => { - const match = matchAllowlist([{ pattern: "bin/rg" }], { - rawExecutable: "bin/rg", - resolvedPath: undefined, - executableName: "rg", + it("matches absolute paths containing regex metacharacters", () => { + const plusPathCases = ["/usr/bin/g++", "/usr/bin/clang++"]; + for (const candidatePath of plusPathCases) { + const match = matchAllowlist([{ pattern: candidatePath }], { + rawExecutable: candidatePath, + resolvedPath: candidatePath, + executableName: candidatePath.split("/").at(-1) ?? candidatePath, + }); + expect(match?.pattern).toBe(candidatePath); + } + }); + + it("does not throw when wildcard globs are mixed with + in path", () => { + const match = matchAllowlist([{ pattern: "/usr/bin/*++" }], { + rawExecutable: "/usr/bin/g++", + resolvedPath: "/usr/bin/g++", + executableName: "g++", }); - expect(match).toBeNull(); + expect(match?.pattern).toBe("/usr/bin/*++"); }); }); diff --git a/src/infra/exec-command-resolution.ts b/src/infra/exec-command-resolution.ts index 2c02983705b..cadbba199d2 100644 --- a/src/infra/exec-command-resolution.ts +++ b/src/infra/exec-command-resolution.ts @@ -111,6 +111,10 @@ function tryRealpath(value: string): string | null { } } +function escapeRegExpLiteral(input: string): string { + return input.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); +} + function globToRegExp(pattern: string): RegExp { let regex = "^"; let i = 0; @@ -132,7 +136,7 @@ function globToRegExp(pattern: string): RegExp { i += 1; continue; } - regex += ch.replace(/[.*+?^${}()|[\\]\\\\]/g, "\\$&"); + regex += escapeRegExpLiteral(ch); i += 1; } regex += "$";