@posthog/wizard 2.38.2 → 2.39.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +70 -13
- package/dist/{add-mcp-server-to-clients-CHSzsAG8.js → add-mcp-server-to-clients-Ci0I1vbS.js} +5 -5
- package/dist/{add-mcp-server-to-clients-CHSzsAG8.js.map → add-mcp-server-to-clients-Ci0I1vbS.js.map} +1 -1
- package/dist/{agent-interface-DExARWay.js → agent-interface-BR3zAUpM.js} +30 -623
- package/dist/agent-interface-BR3zAUpM.js.map +1 -0
- package/dist/{agent-runner-CMfTmM3B.js → agent-runner-B1fzwFzc.js} +33 -18
- package/dist/agent-runner-B1fzwFzc.js.map +1 -0
- package/dist/{analytics-Ciob-x8a.js → analytics-C7Oc3yWZ.js} +3 -2
- package/dist/analytics-C7Oc3yWZ.js.map +1 -0
- package/dist/analytics-tVERo8XR.js +2 -0
- package/dist/{api-T_WHs3qZ.js → api-DSG-l8Ql.js} +3 -3
- package/dist/{api-T_WHs3qZ.js.map → api-DSG-l8Ql.js.map} +1 -1
- package/dist/bin.js +387 -105
- package/dist/bin.js.map +1 -1
- package/dist/{ci-install-DJN5aT7W.js → ci-install-DeAd2HYP.js} +4 -4
- package/dist/{ci-install-DJN5aT7W.js.map → ci-install-DeAd2HYP.js.map} +1 -1
- package/dist/{debug-D1lORUFs.js → debug-BZnZ7Gvh.js} +37 -10
- package/dist/debug-BZnZ7Gvh.js.map +1 -0
- package/dist/{debug-Cne-cLxd.js → debug-DtgfvuKF.js} +1 -1
- package/dist/{defaults-a0e6IRjP.js → defaults-BxiMLkQY.js} +1 -1
- package/dist/{defaults-a0e6IRjP.js.map → defaults-BxiMLkQY.js.map} +1 -1
- package/dist/{env-api-key-DEl3LJBv.js → env-api-key-DDVkPQmo.js} +1 -1
- package/dist/{env-api-key-DEl3LJBv.js.map → env-api-key-DDVkPQmo.js.map} +1 -1
- package/dist/{environment-BEsDCA2q.js → environment-QNoUem74.js} +3 -3
- package/dist/{environment-BEsDCA2q.js.map → environment-QNoUem74.js.map} +1 -1
- package/dist/{file-utils-Cn3dzlo-.js → file-utils-UqdMTR9x.js} +2 -2
- package/dist/{file-utils-Cn3dzlo-.js.map → file-utils-UqdMTR9x.js.map} +1 -1
- package/dist/{headless-ui-DGunpK-s.js → headless-ui-DtFktXIa.js} +2 -2
- package/dist/{headless-ui-DGunpK-s.js.map → headless-ui-DtFktXIa.js.map} +1 -1
- package/dist/{host-resolution-BnUJZKId.js → host-resolution-DjQvur4X.js} +3 -3
- package/dist/{host-resolution-BnUJZKId.js.map → host-resolution-DjQvur4X.js.map} +1 -1
- package/dist/{interactive-BB2IDSWP.js → interactive-_8v2Ymw2.js} +2 -2
- package/dist/{interactive-BB2IDSWP.js.map → interactive-_8v2Ymw2.js.map} +1 -1
- package/dist/{mcp-CgA5vyiS.js → mcp-BX34188X.js} +2 -2
- package/dist/{mcp-CgA5vyiS.js.map → mcp-BX34188X.js.map} +1 -1
- package/dist/{mcp-prompt-streaming-CGAkq9qR.js → mcp-prompt-streaming-l1zsmQQc.js} +4 -4
- package/dist/{mcp-prompt-streaming-CGAkq9qR.js.map → mcp-prompt-streaming-l1zsmQQc.js.map} +1 -1
- package/dist/{non-interactive-Dko5F3vh.js → non-interactive-BBCGIDP5.js} +2 -2
- package/dist/{non-interactive-Dko5F3vh.js.map → non-interactive-BBCGIDP5.js.map} +1 -1
- package/dist/{package-manager-WKKQADHM.js → package-manager-FY9qPU4z.js} +2 -2
- package/dist/{package-manager-WKKQADHM.js.map → package-manager-FY9qPU4z.js.map} +1 -1
- package/dist/{playground-UtT6zbjE.js → playground-B9Gesj-w.js} +5 -5
- package/dist/{playground-UtT6zbjE.js.map → playground-B9Gesj-w.js.map} +1 -1
- package/dist/{posthog-BESrI6-T.js → posthog-BHbtnNQE.js} +1 -1
- package/dist/{posthog-BESrI6-T.js.map → posthog-BHbtnNQE.js.map} +1 -1
- package/dist/{posthog-integration-CWXSng0W.js → posthog-integration-CMNtIV97.js} +14 -14
- package/dist/{posthog-integration-CWXSng0W.js.map → posthog-integration-CMNtIV97.js.map} +1 -1
- package/dist/{provisioning-CgomM26T.js → provisioning-BInfIJy5.js} +4 -4
- package/dist/{provisioning-CgomM26T.js.map → provisioning-BInfIJy5.js.map} +1 -1
- package/dist/{registry-BQIX-25O.js → registry-BsPnELr-.js} +4 -4
- package/dist/{registry-BQIX-25O.js.map → registry-BsPnELr-.js.map} +1 -1
- package/dist/security-CSlaLY6P.js +204 -0
- package/dist/security-CSlaLY6P.js.map +1 -0
- package/dist/{setup-utils-DLGNk8T6.js → setup-utils-DfRLklZ3.js} +9 -9
- package/dist/{setup-utils-DLGNk8T6.js.map → setup-utils-DfRLklZ3.js.map} +1 -1
- package/dist/{start-tui-BjCKghQq.js → start-tui-H_BSAFdp.js} +29 -27
- package/dist/start-tui-H_BSAFdp.js.map +1 -0
- package/dist/{steps-XjV9CiqH.js → steps-z89LzfJG.js} +7 -7
- package/dist/{steps-XjV9CiqH.js.map → steps-z89LzfJG.js.map} +1 -1
- package/dist/{store-CDIxU0rr.js → store-B3B76DgD.js} +4 -4
- package/dist/{store-CDIxU0rr.js.map → store-B3B76DgD.js.map} +1 -1
- package/dist/{subagent-S5Yh16IJ.js → subagent-CV59ljO1.js} +2 -2
- package/dist/{subagent-S5Yh16IJ.js.map → subagent-CV59ljO1.js.map} +1 -1
- package/dist/{task-stream-rC4IEUHC.js → task-stream-d-R9J0g4.js} +2 -2
- package/dist/{task-stream-rC4IEUHC.js.map → task-stream-d-R9J0g4.js.map} +1 -1
- package/dist/{tasks-wbjOdifO.js → tasks-DkX454Mg.js} +2 -2
- package/dist/{tasks-wbjOdifO.js.map → tasks-DkX454Mg.js.map} +1 -1
- package/dist/{telemetry-BfnR7gtf.js → telemetry-BCIe6Kli.js} +3 -3
- package/dist/{telemetry-BfnR7gtf.js.map → telemetry-BCIe6Kli.js.map} +1 -1
- package/dist/{terminal-BvRwVeQp.js → terminal-CPXW44sU.js} +13 -12
- package/dist/{terminal-BvRwVeQp.js.map → terminal-CPXW44sU.js.map} +1 -1
- package/dist/{tools-CbtYlZV4.js → tools-C4BqVrt6.js} +4 -4
- package/dist/{tools-CbtYlZV4.js.map → tools-C4BqVrt6.js.map} +1 -1
- package/dist/types-evL_rPob.js +46 -0
- package/dist/types-evL_rPob.js.map +1 -0
- package/dist/{urls-CilgPbS-.js → urls-D1AehCBN.js} +2 -2
- package/dist/{urls-CilgPbS-.js.map → urls-D1AehCBN.js.map} +1 -1
- package/dist/warlock-smoke-test.ts +165 -0
- package/dist/{wizard-abort-Cp5Y7f8C.js → wizard-abort-5fOIcY8w.js} +3 -3
- package/dist/{wizard-abort-Cp5Y7f8C.js.map → wizard-abort-5fOIcY8w.js.map} +1 -1
- package/dist/{wizard-abort-DHrEGqaC.js → wizard-abort-Ce2RceZ1.js} +1 -1
- package/dist/{wizard-tools-DQTfAzSV.js → wizard-tools-xvIrJeNN.js} +26 -50
- package/dist/wizard-tools-xvIrJeNN.js.map +1 -0
- package/dist/yara-hooks-7MEXlgqm.js +2 -0
- package/dist/yara-hooks-DNn1Jqih.js +635 -0
- package/dist/yara-hooks-DNn1Jqih.js.map +1 -0
- package/package.json +4 -3
- package/dist/agent-interface-DExARWay.js.map +0 -1
- package/dist/agent-runner-CMfTmM3B.js.map +0 -1
- package/dist/analytics-Ciob-x8a.js.map +0 -1
- package/dist/analytics-DbfiRvPS.js +0 -2
- package/dist/debug-D1lORUFs.js.map +0 -1
- package/dist/security-pJOYY5LO.js +0 -431
- package/dist/security-pJOYY5LO.js.map +0 -1
- package/dist/start-tui-BjCKghQq.js.map +0 -1
- package/dist/wizard-tools-DQTfAzSV.js.map +0 -1
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
import { s as logToFile } from "./debug-BZnZ7Gvh.js";
|
|
2
|
+
import { a as wizardCanUseTool, g as createTriageLLMProvider } from "./agent-interface-BR3zAUpM.js";
|
|
3
|
+
import { c as isWizardDocumentationPath, d as repeatBlockReason, f as scanAndTriage, i as createRepeatBlockTracker, u as recordExternalScan } from "./yara-hooks-DNn1Jqih.js";
|
|
4
|
+
//#region src/lib/agent/runner/harness/pi/security.ts
|
|
5
|
+
/** warlock ScanMatch → the report shape `recordExternalScan` expects. */
|
|
6
|
+
function toReportViolation(m) {
|
|
7
|
+
return {
|
|
8
|
+
rule: m.rule,
|
|
9
|
+
severity: m.metadata.severity ?? "unknown",
|
|
10
|
+
category: m.metadata.category ?? "unknown",
|
|
11
|
+
description: m.metadata.description ?? ""
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
const str = (v) => typeof v === "string" ? v : "";
|
|
15
|
+
/**
|
|
16
|
+
* Translate a pi tool name to the claude-cased name + input the shared policy
|
|
17
|
+
* expects. pi field names (from the live tool stream): bash{command},
|
|
18
|
+
* read/edit/write{path}, write adds {content}, edit adds {edits}, grep{path}.
|
|
19
|
+
*/
|
|
20
|
+
function toClaudePolicyCall(toolName, input) {
|
|
21
|
+
switch (toolName) {
|
|
22
|
+
case "bash": return {
|
|
23
|
+
name: "Bash",
|
|
24
|
+
input: { command: str(input.command) }
|
|
25
|
+
};
|
|
26
|
+
case "read": return {
|
|
27
|
+
name: "Read",
|
|
28
|
+
input: { file_path: input.path }
|
|
29
|
+
};
|
|
30
|
+
case "write": return {
|
|
31
|
+
name: "Write",
|
|
32
|
+
input: { file_path: input.path }
|
|
33
|
+
};
|
|
34
|
+
case "edit": return {
|
|
35
|
+
name: "Edit",
|
|
36
|
+
input: { file_path: input.path }
|
|
37
|
+
};
|
|
38
|
+
case "grep": return {
|
|
39
|
+
name: "Grep",
|
|
40
|
+
input: { path: input.path }
|
|
41
|
+
};
|
|
42
|
+
default: return {
|
|
43
|
+
name: toolName,
|
|
44
|
+
input
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* The agent-facing block message. Includes the rule's remediation so a blocked
|
|
50
|
+
* agent can self-correct (e.g. "move PII to identify()/$set") instead of
|
|
51
|
+
* retrying near-identical content.
|
|
52
|
+
*/
|
|
53
|
+
function blockMessage(m) {
|
|
54
|
+
const remediation = m.metadata.remediation ? ` Fix: ${m.metadata.remediation}.` : "";
|
|
55
|
+
return `[YARA] ${m.rule}: ${m.metadata.description ?? "security rule matched"}.${remediation} Blocked for security.`;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* YARA scan of the content a tool is about to act on, BEFORE it executes.
|
|
59
|
+
* - bash → scan the command ('command'-context rules: exfiltration,
|
|
60
|
+
* destructive, force-push). No triage — a flagged command always blocks,
|
|
61
|
+
* same as the anthropic PreToolUse hook, so the LLM call would be wasted.
|
|
62
|
+
* - write/edit → scan the content being written ('output'-context rules:
|
|
63
|
+
* hardcoded keys, PII), WITH triage, and with the same wizard-doc
|
|
64
|
+
* `posthog_pii` suppression the anthropic path uses so the agent's own
|
|
65
|
+
* event-plan files aren't blocked.
|
|
66
|
+
* Returns a block reason, or undefined to allow. Read/grep are post-scanned on
|
|
67
|
+
* their output (in the tool_result hook), not here.
|
|
68
|
+
*/
|
|
69
|
+
async function preExecutionYaraBlock(toolName, input, llmProvider, repeatTracker) {
|
|
70
|
+
let content;
|
|
71
|
+
let ctx;
|
|
72
|
+
let triage;
|
|
73
|
+
let phase;
|
|
74
|
+
let tool;
|
|
75
|
+
switch (toolName) {
|
|
76
|
+
case "bash":
|
|
77
|
+
content = str(input.command);
|
|
78
|
+
ctx = "command";
|
|
79
|
+
triage = void 0;
|
|
80
|
+
phase = "PreToolUse";
|
|
81
|
+
tool = "Bash";
|
|
82
|
+
break;
|
|
83
|
+
case "write":
|
|
84
|
+
content = str(input.content);
|
|
85
|
+
ctx = "output";
|
|
86
|
+
triage = llmProvider;
|
|
87
|
+
phase = "PostToolUse";
|
|
88
|
+
tool = "Write";
|
|
89
|
+
break;
|
|
90
|
+
case "edit":
|
|
91
|
+
content = (Array.isArray(input.edits) ? input.edits : []).map((e) => str(e?.newText)).join("\n");
|
|
92
|
+
ctx = "output";
|
|
93
|
+
triage = llmProvider;
|
|
94
|
+
phase = "PostToolUse";
|
|
95
|
+
tool = "Edit";
|
|
96
|
+
break;
|
|
97
|
+
default: return;
|
|
98
|
+
}
|
|
99
|
+
if (!content) return void 0;
|
|
100
|
+
let matches = await scanAndTriage(content, ctx, triage);
|
|
101
|
+
if (ctx === "output" && isWizardDocumentationPath(str(input.path))) matches = matches.filter((m) => m.metadata.category !== "posthog_pii");
|
|
102
|
+
recordExternalScan(phase, tool, matches.map(toReportViolation), "blocked");
|
|
103
|
+
if (matches.length === 0) return void 0;
|
|
104
|
+
return repeatBlockReason(repeatTracker?.attempt(toolName, content) ?? 1, toolName, blockMessage(matches[0]));
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* The pure gate decision for a single tool call. Reuses `wizardCanUseTool`
|
|
108
|
+
* (deny → block) then the YARA content scan (match → block). Fail-closed: any
|
|
109
|
+
* thrown error blocks.
|
|
110
|
+
*/
|
|
111
|
+
async function evaluateToolCall(toolName, input, ctx = {}, llmProvider) {
|
|
112
|
+
try {
|
|
113
|
+
const policy = toClaudePolicyCall(toolName, input);
|
|
114
|
+
const decision = wizardCanUseTool(policy.name, policy.input, {
|
|
115
|
+
disallowedTools: ctx.disallowedTools,
|
|
116
|
+
wizardAskPending: ctx.getWizardAskPending?.() ?? false
|
|
117
|
+
});
|
|
118
|
+
if (decision.behavior === "deny") return {
|
|
119
|
+
block: true,
|
|
120
|
+
reason: decision.message
|
|
121
|
+
};
|
|
122
|
+
const yaraReason = await preExecutionYaraBlock(toolName, input, llmProvider, ctx.repeatTracker);
|
|
123
|
+
if (yaraReason) return {
|
|
124
|
+
block: true,
|
|
125
|
+
reason: yaraReason
|
|
126
|
+
};
|
|
127
|
+
return { block: false };
|
|
128
|
+
} catch (err) {
|
|
129
|
+
logToFile("[pi-security] gate error — failing closed:", err);
|
|
130
|
+
return {
|
|
131
|
+
block: true,
|
|
132
|
+
reason: "Security check failed; tool blocked (fail-closed)."
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
/** pi result tool names whose OUTPUT gets scanned for 'input'-context rules. */
|
|
137
|
+
function isPostScanTool(toolName) {
|
|
138
|
+
return toolName === "read" || toolName === "bash";
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Build the pi security extension + the shared state the backend inspects.
|
|
142
|
+
* Install the returned factory via `extensionFactories`; pass the same factory
|
|
143
|
+
* into every subagent session so the fence is inherited.
|
|
144
|
+
*/
|
|
145
|
+
function createSecurityExtension(ctx = {}) {
|
|
146
|
+
const state = {
|
|
147
|
+
criticalViolation: false,
|
|
148
|
+
blockedCount: 0,
|
|
149
|
+
toolCalls: 0
|
|
150
|
+
};
|
|
151
|
+
const llmProvider = createTriageLLMProvider(ctx.triageAuth);
|
|
152
|
+
const gateCtx = {
|
|
153
|
+
...ctx,
|
|
154
|
+
repeatTracker: ctx.repeatTracker ?? createRepeatBlockTracker()
|
|
155
|
+
};
|
|
156
|
+
const factory = (pi) => {
|
|
157
|
+
pi.on("tool_call", async (event) => {
|
|
158
|
+
if (state.criticalViolation) return {
|
|
159
|
+
block: true,
|
|
160
|
+
reason: "Run terminated by a security violation."
|
|
161
|
+
};
|
|
162
|
+
state.toolCalls += 1;
|
|
163
|
+
if (state.toolCalls > 250) return {
|
|
164
|
+
block: true,
|
|
165
|
+
reason: `Stopped: exceeded 250 tool calls (runaway guard).`
|
|
166
|
+
};
|
|
167
|
+
const decision = await evaluateToolCall(event.toolName, event.input ?? {}, gateCtx, llmProvider);
|
|
168
|
+
if (decision.block) {
|
|
169
|
+
state.blockedCount += 1;
|
|
170
|
+
logToFile(`[pi-security] BLOCK ${event.toolName}: ${decision.reason}`);
|
|
171
|
+
return {
|
|
172
|
+
block: true,
|
|
173
|
+
reason: decision.reason
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
return {};
|
|
177
|
+
});
|
|
178
|
+
pi.on("tool_result", async (event) => {
|
|
179
|
+
if (!isPostScanTool(event.toolName)) return {};
|
|
180
|
+
const text = (event.content ?? []).map((c) => c && c.type === "text" ? c.text : "").join("\n");
|
|
181
|
+
if (!text) return {};
|
|
182
|
+
try {
|
|
183
|
+
const matches = await scanAndTriage(text, "input", llmProvider);
|
|
184
|
+
recordExternalScan("PostToolUse", event.toolName === "read" ? "Read" : "Bash", matches.map(toReportViolation), "aborted");
|
|
185
|
+
if (matches.length > 0) {
|
|
186
|
+
state.criticalViolation = true;
|
|
187
|
+
logToFile(`[pi-security] POST-SCAN VIOLATION ${event.toolName}: ${matches[0].rule}`);
|
|
188
|
+
}
|
|
189
|
+
} catch (err) {
|
|
190
|
+
state.criticalViolation = true;
|
|
191
|
+
logToFile("[pi-security] post-scan error — failing closed:", err);
|
|
192
|
+
}
|
|
193
|
+
return {};
|
|
194
|
+
});
|
|
195
|
+
};
|
|
196
|
+
return {
|
|
197
|
+
factory,
|
|
198
|
+
state
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
//#endregion
|
|
202
|
+
export { createSecurityExtension };
|
|
203
|
+
|
|
204
|
+
//# sourceMappingURL=security-CSlaLY6P.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"security-CSlaLY6P.js","names":[],"sources":["../src/lib/agent/runner/harness/pi/security.ts"],"sourcesContent":["/**\n * Fail-closed security for the pi backend (#525). pi has no built-in\n * permission layer, so we attach an extension that intercepts every tool call\n * — built-in (bash/read/edit/write/grep) AND custom — through pi's `tool_call`\n * hook and reuses the EXACT anthropic policy: `wizardCanUseTool` (the bash\n * allowlist + .env fencing) plus the YARA pre-scan. A `tool_result` hook\n * post-scans output. Both fail closed: a scanner error blocks, and a critical\n * post-scan violation latches so every subsequent tool call is blocked and the\n * run terminates as a YARA violation.\n *\n * Scanning runs through @posthog/warlock via yara-hooks' `scanAndTriage` — the\n * same engine, rules, chunking, and LLM triage policy as the anthropic\n * harness. pi handlers are async (pi's ExtensionHandler accepts promises), so\n * the WASM scan awaits inline.\n *\n * This is the one fence. Subagents run their own pi session with the SAME\n * extension installed (see subagent.ts), so a child cannot escape it.\n */\n\nimport type { LLMProvider, ScanMatch } from '@posthog/warlock';\nimport { wizardCanUseTool } from '@lib/agent/agent-interface';\nimport {\n createRepeatBlockTracker,\n isWizardDocumentationPath,\n recordExternalScan,\n repeatBlockReason,\n scanAndTriage,\n type RepeatBlockTracker,\n type ScanContext,\n} from '@lib/yara-hooks';\nimport {\n createTriageLLMProvider,\n type TriageGatewayAuth,\n} from '@lib/agent/triage-provider';\nimport { logToFile } from '@utils/debug';\n\n/** warlock ScanMatch → the report shape `recordExternalScan` expects. */\nfunction toReportViolation(m: ScanMatch) {\n return {\n rule: m.rule,\n severity: m.metadata.severity ?? 'unknown',\n category: m.metadata.category ?? 'unknown',\n description: m.metadata.description ?? '',\n };\n}\n\n/** Runaway backstop: hard cap on tool calls per (sub)agent session. */\nexport const MAX_TOOL_CALLS = 250;\n\nexport interface ToolGateContext {\n disallowedTools?: readonly string[];\n /** True while a wizard_ask overlay is open (interactive); blocks Write/Edit. */\n getWizardAskPending?: () => boolean;\n /**\n * Gateway auth for the LLM triage pass. pi auths the gateway\n * programmatically and never sets ANTHROPIC_BASE_URL/AUTH_TOKEN on the env,\n * so without this triage silently no-ops and every flagged match is acted\n * on (fail-closed, but no false-positive filtering).\n */\n triageAuth?: TriageGatewayAuth;\n /**\n * Per-run tracker of YARA-blocked payloads. When the agent retries content\n * that was already blocked, the block reason escalates (\"change the code,\n * not the retry\" → \"report it and move on\"). The extension wires one in;\n * absent (tests calling `evaluateToolCall` directly) every block reads as\n * a first attempt.\n */\n repeatTracker?: RepeatBlockTracker;\n}\n\nexport interface GateDecision {\n block: boolean;\n reason?: string;\n}\n\nconst str = (v: unknown): string => (typeof v === 'string' ? v : '');\n\n/**\n * Translate a pi tool name to the claude-cased name + input the shared policy\n * expects. pi field names (from the live tool stream): bash{command},\n * read/edit/write{path}, write adds {content}, edit adds {edits}, grep{path}.\n */\nfunction toClaudePolicyCall(\n toolName: string,\n input: Record<string, unknown>,\n): { name: string; input: Record<string, unknown> } {\n switch (toolName) {\n case 'bash':\n return { name: 'Bash', input: { command: str(input.command) } };\n case 'read':\n return { name: 'Read', input: { file_path: input.path } };\n case 'write':\n return { name: 'Write', input: { file_path: input.path } };\n case 'edit':\n return { name: 'Edit', input: { file_path: input.path } };\n case 'grep':\n return { name: 'Grep', input: { path: input.path } };\n default:\n // Custom tools (load_skill_menu, set_env_values, dispatch_agent, …) +\n // find/ls: no path/command, policy allows (their own handlers are fenced).\n return { name: toolName, input };\n }\n}\n\n/**\n * The agent-facing block message. Includes the rule's remediation so a blocked\n * agent can self-correct (e.g. \"move PII to identify()/$set\") instead of\n * retrying near-identical content.\n */\nfunction blockMessage(m: ScanMatch): string {\n const remediation = m.metadata.remediation\n ? ` Fix: ${m.metadata.remediation}.`\n : '';\n return `[YARA] ${m.rule}: ${\n m.metadata.description ?? 'security rule matched'\n }.${remediation} Blocked for security.`;\n}\n\n/**\n * YARA scan of the content a tool is about to act on, BEFORE it executes.\n * - bash → scan the command ('command'-context rules: exfiltration,\n * destructive, force-push). No triage — a flagged command always blocks,\n * same as the anthropic PreToolUse hook, so the LLM call would be wasted.\n * - write/edit → scan the content being written ('output'-context rules:\n * hardcoded keys, PII), WITH triage, and with the same wizard-doc\n * `posthog_pii` suppression the anthropic path uses so the agent's own\n * event-plan files aren't blocked.\n * Returns a block reason, or undefined to allow. Read/grep are post-scanned on\n * their output (in the tool_result hook), not here.\n */\nasync function preExecutionYaraBlock(\n toolName: string,\n input: Record<string, unknown>,\n llmProvider: LLMProvider | undefined,\n repeatTracker?: RepeatBlockTracker,\n): Promise<string | undefined> {\n let content: string;\n let ctx: ScanContext;\n let triage: LLMProvider | undefined;\n // Report labels for the scan report (phase/tool), matching the anthropic\n // hooks so pi and anthropic runs read the same in telemetry.\n let phase: string;\n let tool: string;\n switch (toolName) {\n case 'bash':\n content = str(input.command);\n ctx = 'command';\n triage = undefined;\n phase = 'PreToolUse';\n tool = 'Bash';\n break;\n case 'write':\n content = str(input.content);\n ctx = 'output';\n triage = llmProvider;\n phase = 'PostToolUse';\n tool = 'Write';\n break;\n case 'edit':\n // Scan ONLY the replacement text. `edits` is [{oldText, newText}] and\n // oldText is pre-existing file content the agent is changing — scanning\n // it blocks edits whose *surroundings* contain a violation, and even\n // edits that are removing one (field FPs: capture edits adjacent to\n // existing identify() PII; pre-existing violations blocking their own\n // replacement). Matches the anthropic path, which scans new_string only.\n content = (Array.isArray(input.edits) ? input.edits : [])\n .map((e) => str((e as Record<string, unknown>)?.newText))\n .join('\\n');\n ctx = 'output';\n triage = llmProvider;\n phase = 'PostToolUse';\n tool = 'Edit';\n break;\n default:\n return undefined;\n }\n if (!content) return undefined;\n\n let matches = await scanAndTriage(content, ctx, triage);\n if (ctx === 'output' && isWizardDocumentationPath(str(input.path))) {\n matches = matches.filter((m) => m.metadata.category !== 'posthog_pii');\n }\n recordExternalScan(phase, tool, matches.map(toReportViolation), 'blocked');\n if (matches.length === 0) return undefined;\n\n const attempt = repeatTracker?.attempt(toolName, content) ?? 1;\n return repeatBlockReason(attempt, toolName, blockMessage(matches[0]));\n}\n\n/**\n * The pure gate decision for a single tool call. Reuses `wizardCanUseTool`\n * (deny → block) then the YARA content scan (match → block). Fail-closed: any\n * thrown error blocks.\n */\nexport async function evaluateToolCall(\n toolName: string,\n input: Record<string, unknown>,\n ctx: ToolGateContext = {},\n llmProvider?: LLMProvider,\n): Promise<GateDecision> {\n try {\n const policy = toClaudePolicyCall(toolName, input);\n const decision = wizardCanUseTool(policy.name, policy.input, {\n disallowedTools: ctx.disallowedTools,\n wizardAskPending: ctx.getWizardAskPending?.() ?? false,\n });\n if (decision.behavior === 'deny') {\n return { block: true, reason: decision.message };\n }\n\n const yaraReason = await preExecutionYaraBlock(\n toolName,\n input,\n llmProvider,\n ctx.repeatTracker,\n );\n if (yaraReason) return { block: true, reason: yaraReason };\n\n return { block: false };\n } catch (err) {\n logToFile('[pi-security] gate error — failing closed:', err);\n return {\n block: true,\n reason: 'Security check failed; tool blocked (fail-closed).',\n };\n }\n}\n\n/** pi result tool names whose OUTPUT gets scanned for 'input'-context rules. */\nfunction isPostScanTool(toolName: string): boolean {\n return toolName === 'read' || toolName === 'bash';\n}\n\n/** Mutable state the backend reads after the run to classify the outcome. */\nexport interface SecurityState {\n criticalViolation: boolean;\n blockedCount: number;\n toolCalls: number;\n}\n\n/**\n * Build the pi security extension + the shared state the backend inspects.\n * Install the returned factory via `extensionFactories`; pass the same factory\n * into every subagent session so the fence is inherited.\n */\nexport function createSecurityExtension(ctx: ToolGateContext = {}): {\n factory: (pi: PiExtensionApiLike) => void;\n state: SecurityState;\n} {\n const state: SecurityState = {\n criticalViolation: false,\n blockedCount: 0,\n toolCalls: 0,\n };\n\n // One triage provider per extension. Undefined (no gateway auth) means\n // triage is skipped and every flagged match is acted on — fail closed.\n const llmProvider = createTriageLLMProvider(ctx.triageAuth);\n\n // One tracker per extension = per (sub)agent session, so an identical\n // payload retried after a block gets the escalating reason.\n const gateCtx: ToolGateContext = {\n ...ctx,\n repeatTracker: ctx.repeatTracker ?? createRepeatBlockTracker(),\n };\n\n const factory = (pi: PiExtensionApiLike): void => {\n pi.on('tool_call', async (event) => {\n // A latched post-scan violation blocks everything that follows.\n if (state.criticalViolation) {\n return {\n block: true,\n reason: 'Run terminated by a security violation.',\n };\n }\n state.toolCalls += 1;\n if (state.toolCalls > MAX_TOOL_CALLS) {\n return {\n block: true,\n reason: `Stopped: exceeded ${MAX_TOOL_CALLS} tool calls (runaway guard).`,\n };\n }\n const decision = await evaluateToolCall(\n event.toolName,\n event.input ?? {},\n gateCtx,\n llmProvider,\n );\n if (decision.block) {\n state.blockedCount += 1;\n logToFile(`[pi-security] BLOCK ${event.toolName}: ${decision.reason}`);\n return { block: true, reason: decision.reason };\n }\n return {};\n });\n\n pi.on('tool_result', async (event) => {\n if (!isPostScanTool(event.toolName)) return {};\n const text = (event.content ?? [])\n .map((c) => (c && c.type === 'text' ? c.text : ''))\n .join('\\n');\n if (!text) return {};\n try {\n // 'input'-context rules (prompt injection in read content), with\n // triage — same policy as the anthropic PostToolUse Read hook.\n const matches = await scanAndTriage(text, 'input', llmProvider);\n recordExternalScan(\n 'PostToolUse',\n event.toolName === 'read' ? 'Read' : 'Bash',\n matches.map(toReportViolation),\n 'aborted',\n );\n if (matches.length > 0) {\n state.criticalViolation = true;\n logToFile(\n `[pi-security] POST-SCAN VIOLATION ${event.toolName}: ${matches[0].rule}`,\n );\n }\n } catch (err) {\n // Fail closed: a scanner error on output latches a violation.\n state.criticalViolation = true;\n logToFile('[pi-security] post-scan error — failing closed:', err);\n }\n return {};\n });\n };\n\n return { factory, state };\n}\n\n/**\n * Minimal structural type for pi's ExtensionAPI — just the `on` overloads we\n * use. Kept local so this module has no value import from the pi SDK (so the\n * CommonJS unit tests can load it directly). Handlers may return promises —\n * pi's real ExtensionHandler is `(event, ctx) => Promise<R | void> | R | void`.\n */\nexport interface PiExtensionApiLike {\n on(\n event: 'tool_call',\n handler: (event: {\n toolName: string;\n input?: Record<string, unknown>;\n }) =>\n | { block?: boolean; reason?: string }\n | Promise<{ block?: boolean; reason?: string }>,\n ): void;\n on(\n event: 'tool_result',\n handler: (event: {\n toolName: string;\n content?: Array<{ type: string; text?: string }>;\n isError?: boolean;\n }) => Record<string, never> | Promise<Record<string, never>>,\n ): void;\n}\n"],"mappings":"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"}
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { t as __exportAll } from "./rolldown-runtime-B_-DWIq7.js";
|
|
2
|
-
import { D as DUMMY_PROJECT_API_KEY, F as OAUTH_PORTS, G as POSTHOG_PROXY_CLIENT_ID, I as OAUTH_TIMEOUT_MS, K as POSTHOG_STATUS_PAGE_URL, N as ISSUES_URL, Q as WIZARD_CONTACT_EMAIL, T as DEFAULT_HOST_URL, et as WIZARD_OAUTH_SCOPES, f as getUI, s as logToFile, st as WIZARD_USER_AGENT, z as POSTHOG_DEV_CLIENT_ID } from "./debug-
|
|
3
|
-
import { n as analytics } from "./analytics-
|
|
4
|
-
import { i as withUtm, n as openTrackedLink, t as withProgress } from "./telemetry-
|
|
5
|
-
import { a as getOAuthUrl, s as resolveBaseUrl } from "./urls-
|
|
6
|
-
import { t as HostResolution } from "./host-resolution-
|
|
7
|
-
import { t as provisionNewAccount } from "./provisioning-
|
|
8
|
-
import { a as fetchUserData, r as fetchProjectData } from "./api-
|
|
9
|
-
import { i as wizardAbort } from "./wizard-abort-
|
|
2
|
+
import { D as DUMMY_PROJECT_API_KEY, F as OAUTH_PORTS, G as POSTHOG_PROXY_CLIENT_ID, I as OAUTH_TIMEOUT_MS, K as POSTHOG_STATUS_PAGE_URL, N as ISSUES_URL, Q as WIZARD_CONTACT_EMAIL, T as DEFAULT_HOST_URL, et as WIZARD_OAUTH_SCOPES, f as getUI, s as logToFile, st as WIZARD_USER_AGENT, z as POSTHOG_DEV_CLIENT_ID } from "./debug-BZnZ7Gvh.js";
|
|
3
|
+
import { n as analytics } from "./analytics-C7Oc3yWZ.js";
|
|
4
|
+
import { i as withUtm, n as openTrackedLink, t as withProgress } from "./telemetry-BCIe6Kli.js";
|
|
5
|
+
import { a as getOAuthUrl, s as resolveBaseUrl } from "./urls-D1AehCBN.js";
|
|
6
|
+
import { t as HostResolution } from "./host-resolution-DjQvur4X.js";
|
|
7
|
+
import { t as provisionNewAccount } from "./provisioning-BInfIJy5.js";
|
|
8
|
+
import { a as fetchUserData, r as fetchProjectData } from "./api-DSG-l8Ql.js";
|
|
9
|
+
import { i as wizardAbort } from "./wizard-abort-5fOIcY8w.js";
|
|
10
10
|
import { major, minVersion } from "semver";
|
|
11
11
|
import * as fs$2 from "fs";
|
|
12
12
|
import * as path$1 from "path";
|
|
@@ -1170,4 +1170,4 @@ async function askForProvisioningSignup(email, region, baseUrl) {
|
|
|
1170
1170
|
//#endregion
|
|
1171
1171
|
export { createVersionBucket as a, tryGetPackageJson as i, isUsingTypeScript as n, extractOAuthCode as o, setup_utils_exports as r, detectAllPackageManagers as s, getOrAskForProjectData as t };
|
|
1172
1172
|
|
|
1173
|
-
//# sourceMappingURL=setup-utils-
|
|
1173
|
+
//# sourceMappingURL=setup-utils-DfRLklZ3.js.map
|