Skip to content

Commit febfe1d

Browse files
leno23cursoragent
andcommitted
fix(cli): scope infer web search secret resolution to web targets only
Use getCapabilityWebCommandSecretTargetIds instead of the full agent runtime target set so infer web search/fetch do not resolve unrelated model, memory, or channel credentials. Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent 0e6b5e4 commit febfe1d

5 files changed

Lines changed: 54 additions & 2 deletions

src/cli/capability-cli.test.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2100,9 +2100,11 @@ describe("capability cli", () => {
21002100
argv: ["infer", "web", "search", "--query", "ping", "--json"],
21012101
});
21022102

2103+
const { getCapabilityWebCommandSecretTargetIds } = await import("./command-secret-targets.js");
21032104
expect(mocks.resolveCommandConfigWithSecrets).toHaveBeenCalledWith(
21042105
expect.objectContaining({
21052106
commandName: "infer web search",
2107+
targetIds: getCapabilityWebCommandSecretTargetIds(),
21062108
}),
21072109
);
21082110
expect(webSearchRuntime.runWebSearch).toHaveBeenCalledWith(

src/cli/capability-cli.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ import {
8888
} from "../web-search/runtime.js";
8989
import { runCommandWithRuntime } from "./cli-utils.js";
9090
import { resolveCommandConfigWithSecrets } from "./command-config-resolution.js";
91-
import { getAgentRuntimeCommandSecretTargetIds } from "./command-secret-targets.js";
91+
import { getCapabilityWebCommandSecretTargetIds } from "./command-secret-targets.js";
9292
import { removeCommandByName } from "./program/command-tree.js";
9393
import { collectOption } from "./program/helpers.js";
9494

@@ -1536,7 +1536,7 @@ async function resolveCapabilityCommandConfig(params: {
15361536
const { effectiveConfig } = await resolveCommandConfigWithSecrets({
15371537
config: cfg,
15381538
commandName: params.commandName,
1539-
targetIds: getAgentRuntimeCommandSecretTargetIds(),
1539+
targetIds: getCapabilityWebCommandSecretTargetIds(),
15401540
runtime: params.runtime,
15411541
autoEnable: true,
15421542
});

src/cli/command-secret-resolution.coverage.test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ function hasSupportedTargetIdsWiring(source: string): boolean {
2121
source.includes("resolveAgentRuntimeConfig(") ||
2222
/targetIds:\s*get[A-Za-z0-9_]+\(\)/m.test(source) ||
2323
/targetIds:\s*getAgentRuntimeCommandSecretTargetIds\(/m.test(source) ||
24+
/targetIds:\s*getCapabilityWebCommandSecretTargetIds\(/m.test(source) ||
2425
/targetIds:\s*scopedTargets\.targetIds/m.test(source) ||
2526
source.includes("collectStatusScanOverview({")
2627
);

src/cli/command-secret-targets.test.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ vi.mock("../secrets/target-registry.js", () => ({
5454

5555
import {
5656
getAgentRuntimeCommandSecretTargetIds,
57+
getCapabilityWebCommandSecretTargetIds,
5758
getModelsCommandSecretTargetIds,
5859
getQrRemoteCommandSecretTargetIds,
5960
getScopedChannelsCommandSecretTargets,
@@ -82,6 +83,18 @@ describe("command secret target ids", () => {
8283
expect(ids.has("channels.discord.token")).toBe(false);
8384
});
8485

86+
it("scopes capability web commands to web credential surfaces only", () => {
87+
const ids = getCapabilityWebCommandSecretTargetIds();
88+
expect(ids.has("tools.web.search.apiKey")).toBe(true);
89+
expect(ids.has("plugins.entries.exa.config.webSearch.apiKey")).toBe(true);
90+
expect(ids.has("plugins.entries.firecrawl.config.webFetch.apiKey")).toBe(true);
91+
expect(ids.has("models.providers.openai.apiKey")).toBe(false);
92+
expect(ids.has("agents.defaults.memorySearch.remote.apiKey")).toBe(false);
93+
expect(ids.has("messages.tts.providers.openai.apiKey")).toBe(false);
94+
expect(ids.has("skills.entries.demo.apiKey")).toBe(false);
95+
expect(ids.has("channels.discord.token")).toBe(false);
96+
});
97+
8598
it("includes channel targets for agent runtime when delivery needs them", () => {
8699
const ids = getAgentRuntimeCommandSecretTargetIds({ includeChannelTargets: true });
87100
expect(ids.has("channels.discord.token")).toBe(true);

src/cli/command-secret-targets.ts

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,8 +58,16 @@ type CommandSecretTargets = {
5858
securityAudit: string[];
5959
};
6060

61+
const STATIC_CAPABILITY_WEB_TARGET_IDS = ["tools.web.search.apiKey"] as const;
62+
const CAPABILITY_WEB_TARGET_ID_PREFIXES = [
63+
"tools.web.search",
64+
"tools.web.fetch",
65+
"plugins.entries.",
66+
] as const;
67+
6168
let cachedCommandSecretTargets: CommandSecretTargets | undefined;
6269
let cachedAgentRuntimeBaseTargetIds: string[] | undefined;
70+
let cachedCapabilityWebTargetIds: string[] | undefined;
6371
let cachedChannelSecretTargetIds: string[] | undefined;
6472

6573
function getChannelSecretTargetIds(): string[] {
@@ -76,6 +84,30 @@ function isPluginWebCredentialTargetId(id: string): boolean {
7684
return configPath === "webSearch.apiKey" || configPath === "webFetch.apiKey";
7785
}
7886

87+
function isCapabilityWebSecretTargetId(id: string): boolean {
88+
if ((STATIC_CAPABILITY_WEB_TARGET_IDS as readonly string[]).includes(id)) {
89+
return true;
90+
}
91+
if (isPluginWebCredentialTargetId(id)) {
92+
return true;
93+
}
94+
return CAPABILITY_WEB_TARGET_ID_PREFIXES.some(
95+
(prefix) => id === prefix || id.startsWith(`${prefix}.`),
96+
);
97+
}
98+
99+
function getCapabilityWebTargetIds(): string[] {
100+
cachedCapabilityWebTargetIds ??= [
101+
...new Set([
102+
...STATIC_CAPABILITY_WEB_TARGET_IDS,
103+
...listSecretTargetRegistryEntries()
104+
.map((entry) => entry.id)
105+
.filter(isCapabilityWebSecretTargetId),
106+
]),
107+
].toSorted();
108+
return cachedCapabilityWebTargetIds;
109+
}
110+
79111
function getAgentRuntimeBaseTargetIds(): string[] {
80112
cachedAgentRuntimeBaseTargetIds ??= [
81113
...STATIC_AGENT_RUNTIME_BASE_TARGET_IDS,
@@ -245,6 +277,10 @@ export function getAgentRuntimeCommandSecretTargetIds(params?: {
245277
return toTargetIdSet(getCommandSecretTargets().agentRuntime);
246278
}
247279

280+
export function getCapabilityWebCommandSecretTargetIds(): Set<string> {
281+
return toTargetIdSet(getCapabilityWebTargetIds());
282+
}
283+
248284
export function getStatusCommandSecretTargetIds(
249285
config?: OpenClawConfig,
250286
env?: NodeJS.ProcessEnv,

0 commit comments

Comments
 (0)