multicorn-shield 1.1.0 → 1.3.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/CHANGELOG.md +29 -0
- package/dist/multicorn-proxy.js +2723 -2430
- package/dist/multicorn-shield.js +2677 -2383
- package/dist/openclaw-hook/handler.js +3 -3
- package/dist/openclaw-plugin/multicorn-shield.js +3 -3
- package/dist/shield-extension.js +32 -37
- package/package.json +2 -1
- package/plugins/cline/hooks/scripts/shared.cjs +49 -12
- package/plugins/gemini-cli/hooks/scripts/shared.cjs +49 -12
- package/plugins/multicorn-shield/.claude-plugin/plugin.json +8 -0
- package/plugins/multicorn-shield/hooks/hooks.json +26 -0
- package/plugins/multicorn-shield/hooks/scripts/claude-code-tool-map.cjs +138 -0
- package/plugins/multicorn-shield/hooks/scripts/post-tool-use.cjs +253 -0
- package/plugins/multicorn-shield/hooks/scripts/pre-tool-use.cjs +642 -0
- package/plugins/multicorn-shield/skills/shield-governance/SKILL.md +24 -0
- package/plugins/windsurf/hooks/scripts/post-action.cjs +60 -12
- package/plugins/windsurf/hooks/scripts/pre-action.cjs +60 -12
|
@@ -45,9 +45,9 @@ var TOOL_MAP = {
|
|
|
45
45
|
slack_read: { service: "slack", permissionLevel: "read" },
|
|
46
46
|
slack_message: { service: "slack", permissionLevel: "write" },
|
|
47
47
|
// Payments
|
|
48
|
-
payments: { service: "payments", permissionLevel: "
|
|
49
|
-
payment: { service: "payments", permissionLevel: "
|
|
50
|
-
stripe: { service: "payments", permissionLevel: "
|
|
48
|
+
payments: { service: "payments", permissionLevel: "write" },
|
|
49
|
+
payment: { service: "payments", permissionLevel: "write" },
|
|
50
|
+
stripe: { service: "payments", permissionLevel: "write" }
|
|
51
51
|
};
|
|
52
52
|
function mapToolToScope(toolName, command) {
|
|
53
53
|
const normalized = toolName.trim().toLowerCase();
|
|
@@ -42,9 +42,9 @@ var TOOL_MAP = {
|
|
|
42
42
|
slack_read: { service: "slack", permissionLevel: "read" },
|
|
43
43
|
slack_message: { service: "slack", permissionLevel: "write" },
|
|
44
44
|
// Payments
|
|
45
|
-
payments: { service: "payments", permissionLevel: "
|
|
46
|
-
payment: { service: "payments", permissionLevel: "
|
|
47
|
-
stripe: { service: "payments", permissionLevel: "
|
|
45
|
+
payments: { service: "payments", permissionLevel: "write" },
|
|
46
|
+
payment: { service: "payments", permissionLevel: "write" },
|
|
47
|
+
stripe: { service: "payments", permissionLevel: "write" }
|
|
48
48
|
};
|
|
49
49
|
function isDestructiveExecCommand(command) {
|
|
50
50
|
const destructiveCommands = ["rm", "mv", "sudo", "chmod", "chown", "dd", "truncate", "shred"];
|
package/dist/shield-extension.js
CHANGED
|
@@ -8,6 +8,7 @@ import 'stream';
|
|
|
8
8
|
import { spawn } from 'child_process';
|
|
9
9
|
import { createHash } from 'crypto';
|
|
10
10
|
import 'url';
|
|
11
|
+
import 'module';
|
|
11
12
|
import 'readline';
|
|
12
13
|
|
|
13
14
|
// Multicorn Shield Claude Desktop Extension - https://multicorn.ai
|
|
@@ -2970,7 +2971,7 @@ var require_compile = __commonJS({
|
|
|
2970
2971
|
const schOrFunc = root.refs[ref];
|
|
2971
2972
|
if (schOrFunc)
|
|
2972
2973
|
return schOrFunc;
|
|
2973
|
-
let _sch =
|
|
2974
|
+
let _sch = resolve2.call(this, root, ref);
|
|
2974
2975
|
if (_sch === void 0) {
|
|
2975
2976
|
const schema = (_a2 = root.localRefs) === null || _a2 === void 0 ? void 0 : _a2[ref];
|
|
2976
2977
|
const { schemaId } = this.opts;
|
|
@@ -2997,7 +2998,7 @@ var require_compile = __commonJS({
|
|
|
2997
2998
|
function sameSchemaEnv(s1, s2) {
|
|
2998
2999
|
return s1.schema === s2.schema && s1.root === s2.root && s1.baseId === s2.baseId;
|
|
2999
3000
|
}
|
|
3000
|
-
function
|
|
3001
|
+
function resolve2(root, ref) {
|
|
3001
3002
|
let sch;
|
|
3002
3003
|
while (typeof (sch = this.refs[ref]) == "string")
|
|
3003
3004
|
ref = sch;
|
|
@@ -3569,7 +3570,7 @@ var require_fast_uri = __commonJS({
|
|
|
3569
3570
|
}
|
|
3570
3571
|
return uri;
|
|
3571
3572
|
}
|
|
3572
|
-
function
|
|
3573
|
+
function resolve2(baseURI, relativeURI, options) {
|
|
3573
3574
|
const schemelessOptions = options ? Object.assign({ scheme: "null" }, options) : { scheme: "null" };
|
|
3574
3575
|
const resolved = resolveComponent(parse3(baseURI, schemelessOptions), parse3(relativeURI, schemelessOptions), schemelessOptions, true);
|
|
3575
3576
|
schemelessOptions.skipEscape = true;
|
|
@@ -3796,7 +3797,7 @@ var require_fast_uri = __commonJS({
|
|
|
3796
3797
|
var fastUri = {
|
|
3797
3798
|
SCHEMES,
|
|
3798
3799
|
normalize,
|
|
3799
|
-
resolve,
|
|
3800
|
+
resolve: resolve2,
|
|
3800
3801
|
resolveComponent,
|
|
3801
3802
|
equal,
|
|
3802
3803
|
serialize,
|
|
@@ -20675,7 +20676,7 @@ var Protocol = class {
|
|
|
20675
20676
|
return;
|
|
20676
20677
|
}
|
|
20677
20678
|
const pollInterval = task2.pollInterval ?? this._options?.defaultTaskPollInterval ?? 1e3;
|
|
20678
|
-
await new Promise((
|
|
20679
|
+
await new Promise((resolve2) => setTimeout(resolve2, pollInterval));
|
|
20679
20680
|
options?.signal?.throwIfAborted();
|
|
20680
20681
|
}
|
|
20681
20682
|
} catch (error2) {
|
|
@@ -20692,7 +20693,7 @@ var Protocol = class {
|
|
|
20692
20693
|
*/
|
|
20693
20694
|
request(request, resultSchema, options) {
|
|
20694
20695
|
const { relatedRequestId, resumptionToken, onresumptiontoken, task, relatedTask } = options ?? {};
|
|
20695
|
-
return new Promise((
|
|
20696
|
+
return new Promise((resolve2, reject) => {
|
|
20696
20697
|
const earlyReject = (error2) => {
|
|
20697
20698
|
reject(error2);
|
|
20698
20699
|
};
|
|
@@ -20770,7 +20771,7 @@ var Protocol = class {
|
|
|
20770
20771
|
if (!parseResult.success) {
|
|
20771
20772
|
reject(parseResult.error);
|
|
20772
20773
|
} else {
|
|
20773
|
-
|
|
20774
|
+
resolve2(parseResult.data);
|
|
20774
20775
|
}
|
|
20775
20776
|
} catch (error2) {
|
|
20776
20777
|
reject(error2);
|
|
@@ -21031,12 +21032,12 @@ var Protocol = class {
|
|
|
21031
21032
|
}
|
|
21032
21033
|
} catch {
|
|
21033
21034
|
}
|
|
21034
|
-
return new Promise((
|
|
21035
|
+
return new Promise((resolve2, reject) => {
|
|
21035
21036
|
if (signal.aborted) {
|
|
21036
21037
|
reject(new McpError(ErrorCode.InvalidRequest, "Request cancelled"));
|
|
21037
21038
|
return;
|
|
21038
21039
|
}
|
|
21039
|
-
const timeoutId = setTimeout(
|
|
21040
|
+
const timeoutId = setTimeout(resolve2, interval);
|
|
21040
21041
|
signal.addEventListener("abort", () => {
|
|
21041
21042
|
clearTimeout(timeoutId);
|
|
21042
21043
|
reject(new McpError(ErrorCode.InvalidRequest, "Request cancelled"));
|
|
@@ -21890,12 +21891,12 @@ var StdioServerTransport = class {
|
|
|
21890
21891
|
this.onclose?.();
|
|
21891
21892
|
}
|
|
21892
21893
|
send(message) {
|
|
21893
|
-
return new Promise((
|
|
21894
|
+
return new Promise((resolve2) => {
|
|
21894
21895
|
const json2 = serializeMessage(message);
|
|
21895
21896
|
if (this._stdout.write(json2)) {
|
|
21896
|
-
|
|
21897
|
+
resolve2();
|
|
21897
21898
|
} else {
|
|
21898
|
-
this._stdout.once("drain",
|
|
21899
|
+
this._stdout.once("drain", resolve2);
|
|
21899
21900
|
}
|
|
21900
21901
|
});
|
|
21901
21902
|
}
|
|
@@ -22214,7 +22215,7 @@ function detectScopeHints() {
|
|
|
22214
22215
|
return [];
|
|
22215
22216
|
}
|
|
22216
22217
|
function sleep(ms) {
|
|
22217
|
-
return new Promise((
|
|
22218
|
+
return new Promise((resolve2) => setTimeout(resolve2, ms));
|
|
22218
22219
|
}
|
|
22219
22220
|
function isApiSuccessResponse(value) {
|
|
22220
22221
|
if (typeof value !== "object" || value === null) return false;
|
|
@@ -22260,54 +22261,48 @@ function getClaudeDesktopConfigPath() {
|
|
|
22260
22261
|
}
|
|
22261
22262
|
}
|
|
22262
22263
|
var INIT_WIZARD_PLATFORM_REGISTRY = [
|
|
22263
|
-
{ slug: "openclaw", displayName: "OpenClaw", section: "native"
|
|
22264
|
-
{ slug: "claude-code", displayName: "Claude Code", section: "native"
|
|
22265
|
-
{ slug: "windsurf", displayName: "Windsurf", section: "native"
|
|
22266
|
-
{ slug: "cline", displayName: "Cline", section: "native"
|
|
22267
|
-
{ slug: "gemini-cli", displayName: "Gemini CLI", section: "native"
|
|
22264
|
+
{ slug: "openclaw", displayName: "OpenClaw", section: "native" },
|
|
22265
|
+
{ slug: "claude-code", displayName: "Claude Code", section: "native" },
|
|
22266
|
+
{ slug: "windsurf", displayName: "Windsurf", section: "native" },
|
|
22267
|
+
{ slug: "cline", displayName: "Cline", section: "native" },
|
|
22268
|
+
{ slug: "gemini-cli", displayName: "Gemini CLI", section: "native" },
|
|
22268
22269
|
{
|
|
22269
22270
|
slug: "cursor",
|
|
22270
22271
|
displayName: "Cursor",
|
|
22271
22272
|
section: "hosted",
|
|
22272
|
-
prereqUrl: "https://www.cursor.com/downloads"
|
|
22273
|
-
detectable: true
|
|
22273
|
+
prereqUrl: "https://www.cursor.com/downloads"
|
|
22274
22274
|
},
|
|
22275
22275
|
{
|
|
22276
22276
|
slug: "claude-desktop",
|
|
22277
22277
|
displayName: "Claude Desktop",
|
|
22278
22278
|
section: "hosted",
|
|
22279
|
-
prereqUrl: "https://claude.ai/download"
|
|
22280
|
-
detectable: false
|
|
22279
|
+
prereqUrl: "https://claude.ai/download"
|
|
22281
22280
|
},
|
|
22282
22281
|
{
|
|
22283
22282
|
slug: "github-copilot",
|
|
22284
22283
|
displayName: "GitHub Copilot",
|
|
22285
22284
|
section: "hosted",
|
|
22286
|
-
prereqUrl: "https://docs.github.com/en/copilot/get-started"
|
|
22287
|
-
detectable: false
|
|
22285
|
+
prereqUrl: "https://docs.github.com/en/copilot/get-started"
|
|
22288
22286
|
},
|
|
22289
22287
|
{
|
|
22290
22288
|
slug: "kilo-code",
|
|
22291
22289
|
displayName: "Kilo Code",
|
|
22292
22290
|
section: "hosted",
|
|
22293
|
-
prereqUrl: "https://kilocode.ai/docs/getting-started"
|
|
22294
|
-
detectable: false
|
|
22291
|
+
prereqUrl: "https://kilocode.ai/docs/getting-started"
|
|
22295
22292
|
},
|
|
22296
22293
|
{
|
|
22297
22294
|
slug: "continue-dev",
|
|
22298
22295
|
displayName: "Continue",
|
|
22299
22296
|
section: "hosted",
|
|
22300
|
-
prereqUrl: "https://docs.continue.dev/ide-extensions/install"
|
|
22301
|
-
detectable: false
|
|
22297
|
+
prereqUrl: "https://docs.continue.dev/ide-extensions/install"
|
|
22302
22298
|
},
|
|
22303
22299
|
{
|
|
22304
22300
|
slug: "goose",
|
|
22305
22301
|
displayName: "Goose",
|
|
22306
22302
|
section: "hosted",
|
|
22307
|
-
prereqUrl: "https://goose-docs.ai/docs/quickstart/"
|
|
22308
|
-
detectable: false
|
|
22303
|
+
prereqUrl: "https://goose-docs.ai/docs/quickstart/"
|
|
22309
22304
|
},
|
|
22310
|
-
{ slug: "other-mcp", displayName: "Local MCP / Other", section: "hosted"
|
|
22305
|
+
{ slug: "other-mcp", displayName: "Local MCP / Other", section: "hosted" }
|
|
22311
22306
|
];
|
|
22312
22307
|
(() => {
|
|
22313
22308
|
const itemsFor = (section) => INIT_WIZARD_PLATFORM_REGISTRY.filter((e) => e.section === section).map((e) => ({
|
|
@@ -22422,7 +22417,7 @@ async function writeExtensionBackup(claudeDesktopConfigPath, mcpServers) {
|
|
|
22422
22417
|
|
|
22423
22418
|
// package.json
|
|
22424
22419
|
var package_default = {
|
|
22425
|
-
version: "1.
|
|
22420
|
+
version: "1.3.0"};
|
|
22426
22421
|
|
|
22427
22422
|
// src/package-meta.ts
|
|
22428
22423
|
var PACKAGE_VERSION = package_default.version;
|
|
@@ -23201,7 +23196,7 @@ function createActionLogger(config2) {
|
|
|
23201
23196
|
};
|
|
23202
23197
|
}
|
|
23203
23198
|
function sleep2(ms) {
|
|
23204
|
-
return new Promise((
|
|
23199
|
+
return new Promise((resolve2) => setTimeout(resolve2, ms));
|
|
23205
23200
|
}
|
|
23206
23201
|
|
|
23207
23202
|
// src/proxy/interceptor.ts
|
|
@@ -23779,8 +23774,8 @@ async function runShieldExtension() {
|
|
|
23779
23774
|
const dashboardUrl = deriveDashboardUrl(baseUrl);
|
|
23780
23775
|
let resolveReady;
|
|
23781
23776
|
let rejectReady;
|
|
23782
|
-
const readyPromise = new Promise((
|
|
23783
|
-
resolveReady =
|
|
23777
|
+
const readyPromise = new Promise((resolve2, reject) => {
|
|
23778
|
+
resolveReady = resolve2;
|
|
23784
23779
|
rejectReady = reject;
|
|
23785
23780
|
});
|
|
23786
23781
|
const toolRegistry = /* @__PURE__ */ new Map();
|
|
@@ -24031,10 +24026,10 @@ async function runShieldExtension() {
|
|
|
24031
24026
|
await Promise.all(proxySessions.map((s) => s.close().catch(() => void 0)));
|
|
24032
24027
|
await server.close();
|
|
24033
24028
|
};
|
|
24034
|
-
const stdinEnded = new Promise((
|
|
24029
|
+
const stdinEnded = new Promise((resolve2) => {
|
|
24035
24030
|
process.stdin.resume();
|
|
24036
24031
|
process.stdin.once("end", () => {
|
|
24037
|
-
|
|
24032
|
+
resolve2();
|
|
24038
24033
|
});
|
|
24039
24034
|
});
|
|
24040
24035
|
const onSignal = () => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "multicorn-shield",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.0",
|
|
4
4
|
"description": "The control layer for AI agents: permissions, consent, spending limits, and audit logging.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Multicorn AI Pty Ltd",
|
|
@@ -36,6 +36,7 @@
|
|
|
36
36
|
},
|
|
37
37
|
"files": [
|
|
38
38
|
"dist",
|
|
39
|
+
"plugins/multicorn-shield",
|
|
39
40
|
"plugins/windsurf",
|
|
40
41
|
"plugins/cline",
|
|
41
42
|
"plugins/gemini-cli",
|
|
@@ -53,21 +53,58 @@ function readStdin() {
|
|
|
53
53
|
* @param {Record<string, unknown>} obj
|
|
54
54
|
* @returns {string}
|
|
55
55
|
*/
|
|
56
|
-
function
|
|
56
|
+
function cwdUnderWorkspacePath(cwdResolved, workspacePath) {
|
|
57
|
+
const w = path.resolve(workspacePath);
|
|
58
|
+
if (cwdResolved === w) return true;
|
|
59
|
+
const prefix = w.endsWith(path.sep) ? w : w + path.sep;
|
|
60
|
+
return cwdResolved.startsWith(prefix);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function pickAgentNameForPlatform(obj, platform, cwd) {
|
|
57
64
|
const agents = obj.agents;
|
|
58
|
-
if (Array.isArray(agents)) {
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
65
|
+
if (!Array.isArray(agents)) {
|
|
66
|
+
return typeof obj.agentName === "string" ? obj.agentName : "";
|
|
67
|
+
}
|
|
68
|
+
const matches = [];
|
|
69
|
+
for (const entry of agents) {
|
|
70
|
+
if (
|
|
71
|
+
entry &&
|
|
72
|
+
typeof entry === "object" &&
|
|
73
|
+
/** @type {{ platform?: string; name?: string; workspacePath?: string }} */ (entry)
|
|
74
|
+
.platform === platform &&
|
|
75
|
+
typeof (/** @type {{ platform?: string; name?: string }} */ (entry).name) === "string"
|
|
76
|
+
) {
|
|
77
|
+
matches.push(/** @type {{ name: string; workspacePath?: string }} */ (entry));
|
|
68
78
|
}
|
|
69
79
|
}
|
|
70
|
-
|
|
80
|
+
if (matches.length === 0) {
|
|
81
|
+
return typeof obj.agentName === "string" ? obj.agentName : "";
|
|
82
|
+
}
|
|
83
|
+
const withWs = matches.filter(
|
|
84
|
+
(m) => typeof m.workspacePath === "string" && m.workspacePath.length > 0,
|
|
85
|
+
);
|
|
86
|
+
if (withWs.length === 0) {
|
|
87
|
+
return matches[0].name;
|
|
88
|
+
}
|
|
89
|
+
const resolvedCwd = path.resolve(cwd);
|
|
90
|
+
let best = null;
|
|
91
|
+
let bestLen = -1;
|
|
92
|
+
for (const m of withWs) {
|
|
93
|
+
if (!cwdUnderWorkspacePath(resolvedCwd, m.workspacePath)) continue;
|
|
94
|
+
const len = path.resolve(m.workspacePath).length;
|
|
95
|
+
if (len > bestLen) {
|
|
96
|
+
bestLen = len;
|
|
97
|
+
best = m;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
if (best !== null) {
|
|
101
|
+
return best.name;
|
|
102
|
+
}
|
|
103
|
+
return matches[0].name;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function resolveClineAgentName(obj) {
|
|
107
|
+
return pickAgentNameForPlatform(obj, "cline", process.cwd());
|
|
71
108
|
}
|
|
72
109
|
|
|
73
110
|
/**
|
|
@@ -56,21 +56,58 @@ function readStdin() {
|
|
|
56
56
|
});
|
|
57
57
|
}
|
|
58
58
|
|
|
59
|
-
function
|
|
59
|
+
function cwdUnderWorkspacePath(cwdResolved, workspacePath) {
|
|
60
|
+
const w = path.resolve(workspacePath);
|
|
61
|
+
if (cwdResolved === w) return true;
|
|
62
|
+
const prefix = w.endsWith(path.sep) ? w : w + path.sep;
|
|
63
|
+
return cwdResolved.startsWith(prefix);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function pickAgentNameForPlatform(obj, platform, cwd) {
|
|
60
67
|
const agents = obj.agents;
|
|
61
|
-
if (Array.isArray(agents)) {
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
68
|
+
if (!Array.isArray(agents)) {
|
|
69
|
+
return typeof obj.agentName === "string" ? obj.agentName : "";
|
|
70
|
+
}
|
|
71
|
+
const matches = [];
|
|
72
|
+
for (const entry of agents) {
|
|
73
|
+
if (
|
|
74
|
+
entry &&
|
|
75
|
+
typeof entry === "object" &&
|
|
76
|
+
/** @type {{ platform?: string; name?: string; workspacePath?: string }} */ (entry)
|
|
77
|
+
.platform === platform &&
|
|
78
|
+
typeof (/** @type {{ platform?: string; name?: string }} */ (entry).name) === "string"
|
|
79
|
+
) {
|
|
80
|
+
matches.push(/** @type {{ name: string; workspacePath?: string }} */ (entry));
|
|
71
81
|
}
|
|
72
82
|
}
|
|
73
|
-
|
|
83
|
+
if (matches.length === 0) {
|
|
84
|
+
return typeof obj.agentName === "string" ? obj.agentName : "";
|
|
85
|
+
}
|
|
86
|
+
const withWs = matches.filter(
|
|
87
|
+
(m) => typeof m.workspacePath === "string" && m.workspacePath.length > 0,
|
|
88
|
+
);
|
|
89
|
+
if (withWs.length === 0) {
|
|
90
|
+
return matches[0].name;
|
|
91
|
+
}
|
|
92
|
+
const resolvedCwd = path.resolve(cwd);
|
|
93
|
+
let best = null;
|
|
94
|
+
let bestLen = -1;
|
|
95
|
+
for (const m of withWs) {
|
|
96
|
+
if (!cwdUnderWorkspacePath(resolvedCwd, m.workspacePath)) continue;
|
|
97
|
+
const len = path.resolve(m.workspacePath).length;
|
|
98
|
+
if (len > bestLen) {
|
|
99
|
+
bestLen = len;
|
|
100
|
+
best = m;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
if (best !== null) {
|
|
104
|
+
return best.name;
|
|
105
|
+
}
|
|
106
|
+
return matches[0].name;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function resolveGeminiCliAgentName(obj) {
|
|
110
|
+
return pickAgentNameForPlatform(obj, "gemini-cli", process.cwd());
|
|
74
111
|
}
|
|
75
112
|
|
|
76
113
|
function loadConfig() {
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "multicorn-shield",
|
|
3
|
+
"description": "Agent governance plugin for Claude Code. Intercepts tool calls via PreToolUse hooks, enforces permissions, spending limits, and maintains audit trails.",
|
|
4
|
+
"author": {
|
|
5
|
+
"name": "Multicorn AI",
|
|
6
|
+
"email": "hello@multicorn.ai"
|
|
7
|
+
}
|
|
8
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
{
|
|
2
|
+
"hooks": {
|
|
3
|
+
"PreToolUse": [
|
|
4
|
+
{
|
|
5
|
+
"matcher": "",
|
|
6
|
+
"hooks": [
|
|
7
|
+
{
|
|
8
|
+
"type": "command",
|
|
9
|
+
"command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/scripts/pre-tool-use.cjs\""
|
|
10
|
+
}
|
|
11
|
+
]
|
|
12
|
+
}
|
|
13
|
+
],
|
|
14
|
+
"PostToolUse": [
|
|
15
|
+
{
|
|
16
|
+
"matcher": "",
|
|
17
|
+
"hooks": [
|
|
18
|
+
{
|
|
19
|
+
"type": "command",
|
|
20
|
+
"command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/scripts/post-tool-use.cjs\""
|
|
21
|
+
}
|
|
22
|
+
]
|
|
23
|
+
}
|
|
24
|
+
]
|
|
25
|
+
}
|
|
26
|
+
}
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
// AUTO-GENERATED from src/hooks/claude-code-tool-map.ts — do not edit manually. Run pnpm build from the package root to regenerate.
|
|
4
|
+
|
|
5
|
+
// src/openclaw/tool-mapper.ts
|
|
6
|
+
var TOOL_MAP = {
|
|
7
|
+
// OpenClaw built-in tools
|
|
8
|
+
read: { service: "filesystem", permissionLevel: "read" },
|
|
9
|
+
write: { service: "filesystem", permissionLevel: "write" },
|
|
10
|
+
edit: { service: "filesystem", permissionLevel: "write" },
|
|
11
|
+
exec: { service: "terminal", permissionLevel: "execute" },
|
|
12
|
+
browser: { service: "browser", permissionLevel: "execute" },
|
|
13
|
+
message: { service: "messaging", permissionLevel: "write" },
|
|
14
|
+
process: { service: "terminal", permissionLevel: "execute" },
|
|
15
|
+
sessions_spawn: { service: "agents", permissionLevel: "execute" },
|
|
16
|
+
// Common integration tools (MCP servers, skills, etc.)
|
|
17
|
+
// Gmail
|
|
18
|
+
gmail: { service: "gmail", permissionLevel: "execute" },
|
|
19
|
+
gmail_send: { service: "gmail", permissionLevel: "write" },
|
|
20
|
+
gmail_read: { service: "gmail", permissionLevel: "read" },
|
|
21
|
+
// Google Calendar
|
|
22
|
+
google_calendar: { service: "google_calendar", permissionLevel: "execute" },
|
|
23
|
+
calendar: { service: "google_calendar", permissionLevel: "execute" },
|
|
24
|
+
calendar_create: { service: "google_calendar", permissionLevel: "write" },
|
|
25
|
+
calendar_read: { service: "google_calendar", permissionLevel: "read" },
|
|
26
|
+
// Google Drive
|
|
27
|
+
google_drive: { service: "google_drive", permissionLevel: "execute" },
|
|
28
|
+
drive: { service: "google_drive", permissionLevel: "execute" },
|
|
29
|
+
drive_read: { service: "google_drive", permissionLevel: "read" },
|
|
30
|
+
drive_write: { service: "google_drive", permissionLevel: "write" },
|
|
31
|
+
// Slack
|
|
32
|
+
slack: { service: "slack", permissionLevel: "execute" },
|
|
33
|
+
slack_send: { service: "slack", permissionLevel: "write" },
|
|
34
|
+
slack_read: { service: "slack", permissionLevel: "read" },
|
|
35
|
+
slack_message: { service: "slack", permissionLevel: "write" },
|
|
36
|
+
// Payments
|
|
37
|
+
payments: { service: "payments", permissionLevel: "write" },
|
|
38
|
+
payment: { service: "payments", permissionLevel: "write" },
|
|
39
|
+
stripe: { service: "payments", permissionLevel: "write" },
|
|
40
|
+
};
|
|
41
|
+
function isDestructiveExecCommand(command) {
|
|
42
|
+
const destructiveCommands = ["rm", "mv", "sudo", "chmod", "chown", "dd", "truncate", "shred"];
|
|
43
|
+
const normalized = command.toLowerCase();
|
|
44
|
+
return destructiveCommands.some((destructive) => normalized.includes(destructive));
|
|
45
|
+
}
|
|
46
|
+
function mapToolToScope(toolName, command) {
|
|
47
|
+
const normalized = toolName.trim().toLowerCase();
|
|
48
|
+
if (normalized.length === 0) {
|
|
49
|
+
return { service: "unknown", permissionLevel: "execute" };
|
|
50
|
+
}
|
|
51
|
+
const known = TOOL_MAP[normalized];
|
|
52
|
+
if (known !== void 0) {
|
|
53
|
+
return known;
|
|
54
|
+
}
|
|
55
|
+
const integrationPrefixes = {
|
|
56
|
+
gmail: "gmail",
|
|
57
|
+
google_calendar: "google_calendar",
|
|
58
|
+
calendar: "google_calendar",
|
|
59
|
+
google_drive: "google_drive",
|
|
60
|
+
drive: "google_drive",
|
|
61
|
+
slack: "slack",
|
|
62
|
+
payments: "payments",
|
|
63
|
+
payment: "payments",
|
|
64
|
+
stripe: "payments",
|
|
65
|
+
};
|
|
66
|
+
for (const [prefix, service] of Object.entries(integrationPrefixes)) {
|
|
67
|
+
if (normalized.startsWith(prefix + "_") || normalized === prefix) {
|
|
68
|
+
let permissionLevel = "execute";
|
|
69
|
+
if (
|
|
70
|
+
normalized.includes("_read") ||
|
|
71
|
+
normalized.includes("_get") ||
|
|
72
|
+
normalized.includes("_list")
|
|
73
|
+
) {
|
|
74
|
+
permissionLevel = "read";
|
|
75
|
+
} else if (
|
|
76
|
+
normalized.includes("_write") ||
|
|
77
|
+
normalized.includes("_send") ||
|
|
78
|
+
normalized.includes("_create") ||
|
|
79
|
+
normalized.includes("_update") ||
|
|
80
|
+
normalized.includes("_delete")
|
|
81
|
+
) {
|
|
82
|
+
permissionLevel = "write";
|
|
83
|
+
}
|
|
84
|
+
return { service, permissionLevel };
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
return { service: normalized, permissionLevel: "execute" };
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// src/hooks/claude-code-tool-map.ts
|
|
91
|
+
function extractExecCommand(toolInput) {
|
|
92
|
+
if (toolInput === void 0 || toolInput === null) {
|
|
93
|
+
return void 0;
|
|
94
|
+
}
|
|
95
|
+
if (typeof toolInput === "string") {
|
|
96
|
+
try {
|
|
97
|
+
return extractExecCommand(JSON.parse(toolInput));
|
|
98
|
+
} catch {
|
|
99
|
+
process.stderr.write("Shield: failed to parse tool input as JSON, using raw string\n");
|
|
100
|
+
return toolInput;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
if (typeof toolInput === "object") {
|
|
104
|
+
const o = toolInput;
|
|
105
|
+
const c = o["command"] ?? o["cmd"];
|
|
106
|
+
if (typeof c === "string") {
|
|
107
|
+
return c;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
return void 0;
|
|
111
|
+
}
|
|
112
|
+
function mapClaudeCodeToolToShield(toolName, toolInput) {
|
|
113
|
+
const n = toolName.trim().toLowerCase();
|
|
114
|
+
if (n.length === 0) {
|
|
115
|
+
return { service: "unknown", actionType: "execute" };
|
|
116
|
+
}
|
|
117
|
+
if (n === "bash" || n === "shell") {
|
|
118
|
+
const cmd = extractExecCommand(toolInput);
|
|
119
|
+
if (cmd !== void 0 && isDestructiveExecCommand(cmd)) {
|
|
120
|
+
return { service: "terminal", actionType: "write" };
|
|
121
|
+
}
|
|
122
|
+
return { service: "terminal", actionType: "execute" };
|
|
123
|
+
}
|
|
124
|
+
if (n === "glob" || n === "grep") {
|
|
125
|
+
return { service: "filesystem", actionType: "read" };
|
|
126
|
+
}
|
|
127
|
+
if (n === "webfetch") {
|
|
128
|
+
return { service: "web", actionType: "read" };
|
|
129
|
+
}
|
|
130
|
+
if (n === "task") {
|
|
131
|
+
return { service: "subagent", actionType: "execute" };
|
|
132
|
+
}
|
|
133
|
+
const scope = mapToolToScope(n);
|
|
134
|
+
return { service: scope.service, actionType: scope.permissionLevel };
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
exports.extractExecCommand = extractExecCommand;
|
|
138
|
+
exports.mapClaudeCodeToolToShield = mapClaudeCodeToolToShield;
|