From f86f8400f5436bb6d6e193008408a6c70202c0fb Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Sat, 25 Apr 2026 02:21:45 +0100 Subject: [PATCH] fix(codex): compact home permission paths --- .../src/app-server/approval-bridge.test.ts | 17 ++++++++--------- .../codex/src/app-server/approval-bridge.ts | 17 ++++++----------- 2 files changed, 14 insertions(+), 20 deletions(-) diff --git a/extensions/codex/src/app-server/approval-bridge.test.ts b/extensions/codex/src/app-server/approval-bridge.test.ts index 826c0f7bfc2..fe0a41d3050 100644 --- a/extensions/codex/src/app-server/approval-bridge.test.ts +++ b/extensions/codex/src/app-server/approval-bridge.test.ts @@ -554,9 +554,9 @@ describe("Codex app-server approval bridge", () => { const [, , requestPayload] = mockCallGatewayTool.mock.calls[0] ?? []; const description = (requestPayload as { description: string }).description; expect(description).toContain("Network allowHosts: example.com, *.internal"); - expect(description).toContain("File system roots: /; writePaths: /home/simone"); + expect(description).toContain("File system roots: /; writePaths: ~"); expect(description).toContain( - "High-risk targets: wildcard hosts, private-network wildcards, filesystem root, home directory", + "High-risk targets: wildcard hosts, private-network wildcards, filesystem root", ); expect(requestPayload).toEqual( expect.objectContaining({ @@ -565,7 +565,7 @@ describe("Codex app-server approval bridge", () => { ); }); - it("keeps permission detail bounded with truncated target samples", async () => { + it("keeps permission detail bounded with truncated and compacted target samples", async () => { const params = createParams(); mockCallGatewayTool .mockResolvedValueOnce({ id: "plugin:approval-4", status: "accepted" }) @@ -608,13 +608,14 @@ describe("Codex app-server approval bridge", () => { expect(description.length).toBeLessThanOrEqual(700); expect(description).toContain("example.com"); expect(description).not.toContain("secret-token"); + expect(description).not.toContain("simone"); expect(description).toContain("*.internal"); expect(description).toContain("/workspace/project"); expect(description).toContain("High-risk targets:"); - expect(description).toContain("readPaths: /Users/simone/.ssh/id_rsa"); + expect(description).toContain("readPaths: ~/.ssh/id_rsa, /etc/hosts"); }); - it("preserves Windows home paths in permission descriptions", async () => { + it("compacts Windows home paths in permission descriptions", async () => { const params = createParams(); mockCallGatewayTool .mockResolvedValueOnce({ id: "plugin:approval-windows-home", status: "accepted" }) @@ -640,10 +641,8 @@ describe("Codex app-server approval bridge", () => { const [, , requestPayload] = mockCallGatewayTool.mock.calls[0] ?? []; const description = (requestPayload as { description: string }).description; - expect(description).toContain( - "File system roots: C:/Users/alice; readPaths: C:\\Users\\alice\\.ssh\\id_rsa, c:/users/bob/project", - ); - expect(description).toContain("High-risk targets: home directory"); + expect(description).toContain("File system roots: ~; readPaths: ~\\.ssh\\id_rsa, ~/project"); + expect(description).not.toContain("High-risk targets"); }); it("strips terminal and invisible controls from permission descriptions", async () => { diff --git a/extensions/codex/src/app-server/approval-bridge.ts b/extensions/codex/src/app-server/approval-bridge.ts index f5c66737de7..dfb31166e80 100644 --- a/extensions/codex/src/app-server/approval-bridge.ts +++ b/extensions/codex/src/app-server/approval-bridge.ts @@ -431,7 +431,12 @@ function sanitizePermissionHostValue(value: string): string { } function sanitizePermissionPathValue(value: string): string { - return truncate(sanitizePermissionScalar(value), PERMISSION_VALUE_MAX_LENGTH); + const normalized = sanitizePermissionScalar(value); + const homeCompacted = normalized + .replace(/^\/home\/(?!\.{1,2}(?=\/|$))[^/]+(?=\/|$)/, "~") + .replace(/^\/Users\/(?!\.{1,2}(?=\/|$))[^/]+(?=\/|$)/, "~") + .replace(/^[A-Za-z]:[\\/]Users[\\/](?!\.{1,2}(?=[\\/]|$))[^\\/]+(?=[\\/]|$)/i, "~"); + return truncate(homeCompacted, PERMISSION_VALUE_MAX_LENGTH); } function sanitizePermissionScalar(value: string): string { @@ -456,16 +461,6 @@ function permissionPathRisks(value: string): string[] { if (normalized === "/" || normalized === "\\" || /^[A-Za-z]:[\\/]*$/.test(normalized)) { risks.push("filesystem root"); } - if ( - normalized === "~" || - normalized === "~/" || - normalized === "~\\" || - /^\/home\/[^/]+\/?$/.test(normalized) || - /^\/Users\/[^/]+\/?$/.test(normalized) || - /^[A-Za-z]:[\\/]Users[\\/][^\\/]+[\\/]?$/i.test(normalized) - ) { - risks.push("home directory"); - } return risks; }