From 7c83cae42542b55bd99eba4dc23e960200d082e4 Mon Sep 17 00:00:00 2001 From: luoyanglang Date: Fri, 3 Apr 2026 00:06:08 +0800 Subject: [PATCH] fix(exec): keep strict inline-eval interpreter approvals reusable --- src/infra/exec-approvals-allow-always.test.ts | 24 +++++++++++++++++++ src/infra/exec-approvals-allowlist.ts | 9 ++++++- 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/src/infra/exec-approvals-allow-always.test.ts b/src/infra/exec-approvals-allow-always.test.ts index 7c399d9e5e0..f68503aeb1c 100644 --- a/src/infra/exec-approvals-allow-always.test.ts +++ b/src/infra/exec-approvals-allow-always.test.ts @@ -1,6 +1,7 @@ import fs from "node:fs"; import path from "node:path"; import { describe, expect, it } from "vitest"; +import { resolveAllowAlwaysPatternEntries } from "./exec-approvals-allowlist.js"; import { makeMockCommandResolution, makeMockExecutableResolution, @@ -268,6 +269,29 @@ describe("resolveAllowAlwaysPatterns", () => { expect(persisted).toEqual([]); }); + it("persists benign awk interpreters without argv binding in strict inline-eval mode on Windows", () => { + const awk = "C:\\temp\\awk.exe"; + const entries = resolveAllowAlwaysPatternEntries({ + segments: [ + { + raw: `${awk} -F , -f script.awk data.csv`, + argv: [awk, "-F", ",", "-f", "script.awk", "data.csv"], + resolution: makeMockCommandResolution({ + execution: makeMockExecutableResolution({ + rawExecutable: awk, + resolvedPath: awk, + executableName: "awk", + }), + }), + }, + ], + platform: "win32", + strictInlineEval: true, + }); + + expect(entries).toEqual([{ pattern: awk }]); + }); + it("unwraps shell wrappers and persists the inner executable instead", () => { if (process.platform === "win32") { return; diff --git a/src/infra/exec-approvals-allowlist.ts b/src/infra/exec-approvals-allowlist.ts index db4c1331493..3b496a95b55 100644 --- a/src/infra/exec-approvals-allowlist.ts +++ b/src/infra/exec-approvals-allowlist.ts @@ -958,7 +958,14 @@ function collectAllowAlwaysPatterns(params: { } } if (!trustPlan.shellWrapperExecutable) { - const argPattern = buildArgPatternFromArgv(segment.argv, params.platform); + // In strict inline-eval mode we deliberately persist benign interpreter + // executables without an argv binding so later inline-eval attempts match + // the binary and get rejected by strictInlineEval instead of reopening the + // generic allowlist approval prompt on Windows. + const argPattern = + params.strictInlineEval === true && isInterpreterLikeAllowlistPattern(candidatePath) + ? undefined + : buildArgPatternFromArgv(segment.argv, params.platform); addAllowAlwaysPattern(params.out, candidatePath, argPattern); return; }