@praxis.guard/auditor-cli 0.0.32 → 0.0.33

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.
Files changed (40) hide show
  1. package/dist/hooks/before-mcp-argv.d.ts +17 -0
  2. package/dist/hooks/before-mcp-argv.d.ts.map +1 -0
  3. package/dist/hooks/before-mcp-argv.js +67 -0
  4. package/dist/hooks/before-mcp-argv.js.map +1 -0
  5. package/dist/hooks/before-mcp-mutate.d.ts +23 -0
  6. package/dist/hooks/before-mcp-mutate.d.ts.map +1 -0
  7. package/dist/hooks/before-mcp-mutate.js +76 -0
  8. package/dist/hooks/before-mcp-mutate.js.map +1 -0
  9. package/dist/hooks/before-mcp-skipped.d.ts +14 -0
  10. package/dist/hooks/before-mcp-skipped.d.ts.map +1 -0
  11. package/dist/hooks/before-mcp-skipped.js +56 -0
  12. package/dist/hooks/before-mcp-skipped.js.map +1 -0
  13. package/dist/hooks/before-mcp-types.d.ts +15 -0
  14. package/dist/hooks/before-mcp-types.d.ts.map +1 -0
  15. package/dist/hooks/before-mcp-types.js +2 -0
  16. package/dist/hooks/before-mcp-types.js.map +1 -0
  17. package/dist/hooks/run-before-mcp.d.ts +3 -27
  18. package/dist/hooks/run-before-mcp.d.ts.map +1 -1
  19. package/dist/hooks/run-before-mcp.js +57 -195
  20. package/dist/hooks/run-before-mcp.js.map +1 -1
  21. package/dist/mcp/evaluate-guard.d.ts +11 -0
  22. package/dist/mcp/evaluate-guard.d.ts.map +1 -0
  23. package/dist/mcp/evaluate-guard.js +148 -0
  24. package/dist/mcp/evaluate-guard.js.map +1 -0
  25. package/dist/mcp/guard-approval-block.d.ts +26 -0
  26. package/dist/mcp/guard-approval-block.d.ts.map +1 -0
  27. package/dist/mcp/guard-approval-block.js +154 -0
  28. package/dist/mcp/guard-approval-block.js.map +1 -0
  29. package/dist/mcp/guard-heartbeat.d.ts +6 -0
  30. package/dist/mcp/guard-heartbeat.d.ts.map +1 -0
  31. package/dist/mcp/guard-heartbeat.js +68 -0
  32. package/dist/mcp/guard-heartbeat.js.map +1 -0
  33. package/dist/mcp/guard-schemas.d.ts +42 -0
  34. package/dist/mcp/guard-schemas.d.ts.map +1 -0
  35. package/dist/mcp/guard-schemas.js +39 -0
  36. package/dist/mcp/guard-schemas.js.map +1 -0
  37. package/dist/mcp/server.d.ts.map +1 -1
  38. package/dist/mcp/server.js +4 -327
  39. package/dist/mcp/server.js.map +1 -1
  40. package/package.json +1 -1
@@ -0,0 +1,17 @@
1
+ import type { BeforeMCPExecutionPayload } from "./before-mcp-types.js";
2
+ /**
3
+ * When Cursor encodes MCP tools as `MCP:<server>:<tool>` (see Cursor hooks docs / preToolUse), split into
4
+ * server + bare tool name for policy rows under `policies.mcp.<server>.<tool>`.
5
+ */
6
+ export declare function splitMcpToolName(raw: string): {
7
+ serverGuess: string | null;
8
+ tool: string;
9
+ };
10
+ /**
11
+ * Maps hook payload → argv for `policies.v1.json` under tool key `mcp`.
12
+ * Omits raw `tool_input` from argv tokens so JSON metacharacters do not trip shell metachar heuristics.
13
+ */
14
+ export declare function mcpHookArgvFromPayload(payload: BeforeMCPExecutionPayload): string[];
15
+ export declare function stringifyToolInput(raw: unknown): string;
16
+ export declare function preferredHookCwd(payload: BeforeMCPExecutionPayload): string | undefined;
17
+ //# sourceMappingURL=before-mcp-argv.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"before-mcp-argv.d.ts","sourceRoot":"","sources":["../../src/hooks/before-mcp-argv.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,uBAAuB,CAAC;AAEvE;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG;IAAE,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAa1F;AAED;;;GAGG;AACH,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,yBAAyB,GAAG,MAAM,EAAE,CAkBnF;AAED,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,OAAO,GAAG,MAAM,CAQvD;AAED,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,yBAAyB,GAAG,MAAM,GAAG,SAAS,CASvF"}
@@ -0,0 +1,67 @@
1
+ /**
2
+ * When Cursor encodes MCP tools as `MCP:<server>:<tool>` (see Cursor hooks docs / preToolUse), split into
3
+ * server + bare tool name for policy rows under `policies.mcp.<server>.<tool>`.
4
+ */
5
+ export function splitMcpToolName(raw) {
6
+ const t = raw.trim();
7
+ if (!t)
8
+ return { serverGuess: null, tool: "_" };
9
+ if (t.startsWith("MCP:")) {
10
+ const body = t.slice(4).trim();
11
+ const idx = body.lastIndexOf(":");
12
+ if (idx !== -1) {
13
+ const serverPart = body.slice(0, idx).trim();
14
+ const toolPart = body.slice(idx + 1).trim();
15
+ if (serverPart && toolPart)
16
+ return { serverGuess: serverPart, tool: toolPart };
17
+ }
18
+ }
19
+ return { serverGuess: null, tool: t };
20
+ }
21
+ /**
22
+ * Maps hook payload → argv for `policies.v1.json` under tool key `mcp`.
23
+ * Omits raw `tool_input` from argv tokens so JSON metacharacters do not trip shell metachar heuristics.
24
+ */
25
+ export function mcpHookArgvFromPayload(payload) {
26
+ const rawName = typeof payload.tool_name === "string" ? payload.tool_name.trim() : "";
27
+ const { serverGuess, tool } = splitMcpToolName(rawName);
28
+ let server = "stdio";
29
+ if (typeof payload.url === "string" && payload.url.trim()) {
30
+ const u = payload.url.trim();
31
+ try {
32
+ server = new URL(u).host || u;
33
+ }
34
+ catch {
35
+ server = u;
36
+ }
37
+ }
38
+ else if (serverGuess) {
39
+ server = serverGuess;
40
+ }
41
+ else if (typeof payload.command === "string" && payload.command.trim()) {
42
+ server = payload.command.trim().slice(0, 400);
43
+ }
44
+ return ["mcp", server, tool || "_"];
45
+ }
46
+ export function stringifyToolInput(raw) {
47
+ if (raw === undefined || raw === null)
48
+ return "";
49
+ if (typeof raw === "string")
50
+ return raw;
51
+ try {
52
+ return JSON.stringify(raw);
53
+ }
54
+ catch {
55
+ return String(raw);
56
+ }
57
+ }
58
+ export function preferredHookCwd(payload) {
59
+ if (typeof payload.cwd === "string")
60
+ return payload.cwd;
61
+ if (Array.isArray(payload.workspace_roots) &&
62
+ typeof payload.workspace_roots[0] === "string") {
63
+ return payload.workspace_roots[0];
64
+ }
65
+ return undefined;
66
+ }
67
+ //# sourceMappingURL=before-mcp-argv.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"before-mcp-argv.js","sourceRoot":"","sources":["../../src/hooks/before-mcp-argv.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,23 @@
1
+ import type { Tier } from "../policy/index.js";
2
+ import type { BeforeMCPExecutionResponse } from "./before-mcp-types.js";
3
+ export type MutateHookPermission = {
4
+ permission: BeforeMCPExecutionResponse["permission"];
5
+ ticketConsumed: boolean;
6
+ inlineApproval: {
7
+ request_id: string;
8
+ open_url: string;
9
+ } | null;
10
+ approvalFlowSignal: string | null;
11
+ reasons: string[];
12
+ };
13
+ export declare function resolveMutateHookPermission(input: {
14
+ argv: string[];
15
+ tier: Tier;
16
+ storageRoot: string;
17
+ toolInputHash: string | null;
18
+ rawToolName: string;
19
+ toolInputPreview: string;
20
+ policyRevision: number | null;
21
+ initialReasons: string[];
22
+ }): Promise<MutateHookPermission>;
23
+ //# sourceMappingURL=before-mcp-mutate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"before-mcp-mutate.d.ts","sourceRoot":"","sources":["../../src/hooks/before-mcp-mutate.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,oBAAoB,CAAC;AAM/C,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,uBAAuB,CAAC;AAExE,MAAM,MAAM,oBAAoB,GAAG;IACjC,UAAU,EAAE,0BAA0B,CAAC,YAAY,CAAC,CAAC;IACrD,cAAc,EAAE,OAAO,CAAC;IACxB,cAAc,EAAE;QAAE,UAAU,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IAChE,kBAAkB,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB,CAAC;AAEF,wBAAsB,2BAA2B,CAAC,KAAK,EAAE;IACvD,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,IAAI,EAAE,IAAI,CAAC;IACX,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,WAAW,EAAE,MAAM,CAAC;IACpB,gBAAgB,EAAE,MAAM,CAAC;IACzB,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,cAAc,EAAE,MAAM,EAAE,CAAC;CAC1B,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAmFhC"}
@@ -0,0 +1,76 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import { argvSha256 } from "../approval/argv-fingerprint.js";
3
+ import { resolveMutateApproval } from "../approval/mcp-flow.js";
4
+ import { tryHookInlineApprovalRequest } from "../approval/hook-inline-approval.js";
5
+ import { readPendingApprovalIndex } from "../bridge/pending-approval-index.js";
6
+ import { tryConsumeExecutionTicket } from "../bridge/execution-ticket.js";
7
+ export async function resolveMutateHookPermission(input) {
8
+ const { argv, tier, storageRoot, toolInputHash, rawToolName, toolInputPreview, policyRevision, initialReasons, } = input;
9
+ const reasons = [...initialReasons];
10
+ let permission = "deny";
11
+ let ticketConsumed = false;
12
+ let approvalFlowSignal = null;
13
+ let inlineApproval = null;
14
+ if (tier !== "MUTATE") {
15
+ return {
16
+ permission: tier === "READ" ? "allow" : "deny",
17
+ ticketConsumed,
18
+ inlineApproval,
19
+ approvalFlowSignal,
20
+ reasons,
21
+ };
22
+ }
23
+ ticketConsumed = await tryConsumeExecutionTicket(argv, {
24
+ storageRoot,
25
+ kind: "mcp",
26
+ tool_input_sha256: toolInputHash,
27
+ });
28
+ if (ticketConsumed) {
29
+ return { permission: "allow", ticketConsumed, inlineApproval, approvalFlowSignal, reasons };
30
+ }
31
+ const hash = argvSha256(argv);
32
+ const pending = await readPendingApprovalIndex(hash, { storageRoot });
33
+ if (pending) {
34
+ const autoRedeem = await resolveMutateApproval({
35
+ argv: [...argv],
36
+ proposalKind: "mcp",
37
+ storageRoot,
38
+ rawDisplay: `${rawToolName} ${toolInputPreview}`,
39
+ eventId: randomUUID(),
40
+ policyRevision,
41
+ reasons,
42
+ approval: { request_id: pending.request_id },
43
+ waitMs: 0,
44
+ tool_input_sha256: toolInputHash,
45
+ });
46
+ if (autoRedeem.kind === "allow" && autoRedeem.ticketRecorded) {
47
+ ticketConsumed = await tryConsumeExecutionTicket(argv, {
48
+ storageRoot,
49
+ kind: "mcp",
50
+ tool_input_sha256: toolInputHash,
51
+ });
52
+ if (ticketConsumed) {
53
+ return { permission: "allow", ticketConsumed, inlineApproval, approvalFlowSignal, reasons };
54
+ }
55
+ }
56
+ approvalFlowSignal = "retry_without_guard_wait_resolve";
57
+ reasons.push("retry_without_guard_wait_resolve");
58
+ inlineApproval = { request_id: pending.request_id, open_url: pending.open_url };
59
+ return { permission: "deny", ticketConsumed, inlineApproval, approvalFlowSignal, reasons };
60
+ }
61
+ const created = await tryHookInlineApprovalRequest({
62
+ argv: [...argv],
63
+ kind: "mcp",
64
+ rawDisplay: `${rawToolName} ${toolInputPreview}`,
65
+ policyRevision,
66
+ reasons,
67
+ eventId: randomUUID(),
68
+ storageRoot,
69
+ tool_input_sha256: toolInputHash,
70
+ });
71
+ if (created) {
72
+ inlineApproval = { request_id: created.request_id, open_url: created.open_url };
73
+ }
74
+ return { permission: "deny", ticketConsumed, inlineApproval, approvalFlowSignal, reasons };
75
+ }
76
+ //# sourceMappingURL=before-mcp-mutate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"before-mcp-mutate.js","sourceRoot":"","sources":["../../src/hooks/before-mcp-mutate.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ import type { Tier } from "../policy/index.js";
2
+ import type { BeforeMCPExecutionPayload } from "./before-mcp-types.js";
3
+ export declare function handleSkippedMcpHook(input: {
4
+ payload: BeforeMCPExecutionPayload;
5
+ rawToolName: string;
6
+ bareTool: string;
7
+ argv: string[];
8
+ tier: Tier;
9
+ reasons: string[];
10
+ policyRevision: number | null;
11
+ auditLogRoot: string;
12
+ decisionStarted: number;
13
+ }): Promise<void>;
14
+ //# sourceMappingURL=before-mcp-skipped.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"before-mcp-skipped.d.ts","sourceRoot":"","sources":["../../src/hooks/before-mcp-skipped.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,oBAAoB,CAAC;AAC/C,OAAO,KAAK,EAAE,yBAAyB,EAA8B,MAAM,uBAAuB,CAAC;AAGnG,wBAAsB,oBAAoB,CAAC,KAAK,EAAE;IAChD,OAAO,EAAE,yBAAyB,CAAC;IACnC,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,IAAI,EAAE,IAAI,CAAC;IACX,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,YAAY,EAAE,MAAM,CAAC;IACrB,eAAe,EAAE,MAAM,CAAC;CACzB,GAAG,OAAO,CAAC,IAAI,CAAC,CAkEhB"}
@@ -0,0 +1,56 @@
1
+ import { appendAuditJsonl } from "../audit/jsonl.js";
2
+ import { getInstallId } from "../cli/install-id.js";
3
+ import { sendGuardEvent } from "../telemetry/guard-events.js";
4
+ import { stringifyToolInput } from "./before-mcp-argv.js";
5
+ export async function handleSkippedMcpHook(input) {
6
+ const { payload, rawToolName, bareTool, argv, tier, reasons, policyRevision, auditLogRoot, decisionStarted, } = input;
7
+ const latency_ms = performance.now() - decisionStarted;
8
+ const toolInputStr = stringifyToolInput(payload.tool_input);
9
+ try {
10
+ await appendAuditJsonl({
11
+ ts: new Date().toISOString(),
12
+ hook: "beforeMCPExecution",
13
+ tool_name: rawToolName,
14
+ bare_tool: bareTool,
15
+ tool_input: toolInputStr.slice(0, 8000),
16
+ argv,
17
+ status: "skipped",
18
+ skipped: true,
19
+ skip_reason: "mcp_policy_unmatched",
20
+ tier,
21
+ permission: "allow",
22
+ ticketConsumed: false,
23
+ reasons,
24
+ latency_ms,
25
+ }, auditLogRoot);
26
+ }
27
+ catch (e) {
28
+ const msg = e instanceof Error ? e.message : String(e);
29
+ process.stderr.write(`[auditor] audit log append failed: ${msg}\n`);
30
+ }
31
+ const skipResponse = { permission: "allow" };
32
+ process.stdout.write(JSON.stringify(skipResponse, null, 2));
33
+ await sendGuardEvent({
34
+ ts: new Date().toISOString(),
35
+ status: "skipped",
36
+ skipped: true,
37
+ skip_reason: "mcp_policy_unmatched",
38
+ tool: "auditor-hook-mcp",
39
+ command_path: argv[1] ?? null,
40
+ verb: argv[2] ?? null,
41
+ resource: toolInputStr ? toolInputStr.slice(0, 500) : null,
42
+ reason: reasons[0] ?? "mcp_policy_unmatched",
43
+ cmd: `${rawToolName}`,
44
+ tier,
45
+ decision: "allow",
46
+ latency_ms,
47
+ installId: getInstallId(),
48
+ kind: "mcp",
49
+ ...(policyRevision !== null ? { policy_revision: policyRevision } : {}),
50
+ meta: {
51
+ hook: "beforeMCPExecution",
52
+ ticketConsumed: false,
53
+ },
54
+ });
55
+ }
56
+ //# sourceMappingURL=before-mcp-skipped.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"before-mcp-skipped.js","sourceRoot":"","sources":["../../src/hooks/before-mcp-skipped.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AACrD,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AACpD,OAAO,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAG9D,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAE1D,MAAM,CAAC,KAAK,UAAU,oBAAoB,CAAC,KAU1C;IACC,MAAM,EACJ,OAAO,EACP,WAAW,EACX,QAAQ,EACR,IAAI,EACJ,IAAI,EACJ,OAAO,EACP,cAAc,EACd,YAAY,EACZ,eAAe,GAChB,GAAG,KAAK,CAAC;IAEV,MAAM,UAAU,GAAG,WAAW,CAAC,GAAG,EAAE,GAAG,eAAe,CAAC;IACvD,MAAM,YAAY,GAAG,kBAAkB,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;IAE5D,IAAI,CAAC;QACH,MAAM,gBAAgB,CACpB;YACE,EAAE,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;YAC5B,IAAI,EAAE,oBAAoB;YAC1B,SAAS,EAAE,WAAW;YACtB,SAAS,EAAE,QAAQ;YACnB,UAAU,EAAE,YAAY,CAAC,KAAK,CAAC,CAAC,EAAE,IAAI,CAAC;YACvC,IAAI;YACJ,MAAM,EAAE,SAAS;YACjB,OAAO,EAAE,IAAI;YACb,WAAW,EAAE,sBAAsB;YACnC,IAAI;YACJ,UAAU,EAAE,OAAO;YACnB,cAAc,EAAE,KAAK;YACrB,OAAO;YACP,UAAU;SACX,EACD,YAAY,CACb,CAAC;IACJ,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,MAAM,GAAG,GAAG,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;QACvD,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,sCAAsC,GAAG,IAAI,CAAC,CAAC;IACtE,CAAC;IAED,MAAM,YAAY,GAA+B,EAAE,UAAU,EAAE,OAAO,EAAE,CAAC;IACzE,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,YAAY,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;IAE5D,MAAM,cAAc,CAAC;QACnB,EAAE,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;QAC5B,MAAM,EAAE,SAAS;QACjB,OAAO,EAAE,IAAI;QACb,WAAW,EAAE,sBAAsB;QACnC,IAAI,EAAE,kBAAkB;QACxB,YAAY,EAAE,IAAI,CAAC,CAAC,CAAC,IAAI,IAAI;QAC7B,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,IAAI,IAAI;QACrB,QAAQ,EAAE,YAAY,CAAC,CAAC,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI;QAC1D,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,IAAI,sBAAsB;QAC5C,GAAG,EAAE,GAAG,WAAW,EAAE;QACrB,IAAI;QACJ,QAAQ,EAAE,OAAO;QACjB,UAAU;QACV,SAAS,EAAE,YAAY,EAAE;QACzB,IAAI,EAAE,KAAK;QACX,GAAG,CAAC,cAAc,KAAK,IAAI,CAAC,CAAC,CAAC,EAAE,eAAe,EAAE,cAAc,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACvE,IAAI,EAAE;YACJ,IAAI,EAAE,oBAAoB;YAC1B,cAAc,EAAE,KAAK;SACtB;KACF,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,15 @@
1
+ /** Cursor `beforeMCPExecution` stdin (see https://cursor.com/docs/hooks.md). */
2
+ export type BeforeMCPExecutionPayload = {
3
+ tool_name?: unknown;
4
+ tool_input?: unknown;
5
+ url?: unknown;
6
+ command?: unknown;
7
+ cwd?: unknown;
8
+ workspace_roots?: unknown;
9
+ };
10
+ export type BeforeMCPExecutionResponse = {
11
+ permission: "allow" | "deny" | "ask";
12
+ user_message?: string;
13
+ agent_message?: string;
14
+ };
15
+ //# sourceMappingURL=before-mcp-types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"before-mcp-types.d.ts","sourceRoot":"","sources":["../../src/hooks/before-mcp-types.ts"],"names":[],"mappings":"AAAA,gFAAgF;AAChF,MAAM,MAAM,yBAAyB,GAAG;IACtC,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B,CAAC;AAEF,MAAM,MAAM,0BAA0B,GAAG;IACvC,UAAU,EAAE,OAAO,GAAG,MAAM,GAAG,KAAK,CAAC;IACrC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=before-mcp-types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"before-mcp-types.js","sourceRoot":"","sources":["../../src/hooks/before-mcp-types.ts"],"names":[],"mappings":""}
@@ -1,30 +1,6 @@
1
- /** Cursor `beforeMCPExecution` stdin (see https://cursor.com/docs/hooks.md). */
2
- export type BeforeMCPExecutionPayload = {
3
- tool_name?: unknown;
4
- tool_input?: unknown;
5
- url?: unknown;
6
- command?: unknown;
7
- cwd?: unknown;
8
- workspace_roots?: unknown;
9
- };
10
- export type BeforeMCPExecutionResponse = {
11
- permission: "allow" | "deny" | "ask";
12
- user_message?: string;
13
- agent_message?: string;
14
- };
15
- /**
16
- * When Cursor encodes MCP tools as `MCP:<server>:<tool>` (see Cursor hooks docs / preToolUse), split into
17
- * server + bare tool name for policy rows under `policies.mcp.<server>.<tool>`.
18
- */
19
- export declare function splitMcpToolName(raw: string): {
20
- serverGuess: string | null;
21
- tool: string;
22
- };
23
- /**
24
- * Maps hook payload → argv for `policies.v1.json` under tool key `mcp`.
25
- * Omits raw `tool_input` from argv tokens so JSON metacharacters do not trip shell metachar heuristics.
26
- */
27
- export declare function mcpHookArgvFromPayload(payload: BeforeMCPExecutionPayload): string[];
1
+ import type { BeforeMCPExecutionResponse } from "./before-mcp-types.js";
2
+ export type { BeforeMCPExecutionPayload, BeforeMCPExecutionResponse } from "./before-mcp-types.js";
3
+ export { mcpHookArgvFromPayload, splitMcpToolName } from "./before-mcp-argv.js";
28
4
  /**
29
5
  * Cursor `beforeMCPExecution`: stdin JSON → stdout JSON (`permission` only contract).
30
6
  */
@@ -1 +1 @@
1
- {"version":3,"file":"run-before-mcp.d.ts","sourceRoot":"","sources":["../../src/hooks/run-before-mcp.ts"],"names":[],"mappings":"AAmBA,gFAAgF;AAChF,MAAM,MAAM,yBAAyB,GAAG;IACtC,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B,CAAC;AAEF,MAAM,MAAM,0BAA0B,GAAG;IACvC,UAAU,EAAE,OAAO,GAAG,MAAM,GAAG,KAAK,CAAC;IACrC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB,CAAC;AAiBF;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG;IAAE,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAa1F;AAED;;;GAGG;AACH,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,yBAAyB,GAAG,MAAM,EAAE,CAkBnF;AA0BD;;GAEG;AACH,wBAAsB,yBAAyB,IAAI,OAAO,CAAC,IAAI,CAAC,CA6N/D;AAED,wBAAgB,oCAAoC,CAAC,GAAG,EAAE,OAAO,GAAG,0BAA0B,CAM7F"}
1
+ {"version":3,"file":"run-before-mcp.d.ts","sourceRoot":"","sources":["../../src/hooks/run-before-mcp.ts"],"names":[],"mappings":"AAoBA,OAAO,KAAK,EAEV,0BAA0B,EAC3B,MAAM,uBAAuB,CAAC;AAE/B,YAAY,EAAE,yBAAyB,EAAE,0BAA0B,EAAE,MAAM,uBAAuB,CAAC;AACnG,OAAO,EAAE,sBAAsB,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAsBhF;;GAEG;AACH,wBAAsB,yBAAyB,IAAI,OAAO,CAAC,IAAI,CAAC,CAkJ/D;AAED,wBAAgB,oCAAoC,CAAC,GAAG,EAAE,OAAO,GAAG,0BAA0B,CAM7F"}
@@ -3,77 +3,13 @@ import { appendAuditJsonl } from "../audit/jsonl.js";
3
3
  import { getInstallId } from "../cli/install-id.js";
4
4
  import { evaluateMcpProposal } from "../shell/evaluate.js";
5
5
  import { resolveGuardStorageRoot } from "../bridge/guard-storage-root.js";
6
- import { tryConsumeExecutionTicket } from "../bridge/execution-ticket.js";
7
- import { tryHookInlineApprovalRequest } from "../approval/hook-inline-approval.js";
8
- import { readPendingApprovalIndex } from "../bridge/pending-approval-index.js";
9
- import { argvSha256 } from "../approval/argv-fingerprint.js";
10
6
  import { toolInputSha256 } from "../approval/fingerprint.js";
11
- import { resolveMutateApproval } from "../approval/mcp-flow.js";
12
7
  import { formatHookAllowViaCredentialMessage, formatHookDenyMessages, } from "./agent-message.js";
13
- import { randomUUID } from "node:crypto";
14
8
  import { sendGuardEvent } from "../telemetry/guard-events.js";
15
- function tierToPermission(tier) {
16
- if (tier === "READ")
17
- return "allow";
18
- return "deny";
19
- }
20
- function stringifyToolInput(raw) {
21
- if (raw === undefined || raw === null)
22
- return "";
23
- if (typeof raw === "string")
24
- return raw;
25
- try {
26
- return JSON.stringify(raw);
27
- }
28
- catch {
29
- return String(raw);
30
- }
31
- }
32
- /**
33
- * When Cursor encodes MCP tools as `MCP:<server>:<tool>` (see Cursor hooks docs / preToolUse), split into
34
- * server + bare tool name for policy rows under `policies.mcp.<server>.<tool>`.
35
- */
36
- export function splitMcpToolName(raw) {
37
- const t = raw.trim();
38
- if (!t)
39
- return { serverGuess: null, tool: "_" };
40
- if (t.startsWith("MCP:")) {
41
- const body = t.slice(4).trim();
42
- const idx = body.lastIndexOf(":");
43
- if (idx !== -1) {
44
- const serverPart = body.slice(0, idx).trim();
45
- const toolPart = body.slice(idx + 1).trim();
46
- if (serverPart && toolPart)
47
- return { serverGuess: serverPart, tool: toolPart };
48
- }
49
- }
50
- return { serverGuess: null, tool: t };
51
- }
52
- /**
53
- * Maps hook payload → argv for `policies.v1.json` under tool key `mcp`.
54
- * Omits raw `tool_input` from argv tokens so JSON metacharacters do not trip shell metachar heuristics.
55
- */
56
- export function mcpHookArgvFromPayload(payload) {
57
- const rawName = typeof payload.tool_name === "string" ? payload.tool_name.trim() : "";
58
- const { serverGuess, tool } = splitMcpToolName(rawName);
59
- let server = "stdio";
60
- if (typeof payload.url === "string" && payload.url.trim()) {
61
- const u = payload.url.trim();
62
- try {
63
- server = new URL(u).host || u;
64
- }
65
- catch {
66
- server = u;
67
- }
68
- }
69
- else if (serverGuess) {
70
- server = serverGuess;
71
- }
72
- else if (typeof payload.command === "string" && payload.command.trim()) {
73
- server = payload.command.trim().slice(0, 400);
74
- }
75
- return ["mcp", server, tool || "_"];
76
- }
9
+ import { mcpHookArgvFromPayload, preferredHookCwd, stringifyToolInput, } from "./before-mcp-argv.js";
10
+ import { resolveMutateHookPermission } from "./before-mcp-mutate.js";
11
+ import { handleSkippedMcpHook } from "./before-mcp-skipped.js";
12
+ export { mcpHookArgvFromPayload, splitMcpToolName } from "./before-mcp-argv.js";
77
13
  async function readStdinJson() {
78
14
  return await new Promise((resolve, reject) => {
79
15
  let data = "";
@@ -89,14 +25,10 @@ async function readStdinJson() {
89
25
  });
90
26
  });
91
27
  }
92
- async function tryAppendAuditEvent(evt, auditLogRoot) {
93
- try {
94
- await appendAuditJsonl(evt, auditLogRoot);
95
- }
96
- catch (e) {
97
- const msg = e instanceof Error ? e.message : String(e);
98
- process.stderr.write(`[auditor] audit log append failed: ${msg}\n`);
99
- }
28
+ function tierToPermission(tier) {
29
+ if (tier === "READ")
30
+ return "allow";
31
+ return "deny";
100
32
  }
101
33
  /**
102
34
  * Cursor `beforeMCPExecution`: stdin JSON → stdout JSON (`permission` only contract).
@@ -119,121 +51,45 @@ export async function runBeforeMcpHookFromStdin() {
119
51
  const [policy, policyRevision] = await Promise.all([loadPoliciesV1(), readPoliciesV1Revision()]);
120
52
  const { skipped, evaluation } = evaluateMcpProposal(policy, argv);
121
53
  const { classification, flags, tier } = evaluation;
122
- const reasons = evaluation.reasons.map((r) => r.message);
123
- const preferredCwd = typeof payload.cwd === "string"
124
- ? payload.cwd
125
- : Array.isArray(payload.workspace_roots) &&
126
- typeof payload.workspace_roots[0] === "string"
127
- ? payload.workspace_roots[0]
128
- : undefined;
129
- const storageRoot = resolveGuardStorageRoot(preferredCwd);
54
+ const initialReasons = evaluation.reasons.map((r) => r.message);
55
+ const storageRoot = resolveGuardStorageRoot(preferredHookCwd(payload));
130
56
  const auditLogRoot = storageRoot;
131
57
  const toolInputHash = toolInputSha256(payload.tool_input);
132
58
  if (skipped) {
133
- const latency_ms = performance.now() - decisionStarted;
134
- const toolInputStr = stringifyToolInput(payload.tool_input);
135
- await tryAppendAuditEvent({
136
- ts: new Date().toISOString(),
137
- hook: "beforeMCPExecution",
138
- tool_name: rawToolName,
139
- bare_tool: bareTool,
140
- tool_input: toolInputStr.slice(0, 8000),
59
+ await handleSkippedMcpHook({
60
+ payload,
61
+ rawToolName,
62
+ bareTool,
141
63
  argv,
142
- status: "skipped",
143
- skipped: true,
144
- skip_reason: "mcp_policy_unmatched",
145
- tier,
146
- permission: "allow",
147
- ticketConsumed: false,
148
- reasons,
149
- latency_ms,
150
- }, auditLogRoot);
151
- const skipResponse = { permission: "allow" };
152
- process.stdout.write(JSON.stringify(skipResponse, null, 2));
153
- await sendGuardEvent({
154
- ts: new Date().toISOString(),
155
- status: "skipped",
156
- skipped: true,
157
- skip_reason: "mcp_policy_unmatched",
158
- tool: "auditor-hook-mcp",
159
- command_path: argv[1] ?? null,
160
- verb: argv[2] ?? null,
161
- resource: toolInputStr ? toolInputStr.slice(0, 500) : null,
162
- reason: reasons[0] ?? "mcp_policy_unmatched",
163
- cmd: `${rawToolName}`,
164
64
  tier,
165
- decision: "allow",
166
- latency_ms,
167
- installId: getInstallId(),
168
- kind: "mcp",
169
- ...(policyRevision !== null ? { policy_revision: policyRevision } : {}),
170
- meta: {
171
- hook: "beforeMCPExecution",
172
- ticketConsumed: false,
173
- },
65
+ reasons: initialReasons,
66
+ policyRevision,
67
+ auditLogRoot,
68
+ decisionStarted,
174
69
  });
175
70
  return;
176
71
  }
177
72
  let permission = tierToPermission(tier);
178
73
  let ticketConsumed = false;
74
+ let inlineApproval = null;
179
75
  let approvalFlowSignal = null;
76
+ let reasons = initialReasons;
180
77
  if (permission === "deny" && tier === "MUTATE") {
181
- ticketConsumed = await tryConsumeExecutionTicket(argv, {
78
+ const mutate = await resolveMutateHookPermission({
79
+ argv,
80
+ tier,
182
81
  storageRoot,
183
- kind: "mcp",
184
- tool_input_sha256: toolInputHash,
82
+ toolInputHash,
83
+ rawToolName,
84
+ toolInputPreview: stringifyToolInput(payload.tool_input).slice(0, 200),
85
+ policyRevision,
86
+ initialReasons,
185
87
  });
186
- if (ticketConsumed)
187
- permission = "allow";
188
- }
189
- let inlineApproval = null;
190
- if (permission === "deny" && tier === "MUTATE") {
191
- const hash = argvSha256(argv);
192
- const pending = await readPendingApprovalIndex(hash, { storageRoot });
193
- if (pending) {
194
- const autoRedeem = await resolveMutateApproval({
195
- argv: [...argv],
196
- proposalKind: "mcp",
197
- storageRoot,
198
- rawDisplay: `${rawToolName} ${stringifyToolInput(payload.tool_input).slice(0, 200)}`,
199
- eventId: randomUUID(),
200
- policyRevision,
201
- reasons,
202
- approval: { request_id: pending.request_id },
203
- waitMs: 0,
204
- tool_input_sha256: toolInputHash,
205
- });
206
- if (autoRedeem.kind === "allow" && autoRedeem.ticketRecorded) {
207
- ticketConsumed = await tryConsumeExecutionTicket(argv, {
208
- storageRoot,
209
- kind: "mcp",
210
- tool_input_sha256: toolInputHash,
211
- });
212
- if (ticketConsumed) {
213
- permission = "allow";
214
- }
215
- }
216
- if (permission === "deny") {
217
- approvalFlowSignal = "retry_without_guard_wait_resolve";
218
- reasons.push("retry_without_guard_wait_resolve");
219
- inlineApproval = { request_id: pending.request_id, open_url: pending.open_url };
220
- }
221
- }
222
- else {
223
- const created = await tryHookInlineApprovalRequest({
224
- argv: [...argv],
225
- kind: "mcp",
226
- rawDisplay: `${rawToolName} ${stringifyToolInput(payload.tool_input).slice(0, 200)}`,
227
- policyRevision,
228
- reasons,
229
- eventId: randomUUID(),
230
- storageRoot,
231
- tool_input_sha256: toolInputHash,
232
- });
233
- if (created) {
234
- inlineApproval = { request_id: created.request_id, open_url: created.open_url };
235
- }
236
- }
88
+ permission = mutate.permission;
89
+ ticketConsumed = mutate.ticketConsumed;
90
+ inlineApproval = mutate.inlineApproval;
91
+ approvalFlowSignal = mutate.approvalFlowSignal;
92
+ reasons = mutate.reasons;
237
93
  }
238
94
  const latency_ms = performance.now() - decisionStarted;
239
95
  const toolInputStr = stringifyToolInput(payload.tool_input);
@@ -258,24 +114,30 @@ export async function runBeforeMcpHookFromStdin() {
258
114
  user_message: denyMessages.user_message,
259
115
  agent_message: denyMessages.agent_message,
260
116
  };
261
- await tryAppendAuditEvent({
262
- ts: new Date().toISOString(),
263
- hook: "beforeMCPExecution",
264
- tool_name: rawToolName,
265
- bare_tool: bareTool,
266
- tool_input: toolInputStr.slice(0, 8000),
267
- argv,
268
- classification,
269
- flags,
270
- tier,
271
- permission,
272
- ticketConsumed,
273
- inline_request_id: inlineApproval?.request_id ?? null,
274
- tool_input_sha256: toolInputHash,
275
- reasons,
276
- approval_flow_signal: approvalFlowSignal,
277
- latency_ms,
278
- }, auditLogRoot);
117
+ try {
118
+ await appendAuditJsonl({
119
+ ts: new Date().toISOString(),
120
+ hook: "beforeMCPExecution",
121
+ tool_name: rawToolName,
122
+ bare_tool: bareTool,
123
+ tool_input: toolInputStr.slice(0, 8000),
124
+ argv,
125
+ classification,
126
+ flags,
127
+ tier,
128
+ permission,
129
+ ticketConsumed,
130
+ inline_request_id: inlineApproval?.request_id ?? null,
131
+ tool_input_sha256: toolInputHash,
132
+ reasons,
133
+ approval_flow_signal: approvalFlowSignal,
134
+ latency_ms,
135
+ }, auditLogRoot);
136
+ }
137
+ catch (e) {
138
+ const msg = e instanceof Error ? e.message : String(e);
139
+ process.stderr.write(`[auditor] audit log append failed: ${msg}\n`);
140
+ }
279
141
  process.stdout.write(JSON.stringify(response, null, 2));
280
142
  const status = permission === "allow" ? "passed" : "blocked";
281
143
  await sendGuardEvent({