diff --git a/src/hooks/llm-slug-generator.ts b/src/hooks/llm-slug-generator.ts index eb355fc3289..cbfeaf032cf 100644 --- a/src/hooks/llm-slug-generator.ts +++ b/src/hooks/llm-slug-generator.ts @@ -50,6 +50,14 @@ Reply with ONLY the slug, nothing else. Examples: "vendor-pitch", "api-design", const provider = parsed?.provider ?? DEFAULT_PROVIDER; const model = parsed?.model ?? DEFAULT_MODEL; + // Security: disable tools for this one-shot call. The prompt embeds + // up to 2 000 chars of raw conversation content, which is attacker- + // controllable. Without disableTools the embedded agent inherits the + // full tool set (exec, file write, messaging, …), so a crafted + // conversation could prompt-inject the slug-generation call into + // executing arbitrary side-effects *before* the (well-sanitised) slug + // text is extracted. Slug generation is pure text — it never needs + // tool access. const result = await runEmbeddedPiAgent({ sessionId: `slug-generator-${Date.now()}`, sessionKey: "temp:slug-generator", @@ -61,6 +69,7 @@ Reply with ONLY the slug, nothing else. Examples: "vendor-pitch", "api-design", prompt, provider, model, + disableTools: true, timeoutMs: 15_000, // 15 second timeout runId: `slug-gen-${Date.now()}`, });