protect-mcp 0.5.0 → 0.5.2
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 +152 -161
- package/dist/{chunk-IUFFDQYZ.mjs → chunk-IWDR5WU3.mjs} +1 -1
- package/dist/{chunk-V52W3XIN.mjs → chunk-N4F76LTC.mjs} +12 -8
- package/dist/{chunk-IAJJA5IW.mjs → chunk-Q4KOQUKV.mjs} +1 -1
- package/dist/{chunk-YKM6W6T7.mjs → chunk-W4U5VNEC.mjs} +2 -2
- package/dist/cli.js +12 -8
- package/dist/cli.mjs +4 -4
- package/dist/hook-server.js +12 -8
- package/dist/hook-server.mjs +2 -2
- package/dist/{http-transport-GXIXLVJQ.mjs → http-transport-PWCK7JHZ.mjs} +2 -2
- package/dist/index.d.mts +118 -1
- package/dist/index.d.ts +118 -1
- package/dist/index.js +289 -8
- package/dist/index.mjs +279 -4
- package/package.json +6 -3
package/dist/index.mjs
CHANGED
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
formatSimulation,
|
|
7
7
|
parseLogFile,
|
|
8
8
|
simulate
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-W4U5VNEC.mjs";
|
|
10
10
|
import {
|
|
11
11
|
ProtectGateway,
|
|
12
12
|
buildDecisionContext,
|
|
@@ -18,7 +18,7 @@ import {
|
|
|
18
18
|
resolveCredential,
|
|
19
19
|
sendApprovalNotification,
|
|
20
20
|
validateCredentials
|
|
21
|
-
} from "./chunk-
|
|
21
|
+
} from "./chunk-IWDR5WU3.mjs";
|
|
22
22
|
import {
|
|
23
23
|
createSandboxServer
|
|
24
24
|
} from "./chunk-SU2FZH7U.mjs";
|
|
@@ -30,17 +30,20 @@ import {
|
|
|
30
30
|
} from "./chunk-UEHLYOJY.mjs";
|
|
31
31
|
import {
|
|
32
32
|
startHookServer
|
|
33
|
-
} from "./chunk-
|
|
33
|
+
} from "./chunk-Q4KOQUKV.mjs";
|
|
34
34
|
import {
|
|
35
35
|
checkRateLimit,
|
|
36
|
+
evaluateCedar,
|
|
36
37
|
getSignerInfo,
|
|
37
38
|
getToolPolicy,
|
|
38
39
|
initSigning,
|
|
40
|
+
isCedarAvailable,
|
|
39
41
|
isSigningEnabled,
|
|
42
|
+
loadCedarPolicies,
|
|
40
43
|
loadPolicy,
|
|
41
44
|
parseRateLimit,
|
|
42
45
|
signDecision
|
|
43
|
-
} from "./chunk-
|
|
46
|
+
} from "./chunk-N4F76LTC.mjs";
|
|
44
47
|
import {
|
|
45
48
|
collectSignedReceipts,
|
|
46
49
|
createAuditBundle
|
|
@@ -202,6 +205,273 @@ function validateEvidenceReceipt(receipt) {
|
|
|
202
205
|
return errors;
|
|
203
206
|
}
|
|
204
207
|
|
|
208
|
+
// src/cedar-schema.ts
|
|
209
|
+
function jsonSchemaToCedarType(schema, namespace, path) {
|
|
210
|
+
if (schema.enum) {
|
|
211
|
+
return "String";
|
|
212
|
+
}
|
|
213
|
+
const type = Array.isArray(schema.type) ? schema.type[0] : schema.type;
|
|
214
|
+
switch (type) {
|
|
215
|
+
case "string":
|
|
216
|
+
if (schema.format === "date-time") return "String";
|
|
217
|
+
if (schema.format === "uri") return "String";
|
|
218
|
+
return "String";
|
|
219
|
+
case "integer":
|
|
220
|
+
case "number":
|
|
221
|
+
return "Long";
|
|
222
|
+
case "boolean":
|
|
223
|
+
return "Bool";
|
|
224
|
+
case "array":
|
|
225
|
+
if (schema.items) {
|
|
226
|
+
const itemType = jsonSchemaToCedarType(schema.items, namespace, path + "_item");
|
|
227
|
+
return `Set<${itemType}>`;
|
|
228
|
+
}
|
|
229
|
+
return "Set<String>";
|
|
230
|
+
// Default to Set<String> for untyped arrays
|
|
231
|
+
case "object":
|
|
232
|
+
if (schema.properties && Object.keys(schema.properties).length > 0) {
|
|
233
|
+
const fields = Object.entries(schema.properties).map(([key, prop]) => {
|
|
234
|
+
const cedarType = jsonSchemaToCedarType(prop, namespace, path + "_" + sanitizeIdentifier(key));
|
|
235
|
+
const isRequired = schema.required?.includes(key) ?? false;
|
|
236
|
+
return ` "${sanitizeIdentifier(key)}": ${cedarType}${isRequired ? "" : "?"}`;
|
|
237
|
+
});
|
|
238
|
+
return `{
|
|
239
|
+
${fields.join(",\n")}
|
|
240
|
+
}`;
|
|
241
|
+
}
|
|
242
|
+
return "Record";
|
|
243
|
+
// Empty objects
|
|
244
|
+
default:
|
|
245
|
+
return "String";
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
function sanitizeIdentifier(name) {
|
|
249
|
+
return name.replace(/[^a-zA-Z0-9_]/g, "_").replace(/^(\d)/, "_$1");
|
|
250
|
+
}
|
|
251
|
+
function cedarActionId(toolName) {
|
|
252
|
+
if (/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(toolName)) {
|
|
253
|
+
return toolName;
|
|
254
|
+
}
|
|
255
|
+
return toolName;
|
|
256
|
+
}
|
|
257
|
+
function generateCedarSchema(tools, config = {}) {
|
|
258
|
+
const ns = config.namespace || "ScopeBlind";
|
|
259
|
+
const includeTier = config.includeTier !== false;
|
|
260
|
+
const includeTimestamp = config.includeTimestamp !== false;
|
|
261
|
+
const includeAgentId = config.includeAgentId !== false;
|
|
262
|
+
const agentAttrs = [];
|
|
263
|
+
if (includeTier) agentAttrs.push(' "tier": String');
|
|
264
|
+
if (includeAgentId) agentAttrs.push(' "agent_id": String?');
|
|
265
|
+
const sessionFields = [];
|
|
266
|
+
if (includeTimestamp) sessionFields.push(' "timestamp": String?');
|
|
267
|
+
sessionFields.push(' "hook_event": String?');
|
|
268
|
+
const actionDeclarations = [];
|
|
269
|
+
const inputTypeDeclarations = [];
|
|
270
|
+
for (const tool of tools) {
|
|
271
|
+
const actionName = cedarActionId(tool.name);
|
|
272
|
+
const inputTypeName = `${sanitizeIdentifier(tool.name)}_Input`;
|
|
273
|
+
if (tool.inputSchema?.properties && Object.keys(tool.inputSchema.properties).length > 0) {
|
|
274
|
+
const fields = Object.entries(tool.inputSchema.properties).map(([key, prop]) => {
|
|
275
|
+
const cedarType = jsonSchemaToCedarType(prop, ns, sanitizeIdentifier(tool.name) + "_" + sanitizeIdentifier(key));
|
|
276
|
+
const isRequired = tool.inputSchema?.required?.includes(key) ?? false;
|
|
277
|
+
return ` "${sanitizeIdentifier(key)}": ${cedarType}${isRequired ? "" : "?"}`;
|
|
278
|
+
});
|
|
279
|
+
inputTypeDeclarations.push(
|
|
280
|
+
` // Input type for tool: ${tool.name}` + (tool.description ? `
|
|
281
|
+
// ${tool.description}` : "") + `
|
|
282
|
+
type ${inputTypeName} = {
|
|
283
|
+
${fields.join(",\n")}
|
|
284
|
+
};`
|
|
285
|
+
);
|
|
286
|
+
actionDeclarations.push(
|
|
287
|
+
` action "${actionName}" in [Action::"MCP::Tool::call"] appliesTo {
|
|
288
|
+
principal: [Agent],
|
|
289
|
+
resource: [Tool],
|
|
290
|
+
context: {
|
|
291
|
+
"input": ${inputTypeName},
|
|
292
|
+
"tier": String${includeTimestamp ? ',\n "timestamp": String?' : ""}${includeAgentId ? ',\n "agent_id": String?' : ""}
|
|
293
|
+
}
|
|
294
|
+
};`
|
|
295
|
+
);
|
|
296
|
+
} else {
|
|
297
|
+
actionDeclarations.push(
|
|
298
|
+
` action "${actionName}" in [Action::"MCP::Tool::call"] appliesTo {
|
|
299
|
+
principal: [Agent],
|
|
300
|
+
resource: [Tool],
|
|
301
|
+
context: {
|
|
302
|
+
"tier": String${includeTimestamp ? ',\n "timestamp": String?' : ""}${includeAgentId ? ',\n "agent_id": String?' : ""}
|
|
303
|
+
}
|
|
304
|
+
};`
|
|
305
|
+
);
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
actionDeclarations.push(
|
|
309
|
+
` // Blanket action for policies matching any tool call
|
|
310
|
+
action "MCP::Tool::call" appliesTo {
|
|
311
|
+
principal: [Agent],
|
|
312
|
+
resource: [Tool],
|
|
313
|
+
context: {
|
|
314
|
+
"tier": String${includeTimestamp ? ',\n "timestamp": String?' : ""}${includeAgentId ? ',\n "agent_id": String?' : ""}
|
|
315
|
+
}
|
|
316
|
+
};`
|
|
317
|
+
);
|
|
318
|
+
const schemaText = [
|
|
319
|
+
`// Cedar schema for MCP tool governance`,
|
|
320
|
+
`// Generated by protect-mcp from ${tools.length} tool description(s)`,
|
|
321
|
+
`// Compatible with cedar-policy/cedar-for-agents`,
|
|
322
|
+
``,
|
|
323
|
+
`namespace ${ns} {`,
|
|
324
|
+
``,
|
|
325
|
+
` // \u2500\u2500 Entity types \u2500\u2500`,
|
|
326
|
+
``,
|
|
327
|
+
` entity Agent${agentAttrs.length > 0 ? ` = {
|
|
328
|
+
${agentAttrs.join(",\n")}
|
|
329
|
+
}` : ""};`,
|
|
330
|
+
``,
|
|
331
|
+
` entity Tool;`,
|
|
332
|
+
``,
|
|
333
|
+
...inputTypeDeclarations.length > 0 ? [` // \u2500\u2500 Tool input types \u2500\u2500`, ``, ...inputTypeDeclarations, ``] : [],
|
|
334
|
+
` // \u2500\u2500 Actions \u2500\u2500`,
|
|
335
|
+
``,
|
|
336
|
+
...actionDeclarations,
|
|
337
|
+
``,
|
|
338
|
+
`}`,
|
|
339
|
+
``
|
|
340
|
+
].join("\n");
|
|
341
|
+
const schemaJson = buildSchemaJson(tools, ns, config);
|
|
342
|
+
return {
|
|
343
|
+
schemaText,
|
|
344
|
+
schemaJson,
|
|
345
|
+
toolCount: tools.length,
|
|
346
|
+
tools: tools.map((t) => t.name)
|
|
347
|
+
};
|
|
348
|
+
}
|
|
349
|
+
function buildSchemaJson(tools, namespace, config) {
|
|
350
|
+
const entityTypes = {
|
|
351
|
+
Agent: {
|
|
352
|
+
shape: {
|
|
353
|
+
type: "Record",
|
|
354
|
+
attributes: {
|
|
355
|
+
...config.includeTier !== false ? { tier: { type: "String", required: false } } : {},
|
|
356
|
+
...config.includeAgentId !== false ? { agent_id: { type: "String", required: false } } : {}
|
|
357
|
+
}
|
|
358
|
+
},
|
|
359
|
+
memberOfTypes: []
|
|
360
|
+
},
|
|
361
|
+
Tool: {
|
|
362
|
+
shape: { type: "Record", attributes: {} },
|
|
363
|
+
memberOfTypes: []
|
|
364
|
+
}
|
|
365
|
+
};
|
|
366
|
+
const actions = {};
|
|
367
|
+
for (const tool of tools) {
|
|
368
|
+
const contextAttrs = {
|
|
369
|
+
tier: { type: "String", required: false }
|
|
370
|
+
};
|
|
371
|
+
if (config.includeTimestamp !== false) {
|
|
372
|
+
contextAttrs["timestamp"] = { type: "String", required: false };
|
|
373
|
+
}
|
|
374
|
+
if (config.includeAgentId !== false) {
|
|
375
|
+
contextAttrs["agent_id"] = { type: "String", required: false };
|
|
376
|
+
}
|
|
377
|
+
if (tool.inputSchema?.properties) {
|
|
378
|
+
const inputAttrs = {};
|
|
379
|
+
for (const [key, prop] of Object.entries(tool.inputSchema.properties)) {
|
|
380
|
+
inputAttrs[sanitizeIdentifier(key)] = {
|
|
381
|
+
type: jsonSchemaToSchemaJsonType(prop),
|
|
382
|
+
required: tool.inputSchema.required?.includes(key) ?? false
|
|
383
|
+
};
|
|
384
|
+
}
|
|
385
|
+
contextAttrs["input"] = {
|
|
386
|
+
type: "Record",
|
|
387
|
+
attributes: inputAttrs,
|
|
388
|
+
required: false
|
|
389
|
+
};
|
|
390
|
+
}
|
|
391
|
+
actions[tool.name] = {
|
|
392
|
+
appliesTo: {
|
|
393
|
+
principalTypes: ["Agent"],
|
|
394
|
+
resourceTypes: ["Tool"],
|
|
395
|
+
context: { type: "Record", attributes: contextAttrs }
|
|
396
|
+
},
|
|
397
|
+
memberOf: [{ id: "MCP::Tool::call" }]
|
|
398
|
+
};
|
|
399
|
+
}
|
|
400
|
+
const blanketContext = {
|
|
401
|
+
tier: { type: "String", required: false }
|
|
402
|
+
};
|
|
403
|
+
if (config.includeTimestamp !== false) {
|
|
404
|
+
blanketContext["timestamp"] = { type: "String", required: false };
|
|
405
|
+
}
|
|
406
|
+
if (config.includeAgentId !== false) {
|
|
407
|
+
blanketContext["agent_id"] = { type: "String", required: false };
|
|
408
|
+
}
|
|
409
|
+
actions["MCP::Tool::call"] = {
|
|
410
|
+
appliesTo: {
|
|
411
|
+
principalTypes: ["Agent"],
|
|
412
|
+
resourceTypes: ["Tool"],
|
|
413
|
+
context: { type: "Record", attributes: blanketContext }
|
|
414
|
+
}
|
|
415
|
+
};
|
|
416
|
+
return {
|
|
417
|
+
[namespace]: {
|
|
418
|
+
entityTypes,
|
|
419
|
+
actions
|
|
420
|
+
}
|
|
421
|
+
};
|
|
422
|
+
}
|
|
423
|
+
function jsonSchemaToSchemaJsonType(schema) {
|
|
424
|
+
if (schema.enum) return "String";
|
|
425
|
+
const type = Array.isArray(schema.type) ? schema.type[0] : schema.type;
|
|
426
|
+
switch (type) {
|
|
427
|
+
case "string":
|
|
428
|
+
return "String";
|
|
429
|
+
case "integer":
|
|
430
|
+
case "number":
|
|
431
|
+
return "Long";
|
|
432
|
+
case "boolean":
|
|
433
|
+
return "EntityOrCommon";
|
|
434
|
+
// Cedar JSON schema uses this for Bool
|
|
435
|
+
case "array":
|
|
436
|
+
return "Set";
|
|
437
|
+
default:
|
|
438
|
+
return "String";
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
function generateSchemaStub(namespace = "ScopeBlind") {
|
|
442
|
+
return [
|
|
443
|
+
`// Cedar schema stub for protect-mcp`,
|
|
444
|
+
`// This defines the principal and resource entity types.`,
|
|
445
|
+
`// Tool-specific actions are auto-generated from MCP tools/list.`,
|
|
446
|
+
`//`,
|
|
447
|
+
`// Compatible with cedar-policy/cedar-for-agents @mcp_principal/@mcp_resource annotations.`,
|
|
448
|
+
`// See: https://github.com/cedar-policy/cedar-for-agents`,
|
|
449
|
+
``,
|
|
450
|
+
`namespace ${namespace} {`,
|
|
451
|
+
``,
|
|
452
|
+
` // @mcp_principal`,
|
|
453
|
+
` entity Agent = {`,
|
|
454
|
+
` "tier": String,`,
|
|
455
|
+
` "agent_id": String?`,
|
|
456
|
+
` };`,
|
|
457
|
+
``,
|
|
458
|
+
` // @mcp_resource`,
|
|
459
|
+
` entity Tool;`,
|
|
460
|
+
``,
|
|
461
|
+
` // @mcp_action`,
|
|
462
|
+
` action "MCP::Tool::call" appliesTo {`,
|
|
463
|
+
` principal: [Agent],`,
|
|
464
|
+
` resource: [Tool],`,
|
|
465
|
+
` context: {`,
|
|
466
|
+
` "tier": String`,
|
|
467
|
+
` }`,
|
|
468
|
+
` };`,
|
|
469
|
+
``,
|
|
470
|
+
`}`,
|
|
471
|
+
``
|
|
472
|
+
].join("\n");
|
|
473
|
+
}
|
|
474
|
+
|
|
205
475
|
// src/rekor-anchor.ts
|
|
206
476
|
import { createHash } from "crypto";
|
|
207
477
|
var REKOR_API = "https://rekor.sigstore.dev/api/v1";
|
|
@@ -1452,18 +1722,21 @@ export {
|
|
|
1452
1722
|
createSandboxServer,
|
|
1453
1723
|
destroySandbox,
|
|
1454
1724
|
ed25519ToDIDKey,
|
|
1725
|
+
evaluateCedar,
|
|
1455
1726
|
evaluateTier,
|
|
1456
1727
|
exportC2PAManifestJSON,
|
|
1457
1728
|
exportJSONL,
|
|
1458
1729
|
formatReportMarkdown,
|
|
1459
1730
|
formatSimulation,
|
|
1460
1731
|
generateC2PACommand,
|
|
1732
|
+
generateCedarSchema,
|
|
1461
1733
|
generateDatasetCard,
|
|
1462
1734
|
generateHFMetadata,
|
|
1463
1735
|
generateHookSettings,
|
|
1464
1736
|
generateReport,
|
|
1465
1737
|
generateSafetyTranscript,
|
|
1466
1738
|
generateSampleCedarPolicy,
|
|
1739
|
+
generateSchemaStub,
|
|
1467
1740
|
generateVerifyReceiptSkill,
|
|
1468
1741
|
getSignerInfo,
|
|
1469
1742
|
getToolPolicy,
|
|
@@ -1471,11 +1744,13 @@ export {
|
|
|
1471
1744
|
hashResponseBody,
|
|
1472
1745
|
initSigning,
|
|
1473
1746
|
isAgentId,
|
|
1747
|
+
isCedarAvailable,
|
|
1474
1748
|
isDisclosureMode,
|
|
1475
1749
|
isEvidenceType,
|
|
1476
1750
|
isManifestStatus,
|
|
1477
1751
|
isSigningEnabled,
|
|
1478
1752
|
listCredentialLabels,
|
|
1753
|
+
loadCedarPolicies,
|
|
1479
1754
|
loadPolicy,
|
|
1480
1755
|
manifestToVC,
|
|
1481
1756
|
meetsMinTier,
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "protect-mcp",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.2",
|
|
4
4
|
"mcpName": "io.github.tomjwxf/protect-mcp",
|
|
5
|
-
"description": "
|
|
5
|
+
"description": "Enterprise security gateway for MCP servers and Claude Code hooks. Cedar policies, Ed25519-signed receipts, swarm tracking, and tamper detection. Shadow or enforce mode.",
|
|
6
6
|
"main": "dist/index.js",
|
|
7
7
|
"types": "dist/index.d.ts",
|
|
8
8
|
"module": "dist/index.mjs",
|
|
@@ -46,7 +46,10 @@
|
|
|
46
46
|
"receipts",
|
|
47
47
|
"trust-tiers",
|
|
48
48
|
"agent-governance",
|
|
49
|
-
"claude-code"
|
|
49
|
+
"claude-code",
|
|
50
|
+
"hooks",
|
|
51
|
+
"swarm",
|
|
52
|
+
"compliance"
|
|
50
53
|
],
|
|
51
54
|
"author": "Tom Farley <tommy@scopeblind.com>",
|
|
52
55
|
"license": "MIT",
|