Skip to content

Commit e5e6fee

Browse files
committed
fix(exec): filter onToolsActivate names through overlay allow list
1 parent d443b59 commit e5e6fee

2 files changed

Lines changed: 105 additions & 16 deletions

File tree

‎src/exec/runner.test.ts‎

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { describe, expect, test } from "bun:test";
2+
import { readFileSync } from "node:fs";
23
import { DIRECTOR_REGISTRY } from "../agent/directors/registry.js";
34
import { createAdvertisedToolset } from "../session/assemble-runtime.js";
45
import {
@@ -114,6 +115,97 @@ describe("exec director allowlist", () => {
114115
);
115116
});
116117

118+
test("the promoter does not commit a name outside the overlay allow list", () => {
119+
const overlay = resolveExecDirectorOverlay("explorer");
120+
const { activated, computeAdvertised, flushPromotions } =
121+
createAdvertisedToolset({
122+
sessionMode: "orchestrator",
123+
toolAvailability: { languageServerAvailable: true },
124+
getProvider: () => ({ providerName: "test", model: "test-model" }),
125+
builtInPrefix: overlay.advertisedAllow,
126+
});
127+
let committed = 0;
128+
const promote = createExecToolPromoter({
129+
activate: (names) => activated.activate(names),
130+
isAllowed: (name) => isExecOverlayToolAllowed(overlay, name),
131+
commitWire: () => {
132+
if (flushPromotions()) committed += 1;
133+
},
134+
});
135+
const registry = [
136+
{
137+
name: OUTSIDE_ALLOW,
138+
description: "create an issue",
139+
inputSchema: { type: "object", properties: {} },
140+
},
141+
{
142+
name: "lsp",
143+
description: "language server",
144+
inputSchema: { type: "object", properties: {} },
145+
},
146+
];
147+
promote([OUTSIDE_ALLOW]);
148+
expect(activated.has(OUTSIDE_ALLOW)).toBe(false);
149+
expect(committed).toBe(0);
150+
expect(computeAdvertised(registry).map((d) => d.name)).not.toContain(
151+
OUTSIDE_ALLOW,
152+
);
153+
});
154+
155+
test("the promoter still commits lsp when the overlay allows it", () => {
156+
const overlay = resolveExecDirectorOverlay("explorer");
157+
expect(isExecOverlayToolAllowed(overlay, "lsp")).toBe(true);
158+
const { activated, computeAdvertised, flushPromotions } =
159+
createAdvertisedToolset({
160+
sessionMode: "orchestrator",
161+
toolAvailability: { languageServerAvailable: true },
162+
getProvider: () => ({ providerName: "test", model: "test-model" }),
163+
builtInPrefix: overlay.advertisedAllow,
164+
});
165+
let committed = 0;
166+
const promote = createExecToolPromoter({
167+
activate: (names) => activated.activate(names),
168+
isAllowed: (name) => isExecOverlayToolAllowed(overlay, name),
169+
commitWire: () => {
170+
if (flushPromotions()) committed += 1;
171+
},
172+
});
173+
const registry = [
174+
{
175+
name: "lsp",
176+
description: "language server",
177+
inputSchema: { type: "object", properties: {} },
178+
},
179+
{
180+
name: OUTSIDE_ALLOW,
181+
description: "create an issue",
182+
inputSchema: { type: "object", properties: {} },
183+
},
184+
];
185+
promote([OUTSIDE_ALLOW, "lsp"]);
186+
expect(activated.has(OUTSIDE_ALLOW)).toBe(false);
187+
expect(activated.has("lsp")).toBe(true);
188+
expect(committed).toBe(1);
189+
const names = computeAdvertised(registry).map((d) => d.name);
190+
expect(names).toContain("lsp");
191+
expect(names).not.toContain(OUTSIDE_ALLOW);
192+
});
193+
194+
test("exec wires commitWire on the overlay-filtered promoter and onToolsActivate uses it", () => {
195+
const source = readFileSync(
196+
new URL("./runner.ts", import.meta.url),
197+
"utf8",
198+
);
199+
expect(source).toContain("commitWire: commitPromotedWire");
200+
expect(source).toMatch(
201+
/createExecToolPromoter\(\{[\s\S]*?isAllowed:\s*\(name\)\s*=>\s*isExecOverlayToolAllowed\(overlay,\s*name\)[\s\S]*?commitWire:\s*commitPromotedWire/,
202+
);
203+
expect(source).toMatch(
204+
/onToolsActivate:\s*\(names\)\s*=>\s*promoteAndCommitWire\(names\)/,
205+
);
206+
expect(source).toContain("setToolPromoter(promoteAndCommitWire");
207+
});
208+
117209
test("skywalker overlay leaves every tool allowed", () => {
118210
const overlay = resolveExecDirectorOverlay("skywalker");
119211
expect(isExecOverlayToolAllowed(overlay, OUTSIDE_ALLOW)).toBe(true);

‎src/exec/runner.ts‎

Lines changed: 13 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -214,8 +214,9 @@ export function resolveExecDirectorOverlay(
214214

215215
/**
216216
* Single enforcement point for the exec allowlist. Everything the overlay
217-
* permits — tool_search results, promoter activation, the call gate — flows
218-
* through here, so a tool outside the allow can never become callable.
217+
* permits — tool_search results, promoter activation, onToolsActivate, the
218+
* call gate — flows through here, so a tool outside the allow can never
219+
* become callable.
219220
*/
220221
export function isExecOverlayToolAllowed(
221222
overlay: ExecDirectorOverlay,
@@ -840,16 +841,15 @@ export async function runExec(config: Config): Promise<ExecResult> {
840841
);
841842
}
842843
};
843-
agentToolset.setToolPromoter(
844-
createExecToolPromoter({
845-
activate: (names) => activatedToolNames.activate(names),
846-
isAllowed: (name) => isExecOverlayToolAllowed(overlay, name),
847-
persist: () => {
848-
void persist("running");
849-
},
850-
commitWire: commitPromotedWire,
851-
}),
852-
);
844+
const promoteAndCommitWire = createExecToolPromoter({
845+
activate: (names) => activatedToolNames.activate(names),
846+
isAllowed: (name) => isExecOverlayToolAllowed(overlay, name),
847+
persist: () => {
848+
void persist("running");
849+
},
850+
commitWire: commitPromotedWire,
851+
});
852+
agentToolset.setToolPromoter(promoteAndCommitWire);
853853

854854
const workflowHost = new WorkflowHost({
855855
cwd: config.cwd,
@@ -941,10 +941,7 @@ export async function runExec(config: Config): Promise<ExecResult> {
941941
tasks: tasks.map((t) => `${t.status}:${t.title}`).join(", "),
942942
});
943943
},
944-
onToolsActivate: (names) => {
945-
if (!activatedToolNames.activate(names)) return;
946-
commitPromotedWire();
947-
},
944+
onToolsActivate: (names) => promoteAndCommitWire(names),
948945
},
949946
(message, fields) => logger.debug(message, fields),
950947
);

0 commit comments

Comments
 (0)