@permission-protocol/mcp-guard 0.1.0 → 0.2.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 +45 -0
- package/LICENSE +21 -0
- package/README.md +101 -59
- package/SECURITY.md +40 -0
- package/dist/src/approval-server.d.ts +69 -3
- package/dist/src/approval-server.js +345 -86
- package/dist/src/approval-server.js.map +1 -1
- package/dist/src/approval-ui.d.ts +5 -0
- package/dist/src/approval-ui.js +120 -81
- package/dist/src/approval-ui.js.map +1 -1
- package/dist/src/classify.d.ts +29 -0
- package/dist/src/classify.js +107 -0
- package/dist/src/classify.js.map +1 -0
- package/dist/src/cli.js +49 -12
- package/dist/src/cli.js.map +1 -1
- package/dist/src/config.d.ts +21 -1
- package/dist/src/config.js +92 -5
- package/dist/src/config.js.map +1 -1
- package/dist/src/enforcement/index.d.ts +12 -0
- package/dist/src/enforcement/index.js +53 -0
- package/dist/src/enforcement/index.js.map +1 -0
- package/dist/src/enforcement/policy.d.ts +29 -0
- package/dist/src/enforcement/policy.js +60 -0
- package/dist/src/enforcement/policy.js.map +1 -0
- package/dist/src/enforcement/types.d.ts +19 -0
- package/dist/src/enforcement/types.js +3 -0
- package/dist/src/enforcement/types.js.map +1 -0
- package/dist/src/enforcement/verifier.d.ts +4 -0
- package/dist/src/enforcement/verifier.js +110 -0
- package/dist/src/enforcement/verifier.js.map +1 -0
- package/dist/src/engine.d.ts +11 -2
- package/dist/src/engine.js +20 -14
- package/dist/src/engine.js.map +1 -1
- package/dist/src/frame.d.ts +67 -0
- package/dist/src/frame.js +113 -0
- package/dist/src/frame.js.map +1 -0
- package/dist/src/pending.d.ts +108 -12
- package/dist/src/pending.js +174 -24
- package/dist/src/pending.js.map +1 -1
- package/dist/src/proxy.d.ts +29 -2
- package/dist/src/proxy.js +274 -105
- package/dist/src/proxy.js.map +1 -1
- package/dist/src/receipt-verify.d.ts +36 -0
- package/dist/src/receipt-verify.js +88 -0
- package/dist/src/receipt-verify.js.map +1 -0
- package/dist/src/receipt.d.ts +81 -7
- package/dist/src/receipt.js +100 -17
- package/dist/src/receipt.js.map +1 -1
- package/dist/src/signer.d.ts +38 -0
- package/dist/src/signer.js +112 -0
- package/dist/src/signer.js.map +1 -0
- package/dist/src/verify-cli.d.ts +1 -0
- package/dist/src/verify-cli.js +133 -0
- package/dist/src/verify-cli.js.map +1 -0
- package/dist/src/verify-core.d.ts +22 -0
- package/dist/src/verify-core.js +50 -0
- package/dist/src/verify-core.js.map +1 -0
- package/dist/src/webhook.d.ts +35 -0
- package/dist/src/webhook.js +57 -0
- package/dist/src/webhook.js.map +1 -0
- package/example/test.sh +5 -7
- package/package.json +33 -7
- package/templates/github-actions/receipt-gate.yml +64 -0
- package/assets/demo.gif +0 -0
- package/demo/_demo-scenario.sh +0 -43
- package/dist/src/tests/config.test.d.ts +0 -1
- package/dist/src/tests/config.test.js +0 -63
- package/dist/src/tests/config.test.js.map +0 -1
- package/dist/src/tests/engine.test.d.ts +0 -1
- package/dist/src/tests/engine.test.js +0 -46
- package/dist/src/tests/engine.test.js.map +0 -1
- package/dist/src/tests/receipt.test.d.ts +0 -1
- package/dist/src/tests/receipt.test.js +0 -44
- package/dist/src/tests/receipt.test.js.map +0 -1
- package/docs/index.html +0 -771
- package/permission-protocol-mcp-guard-0.1.0.tgz +0 -0
- package/src/approval-server.ts +0 -136
- package/src/approval-ui.ts +0 -154
- package/src/cli.ts +0 -84
- package/src/config.ts +0 -68
- package/src/engine.ts +0 -31
- package/src/pending.ts +0 -65
- package/src/proxy.ts +0 -173
- package/src/receipt.ts +0 -52
- package/src/tests/config.test.ts +0 -61
- package/src/tests/engine.test.ts +0 -47
- package/src/tests/receipt.test.ts +0 -44
- package/tsconfig.json +0 -16
package/dist/src/config.js
CHANGED
|
@@ -1,9 +1,21 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.TOOLS_CALL = exports.DEFAULT_RECEIPT_TTL_SECONDS = exports.DEFAULT_APPROVAL_TTL_SECONDS = void 0;
|
|
3
4
|
exports.loadConfig = loadConfig;
|
|
5
|
+
exports.parseConfig = parseConfig;
|
|
6
|
+
const node_crypto_1 = require("node:crypto");
|
|
4
7
|
const node_fs_1 = require("node:fs");
|
|
5
8
|
const yaml_1 = require("yaml");
|
|
9
|
+
exports.DEFAULT_APPROVAL_TTL_SECONDS = 900;
|
|
10
|
+
exports.DEFAULT_RECEIPT_TTL_SECONDS = 900;
|
|
11
|
+
exports.TOOLS_CALL = 'tools/call';
|
|
6
12
|
const VALID_ACTIONS = ['allow', 'block', 'require_approval'];
|
|
13
|
+
const VALID_MODES = ['enforce', 'observe'];
|
|
14
|
+
const VALID_REVERSIBILITY = ['reversible', 'irreversible'];
|
|
15
|
+
const TOP_LEVEL_KEYS = new Set(['default_action', 'mode', 'rules', 'approval_ttl_seconds', 'receipt_ttl_seconds']);
|
|
16
|
+
const RULE_KEYS = new Set([
|
|
17
|
+
'id', 'method', 'tool', 'action', 'reversibility', 'countdown_seconds', 'cap_usd', 'amount_arg', 'verify_threshold',
|
|
18
|
+
]);
|
|
7
19
|
function loadConfig(configPath) {
|
|
8
20
|
let raw;
|
|
9
21
|
try {
|
|
@@ -12,37 +24,112 @@ function loadConfig(configPath) {
|
|
|
12
24
|
catch (err) {
|
|
13
25
|
throw new Error(`Failed to read config file: ${configPath} (${err.code ?? err.message})`);
|
|
14
26
|
}
|
|
27
|
+
return parseConfig(raw);
|
|
28
|
+
}
|
|
29
|
+
/** Parse and strictly validate a policy. Anything unrecognized is an error, never ignored. */
|
|
30
|
+
function parseConfig(raw) {
|
|
15
31
|
const parsed = (0, yaml_1.parse)(raw);
|
|
16
|
-
if (!parsed || typeof parsed !== 'object') {
|
|
32
|
+
if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
|
|
17
33
|
throw new Error('Config must be a YAML object');
|
|
18
34
|
}
|
|
35
|
+
for (const key of Object.keys(parsed)) {
|
|
36
|
+
if (!TOP_LEVEL_KEYS.has(key))
|
|
37
|
+
throw new Error(`Unknown config key: "${key}"`);
|
|
38
|
+
}
|
|
19
39
|
if (!VALID_ACTIONS.includes(parsed.default_action)) {
|
|
20
40
|
throw new Error(`Invalid default_action: "${parsed.default_action}". Must be one of: ${VALID_ACTIONS.join(', ')}`);
|
|
21
41
|
}
|
|
22
|
-
const VALID_MODES = ['enforce', 'observe'];
|
|
23
42
|
const mode = parsed.mode ?? 'enforce';
|
|
24
43
|
if (!VALID_MODES.includes(mode)) {
|
|
25
44
|
throw new Error(`Invalid mode: "${parsed.mode}". Must be one of: ${VALID_MODES.join(', ')}`);
|
|
26
45
|
}
|
|
46
|
+
const approvalTtl = positiveInt(parsed.approval_ttl_seconds, 'approval_ttl_seconds', exports.DEFAULT_APPROVAL_TTL_SECONDS);
|
|
47
|
+
const receiptTtl = positiveInt(parsed.receipt_ttl_seconds, 'receipt_ttl_seconds', exports.DEFAULT_RECEIPT_TTL_SECONDS);
|
|
27
48
|
if (!Array.isArray(parsed.rules)) {
|
|
28
49
|
throw new Error('Config must include a "rules" array');
|
|
29
50
|
}
|
|
51
|
+
const seenIds = new Set();
|
|
52
|
+
const seenTargets = new Set();
|
|
30
53
|
const rules = parsed.rules.map((r, i) => {
|
|
54
|
+
if (!r || typeof r !== 'object' || Array.isArray(r))
|
|
55
|
+
throw new Error(`Rule ${i} must be an object`);
|
|
56
|
+
for (const key of Object.keys(r)) {
|
|
57
|
+
if (!RULE_KEYS.has(key))
|
|
58
|
+
throw new Error(`Rule ${i} has unknown key: "${key}"`);
|
|
59
|
+
}
|
|
31
60
|
if (!r.id || typeof r.id !== 'string') {
|
|
32
61
|
throw new Error(`Rule ${i} missing valid "id"`);
|
|
33
62
|
}
|
|
34
|
-
if (
|
|
35
|
-
throw new Error(`Rule ${i}
|
|
63
|
+
if (seenIds.has(r.id))
|
|
64
|
+
throw new Error(`Rule ${i} duplicates id "${r.id}"`);
|
|
65
|
+
seenIds.add(r.id);
|
|
66
|
+
const method = r.method ?? exports.TOOLS_CALL;
|
|
67
|
+
if (typeof method !== 'string' || !method)
|
|
68
|
+
throw new Error(`Rule ${i} has invalid method`);
|
|
69
|
+
if (method === exports.TOOLS_CALL) {
|
|
70
|
+
if (!r.tool || typeof r.tool !== 'string')
|
|
71
|
+
throw new Error(`Rule ${i} missing valid "tool"`);
|
|
72
|
+
}
|
|
73
|
+
else if (r.tool !== undefined) {
|
|
74
|
+
throw new Error(`Rule ${i}: "tool" applies only to ${exports.TOOLS_CALL} rules`);
|
|
36
75
|
}
|
|
76
|
+
const target = `${method}\u0000${r.tool ?? ''}`;
|
|
77
|
+
if (seenTargets.has(target)) {
|
|
78
|
+
throw new Error(`Rule ${i} duplicates another rule for ${r.tool ? `tool "${r.tool}"` : `method "${method}"`}`);
|
|
79
|
+
}
|
|
80
|
+
seenTargets.add(target);
|
|
37
81
|
if (!VALID_ACTIONS.includes(r.action)) {
|
|
38
82
|
throw new Error(`Rule ${i} has invalid action: "${r.action}"`);
|
|
39
83
|
}
|
|
40
|
-
|
|
84
|
+
if (r.reversibility !== undefined && !VALID_REVERSIBILITY.includes(r.reversibility)) {
|
|
85
|
+
throw new Error(`Rule ${i} has invalid reversibility: "${r.reversibility}". Must be one of: ${VALID_REVERSIBILITY.join(', ')}`);
|
|
86
|
+
}
|
|
87
|
+
for (const numField of ['countdown_seconds', 'cap_usd', 'verify_threshold']) {
|
|
88
|
+
if (r[numField] !== undefined && (typeof r[numField] !== 'number' || !Number.isFinite(r[numField]) || r[numField] < 0)) {
|
|
89
|
+
throw new Error(`Rule ${i} has invalid ${numField}: must be a non-negative number`);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
if (r.verify_threshold !== undefined && r.verify_threshold > 100) {
|
|
93
|
+
throw new Error(`Rule ${i} has invalid verify_threshold: must be 0-100`);
|
|
94
|
+
}
|
|
95
|
+
if (r.countdown_seconds !== undefined && r.countdown_seconds > 0 && r.reversibility !== 'reversible') {
|
|
96
|
+
throw new Error(`Rule ${i}: countdown_seconds requires reversibility: reversible`);
|
|
97
|
+
}
|
|
98
|
+
if (r.cap_usd !== undefined && (typeof r.amount_arg !== 'string' || !r.amount_arg)) {
|
|
99
|
+
throw new Error(`Rule ${i}: cap_usd requires amount_arg (the tool argument holding the amount)`);
|
|
100
|
+
}
|
|
101
|
+
if (r.amount_arg !== undefined && r.cap_usd === undefined) {
|
|
102
|
+
throw new Error(`Rule ${i}: amount_arg is only valid with cap_usd`);
|
|
103
|
+
}
|
|
104
|
+
if (r.amount_arg !== undefined && r.amount_arg.startsWith('_pp')) {
|
|
105
|
+
throw new Error(`Rule ${i}: amount_arg cannot read agent metadata (_pp)`);
|
|
106
|
+
}
|
|
107
|
+
return {
|
|
108
|
+
id: r.id,
|
|
109
|
+
method,
|
|
110
|
+
...(r.tool !== undefined ? { tool: r.tool } : {}),
|
|
111
|
+
action: r.action,
|
|
112
|
+
...(r.reversibility !== undefined ? { reversibility: r.reversibility } : {}),
|
|
113
|
+
...(r.countdown_seconds !== undefined ? { countdown_seconds: r.countdown_seconds } : {}),
|
|
114
|
+
...(r.cap_usd !== undefined ? { cap_usd: r.cap_usd, amount_arg: r.amount_arg } : {}),
|
|
115
|
+
...(r.verify_threshold !== undefined ? { verify_threshold: r.verify_threshold } : {}),
|
|
116
|
+
};
|
|
41
117
|
});
|
|
42
118
|
return {
|
|
43
119
|
default_action: parsed.default_action,
|
|
44
120
|
mode,
|
|
45
121
|
rules,
|
|
122
|
+
approval_ttl_seconds: approvalTtl,
|
|
123
|
+
receipt_ttl_seconds: receiptTtl,
|
|
124
|
+
policy_sha256: (0, node_crypto_1.createHash)('sha256').update(raw).digest('hex'),
|
|
46
125
|
};
|
|
47
126
|
}
|
|
127
|
+
function positiveInt(v, name, fallback) {
|
|
128
|
+
if (v === undefined)
|
|
129
|
+
return fallback;
|
|
130
|
+
if (typeof v !== 'number' || !Number.isInteger(v) || v <= 0) {
|
|
131
|
+
throw new Error(`Invalid ${name}: must be a positive integer`);
|
|
132
|
+
}
|
|
133
|
+
return v;
|
|
134
|
+
}
|
|
48
135
|
//# sourceMappingURL=config.js.map
|
package/dist/src/config.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/config.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/config.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { CanonicalPayload, SignedToken } from './types';
|
|
2
|
+
import { loadPolicy, getToolPolicy, ToolPolicy } from './policy';
|
|
3
|
+
import { verifyAuthorization } from './verifier';
|
|
4
|
+
export { CanonicalPayload, SignedToken, ToolPolicy, loadPolicy, getToolPolicy, verifyAuthorization };
|
|
5
|
+
export declare function interceptToolCall(jsonRpcMessage: any, publicKeyPem: string, expectedKeyId?: string): Promise<{
|
|
6
|
+
allowed: boolean;
|
|
7
|
+
reason?: string;
|
|
8
|
+
payload?: CanonicalPayload;
|
|
9
|
+
token?: SignedToken;
|
|
10
|
+
policy?: ToolPolicy;
|
|
11
|
+
policy_hash?: string;
|
|
12
|
+
}>;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.verifyAuthorization = exports.getToolPolicy = exports.loadPolicy = void 0;
|
|
4
|
+
exports.interceptToolCall = interceptToolCall;
|
|
5
|
+
const policy_1 = require("./policy");
|
|
6
|
+
Object.defineProperty(exports, "loadPolicy", { enumerable: true, get: function () { return policy_1.loadPolicy; } });
|
|
7
|
+
Object.defineProperty(exports, "getToolPolicy", { enumerable: true, get: function () { return policy_1.getToolPolicy; } });
|
|
8
|
+
const verifier_1 = require("./verifier");
|
|
9
|
+
Object.defineProperty(exports, "verifyAuthorization", { enumerable: true, get: function () { return verifier_1.verifyAuthorization; } });
|
|
10
|
+
async function interceptToolCall(jsonRpcMessage, publicKeyPem, expectedKeyId = 'pp-prod-1') {
|
|
11
|
+
if (jsonRpcMessage.method !== 'tools/call') {
|
|
12
|
+
return { allowed: true };
|
|
13
|
+
}
|
|
14
|
+
const toolName = jsonRpcMessage.params?.name;
|
|
15
|
+
let policy;
|
|
16
|
+
let policyHash;
|
|
17
|
+
try {
|
|
18
|
+
policy = (0, policy_1.getToolPolicy)(toolName);
|
|
19
|
+
policyHash = (0, policy_1.loadPolicy)().policy_hash;
|
|
20
|
+
}
|
|
21
|
+
catch (err) {
|
|
22
|
+
// No policy means no basis to allow anything.
|
|
23
|
+
return { allowed: false, reason: err.message };
|
|
24
|
+
}
|
|
25
|
+
if (policy?.protected) {
|
|
26
|
+
const args = jsonRpcMessage.params?.arguments || {};
|
|
27
|
+
const authorization = args._authorization;
|
|
28
|
+
if (!authorization) {
|
|
29
|
+
return { allowed: false, reason: 'Missing _authorization block' };
|
|
30
|
+
}
|
|
31
|
+
const payload = authorization.payload;
|
|
32
|
+
const token = authorization.token;
|
|
33
|
+
if (!payload || !token) {
|
|
34
|
+
return { allowed: false, reason: 'Malformed authorization block' };
|
|
35
|
+
}
|
|
36
|
+
const { _authorization, ...executionArgs } = args;
|
|
37
|
+
if (payload.action !== toolName) {
|
|
38
|
+
return { allowed: false, reason: 'Payload action mismatch' };
|
|
39
|
+
}
|
|
40
|
+
if (JSON.stringify(executionArgs) !== JSON.stringify(payload.args)) {
|
|
41
|
+
return { allowed: false, reason: 'Arguments mismatch' };
|
|
42
|
+
}
|
|
43
|
+
try {
|
|
44
|
+
await (0, verifier_1.verifyAuthorization)(payload, token, publicKeyPem, policy, expectedKeyId);
|
|
45
|
+
return { allowed: true, payload, token, policy, policy_hash: policyHash };
|
|
46
|
+
}
|
|
47
|
+
catch (err) {
|
|
48
|
+
return { allowed: false, reason: err.message };
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return { allowed: true }; // Unprotected tools pass through
|
|
52
|
+
}
|
|
53
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/enforcement/index.ts"],"names":[],"mappings":";;;AAaA,8CAqDC;AAjED,qCAAiE;AAO/D,2FAPO,mBAAU,OAOP;AACV,8FARmB,sBAAa,OAQnB;AAPf,yCAAiD;AAQ/C,oGARO,8BAAmB,OAQP;AAGd,KAAK,UAAU,iBAAiB,CACrC,cAAmB,EACnB,YAAoB,EACpB,gBAAwB,WAAW;IAEnC,IAAI,cAAc,CAAC,MAAM,KAAK,YAAY,EAAE,CAAC;QAC3C,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;IAC3B,CAAC;IAED,MAAM,QAAQ,GAAG,cAAc,CAAC,MAAM,EAAE,IAAI,CAAC;IAC7C,IAAI,MAAyB,CAAC;IAC9B,IAAI,UAA8B,CAAC;IACnC,IAAI,CAAC;QACH,MAAM,GAAG,IAAA,sBAAa,EAAC,QAAQ,CAAC,CAAC;QACjC,UAAU,GAAG,IAAA,mBAAU,GAAE,CAAC,WAAW,CAAC;IACxC,CAAC;IAAC,OAAO,GAAQ,EAAE,CAAC;QAClB,8CAA8C;QAC9C,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,CAAC,OAAO,EAAE,CAAC;IACjD,CAAC;IAED,IAAI,MAAM,EAAE,SAAS,EAAE,CAAC;QACtB,MAAM,IAAI,GAAG,cAAc,CAAC,MAAM,EAAE,SAAS,IAAI,EAAE,CAAC;QACpD,MAAM,aAAa,GAAG,IAAI,CAAC,cAAc,CAAC;QAC1C,IAAI,CAAC,aAAa,EAAE,CAAC;YACnB,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,8BAA8B,EAAE,CAAC;QACpE,CAAC;QAED,MAAM,OAAO,GAAqB,aAAa,CAAC,OAAO,CAAC;QACxD,MAAM,KAAK,GAAgB,aAAa,CAAC,KAAK,CAAC;QAE/C,IAAI,CAAC,OAAO,IAAI,CAAC,KAAK,EAAE,CAAC;YACvB,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,+BAA+B,EAAE,CAAC;QACrE,CAAC;QAED,MAAM,EAAE,cAAc,EAAE,GAAG,aAAa,EAAE,GAAG,IAAI,CAAC;QAElD,IAAI,OAAO,CAAC,MAAM,KAAK,QAAQ,EAAE,CAAC;YAChC,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,yBAAyB,EAAE,CAAC;QAC/D,CAAC;QAED,IAAI,IAAI,CAAC,SAAS,CAAC,aAAa,CAAC,KAAK,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;YACnE,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,oBAAoB,EAAE,CAAC;QAC1D,CAAC;QAED,IAAI,CAAC;YACH,MAAM,IAAA,8BAAmB,EAAC,OAAO,EAAE,KAAK,EAAE,YAAY,EAAE,MAAM,EAAE,aAAa,CAAC,CAAC;YAC/E,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,UAAU,EAAE,CAAC;QAC5E,CAAC;QAAC,OAAO,GAAQ,EAAE,CAAC;YAClB,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,CAAC,OAAO,EAAE,CAAC;QACjD,CAAC;IACH,CAAC;IAED,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,iCAAiC;AAC7D,CAAC"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export interface ToolPolicy {
|
|
2
|
+
protected: boolean;
|
|
3
|
+
risk?: string;
|
|
4
|
+
required_authorization?: string;
|
|
5
|
+
allowed_signers?: string[];
|
|
6
|
+
allowed_roles?: string[];
|
|
7
|
+
required_approvals?: number;
|
|
8
|
+
conditions?: Array<{
|
|
9
|
+
field: string;
|
|
10
|
+
equals: any;
|
|
11
|
+
}>;
|
|
12
|
+
max_ttl_seconds?: number;
|
|
13
|
+
}
|
|
14
|
+
export interface PPConfig {
|
|
15
|
+
tools: Record<string, ToolPolicy>;
|
|
16
|
+
policy_hash?: string;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* A policy that cannot be loaded is an error, never "no tool is protected".
|
|
20
|
+
* Callers treat it as a denial.
|
|
21
|
+
*/
|
|
22
|
+
export declare class PolicyError extends Error {
|
|
23
|
+
readonly code: 'PP_POLICY_MISSING' | 'PP_POLICY_INVALID';
|
|
24
|
+
constructor(code: 'PP_POLICY_MISSING' | 'PP_POLICY_INVALID', message: string);
|
|
25
|
+
}
|
|
26
|
+
export declare function loadPolicy(configPathArg?: string): PPConfig;
|
|
27
|
+
/** Test helper: forget the cached policy. */
|
|
28
|
+
export declare function _resetPolicyCache(): void;
|
|
29
|
+
export declare function getToolPolicy(toolName: string): ToolPolicy | null;
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.PolicyError = void 0;
|
|
7
|
+
exports.loadPolicy = loadPolicy;
|
|
8
|
+
exports._resetPolicyCache = _resetPolicyCache;
|
|
9
|
+
exports.getToolPolicy = getToolPolicy;
|
|
10
|
+
const crypto_1 = __importDefault(require("crypto"));
|
|
11
|
+
const fs_1 = __importDefault(require("fs"));
|
|
12
|
+
const path_1 = __importDefault(require("path"));
|
|
13
|
+
const yaml_1 = require("yaml");
|
|
14
|
+
/**
|
|
15
|
+
* A policy that cannot be loaded is an error, never "no tool is protected".
|
|
16
|
+
* Callers treat it as a denial.
|
|
17
|
+
*/
|
|
18
|
+
class PolicyError extends Error {
|
|
19
|
+
code;
|
|
20
|
+
constructor(code, message) {
|
|
21
|
+
super(message);
|
|
22
|
+
this.code = code;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
exports.PolicyError = PolicyError;
|
|
26
|
+
let cachedConfig = null;
|
|
27
|
+
function loadPolicy(configPathArg) {
|
|
28
|
+
if (cachedConfig && !configPathArg)
|
|
29
|
+
return cachedConfig;
|
|
30
|
+
const configPath = configPathArg || process.env.PP_CONFIG_PATH || path_1.default.join(process.cwd(), 'pp.config.yaml');
|
|
31
|
+
if (!fs_1.default.existsSync(configPath)) {
|
|
32
|
+
throw new PolicyError('PP_POLICY_MISSING', `Policy file not found: ${configPath}`);
|
|
33
|
+
}
|
|
34
|
+
const fileContents = fs_1.default.readFileSync(configPath, 'utf8');
|
|
35
|
+
let parsed;
|
|
36
|
+
try {
|
|
37
|
+
parsed = (0, yaml_1.parse)(fileContents);
|
|
38
|
+
}
|
|
39
|
+
catch (err) {
|
|
40
|
+
throw new PolicyError('PP_POLICY_INVALID', `Policy file is not valid YAML: ${configPath} (${err.message})`);
|
|
41
|
+
}
|
|
42
|
+
const tools = parsed?.tools;
|
|
43
|
+
if (!tools || typeof tools !== 'object' || Array.isArray(tools)) {
|
|
44
|
+
throw new PolicyError('PP_POLICY_INVALID', `Policy file has no "tools" map: ${configPath}`);
|
|
45
|
+
}
|
|
46
|
+
cachedConfig = {
|
|
47
|
+
tools,
|
|
48
|
+
policy_hash: crypto_1.default.createHash('sha256').update(fileContents).digest('hex').substring(0, 12),
|
|
49
|
+
};
|
|
50
|
+
return cachedConfig;
|
|
51
|
+
}
|
|
52
|
+
/** Test helper: forget the cached policy. */
|
|
53
|
+
function _resetPolicyCache() {
|
|
54
|
+
cachedConfig = null;
|
|
55
|
+
}
|
|
56
|
+
function getToolPolicy(toolName) {
|
|
57
|
+
const config = loadPolicy();
|
|
58
|
+
return config.tools[toolName] || null;
|
|
59
|
+
}
|
|
60
|
+
//# sourceMappingURL=policy.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"policy.js","sourceRoot":"","sources":["../../../src/enforcement/policy.ts"],"names":[],"mappings":";;;;;;AAiCA,gCAyBC;AAGD,8CAEC;AAED,sCAGC;AApED,oDAA4B;AAC5B,4CAAoB;AACpB,gDAAwB;AACxB,+BAA6B;AAkB7B;;;GAGG;AACH,MAAa,WAAY,SAAQ,KAAK;IACf;IAArB,YAAqB,IAA+C,EAAE,OAAe;QACnF,KAAK,CAAC,OAAO,CAAC,CAAC;QADI,SAAI,GAAJ,IAAI,CAA2C;IAEpE,CAAC;CACF;AAJD,kCAIC;AAED,IAAI,YAAY,GAAoB,IAAI,CAAC;AAEzC,SAAgB,UAAU,CAAC,aAAsB;IAC/C,IAAI,YAAY,IAAI,CAAC,aAAa;QAAE,OAAO,YAAY,CAAC;IAExD,MAAM,UAAU,GAAG,aAAa,IAAI,OAAO,CAAC,GAAG,CAAC,cAAc,IAAI,cAAI,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,gBAAgB,CAAC,CAAC;IAC7G,IAAI,CAAC,YAAE,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;QAC/B,MAAM,IAAI,WAAW,CAAC,mBAAmB,EAAE,0BAA0B,UAAU,EAAE,CAAC,CAAC;IACrF,CAAC;IAED,MAAM,YAAY,GAAG,YAAE,CAAC,YAAY,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC;IACzD,IAAI,MAAe,CAAC;IACpB,IAAI,CAAC;QACH,MAAM,GAAG,IAAA,YAAK,EAAC,YAAY,CAAC,CAAC;IAC/B,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,IAAI,WAAW,CAAC,mBAAmB,EAAE,kCAAkC,UAAU,KAAM,GAAa,CAAC,OAAO,GAAG,CAAC,CAAC;IACzH,CAAC;IACD,MAAM,KAAK,GAAI,MAA0B,EAAE,KAAK,CAAC;IACjD,IAAI,CAAC,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QAChE,MAAM,IAAI,WAAW,CAAC,mBAAmB,EAAE,mCAAmC,UAAU,EAAE,CAAC,CAAC;IAC9F,CAAC;IAED,YAAY,GAAG;QACb,KAAK;QACL,WAAW,EAAE,gBAAM,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,SAAS,CAAC,CAAC,EAAE,EAAE,CAAC;KAC7F,CAAC;IACF,OAAO,YAAY,CAAC;AACtB,CAAC;AAED,6CAA6C;AAC7C,SAAgB,iBAAiB;IAC/B,YAAY,GAAG,IAAI,CAAC;AACtB,CAAC;AAED,SAAgB,aAAa,CAAC,QAAgB;IAC5C,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC;IAC5B,OAAO,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,IAAI,IAAI,CAAC;AACxC,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export interface CanonicalPayload {
|
|
2
|
+
request_id: string;
|
|
3
|
+
action: string;
|
|
4
|
+
args: Record<string, any>;
|
|
5
|
+
issued_at: number;
|
|
6
|
+
expires_at: number;
|
|
7
|
+
nonce: string;
|
|
8
|
+
}
|
|
9
|
+
export interface SignedToken {
|
|
10
|
+
request_id: string;
|
|
11
|
+
decision: string;
|
|
12
|
+
payload_hash: string;
|
|
13
|
+
signers: string[];
|
|
14
|
+
roles: string[];
|
|
15
|
+
signed_at: number;
|
|
16
|
+
expires_at: number;
|
|
17
|
+
key_id: string;
|
|
18
|
+
signature: string;
|
|
19
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/enforcement/types.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { CanonicalPayload, SignedToken } from './types';
|
|
2
|
+
import { ToolPolicy } from './policy';
|
|
3
|
+
export declare function buildAuthorizationSigningBytes(token: SignedToken): string;
|
|
4
|
+
export declare function verifyAuthorization(payload: CanonicalPayload, token: SignedToken, publicKeyPem: string, policy: ToolPolicy, expectedKeyId?: string): Promise<boolean>;
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.buildAuthorizationSigningBytes = buildAuthorizationSigningBytes;
|
|
7
|
+
exports.verifyAuthorization = verifyAuthorization;
|
|
8
|
+
const fast_json_stable_stringify_1 = __importDefault(require("fast-json-stable-stringify"));
|
|
9
|
+
const crypto_1 = __importDefault(require("crypto"));
|
|
10
|
+
const verify_core_js_1 = require("../verify-core.js");
|
|
11
|
+
// In-memory replay cache: each authorization is honored once per process.
|
|
12
|
+
const usedNonces = new verify_core_js_1.ReplayCache();
|
|
13
|
+
function buildAuthorizationSigningBytes(token) {
|
|
14
|
+
return (0, fast_json_stable_stringify_1.default)({
|
|
15
|
+
request_id: token.request_id,
|
|
16
|
+
decision: token.decision,
|
|
17
|
+
payload_hash: token.payload_hash,
|
|
18
|
+
signers: token.signers,
|
|
19
|
+
roles: token.roles,
|
|
20
|
+
signed_at: token.signed_at,
|
|
21
|
+
expires_at: token.expires_at,
|
|
22
|
+
key_id: token.key_id,
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
async function verifyAuthorization(payload, token, publicKeyPem, policy, expectedKeyId = 'pp-prod-1') {
|
|
26
|
+
// 0. Schema Validation (Fail fast)
|
|
27
|
+
if (!payload || typeof payload !== 'object') {
|
|
28
|
+
throw new Error('Missing or invalid payload block');
|
|
29
|
+
}
|
|
30
|
+
if (!payload.request_id || !payload.action || !payload.args || !payload.issued_at || !payload.expires_at || !payload.nonce) {
|
|
31
|
+
throw new Error('Malformed payload schema: missing required fields');
|
|
32
|
+
}
|
|
33
|
+
if (!token || typeof token !== 'object') {
|
|
34
|
+
throw new Error('Missing or invalid token block');
|
|
35
|
+
}
|
|
36
|
+
if (!token.request_id || !token.decision || !token.payload_hash || !token.signers || !token.roles || !token.signed_at || !token.expires_at || !token.key_id || !token.signature) {
|
|
37
|
+
throw new Error('Malformed token schema: missing required fields');
|
|
38
|
+
}
|
|
39
|
+
if (token.request_id !== payload.request_id) {
|
|
40
|
+
throw new Error('Request ID mismatch - token does not authorize this payload');
|
|
41
|
+
}
|
|
42
|
+
// Trust boundary: Check key identity
|
|
43
|
+
if (token.key_id !== expectedKeyId) {
|
|
44
|
+
throw new Error(`Untrusted key_id: expected ${expectedKeyId}, got ${token.key_id}`);
|
|
45
|
+
}
|
|
46
|
+
// 1. Check Expiry
|
|
47
|
+
const now = Math.floor(Date.now() / 1000);
|
|
48
|
+
if (token.expires_at < now) {
|
|
49
|
+
throw new Error('Authorization token expired');
|
|
50
|
+
}
|
|
51
|
+
if (payload.expires_at < now) {
|
|
52
|
+
throw new Error('Payload expired');
|
|
53
|
+
}
|
|
54
|
+
// 2. Enforce Decision
|
|
55
|
+
if (token.decision !== 'approved') {
|
|
56
|
+
throw new Error(`Invalid decision: ${token.decision}`);
|
|
57
|
+
}
|
|
58
|
+
// 3. Stable Stringify & Hash
|
|
59
|
+
const canonicalString = (0, fast_json_stable_stringify_1.default)(payload);
|
|
60
|
+
const recomputedHash = crypto_1.default.createHash('sha256').update(canonicalString).digest('hex');
|
|
61
|
+
// 4. Compare Hashes (Exact Binding)
|
|
62
|
+
if (recomputedHash !== token.payload_hash.replace('sha256:', '')) {
|
|
63
|
+
throw new Error('Payload hash mismatch - args were modified after approval');
|
|
64
|
+
}
|
|
65
|
+
// 5. Verify Signature (shared core: pinned key for the expected key id only)
|
|
66
|
+
const sigError = (0, verify_core_js_1.verifyDetached)(buildAuthorizationSigningBytes(token), token.signature, token.key_id, { [expectedKeyId]: publicKeyPem });
|
|
67
|
+
if (sigError) {
|
|
68
|
+
throw new Error(`Cryptographic signature verification failed: ${sigError}`);
|
|
69
|
+
}
|
|
70
|
+
// 6. Policy Enforcement (The Authority Plane)
|
|
71
|
+
// A. Multi-Sig & Role-Based Auth
|
|
72
|
+
const requiredApprovals = policy.required_approvals || 1;
|
|
73
|
+
let validSignatures = 0;
|
|
74
|
+
// Each distinct signer counts once: a repeated name is not a second approval.
|
|
75
|
+
for (const signer of new Set(token.signers)) {
|
|
76
|
+
const isAllowedSigner = policy.allowed_signers ? policy.allowed_signers.includes(signer) : true;
|
|
77
|
+
const hasAllowedRole = policy.allowed_roles ? token.roles.some(r => policy.allowed_roles.includes(r)) : true;
|
|
78
|
+
if (isAllowedSigner && hasAllowedRole) {
|
|
79
|
+
validSignatures++;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
if (validSignatures < requiredApprovals) {
|
|
83
|
+
throw new Error(`Policy violation: Requires ${requiredApprovals} valid approvals, found ${validSignatures}. Evaluated signers: ${token.signers.join(', ')}`);
|
|
84
|
+
}
|
|
85
|
+
// B. Basic Conditions
|
|
86
|
+
if (policy.conditions) {
|
|
87
|
+
for (const cond of policy.conditions) {
|
|
88
|
+
const keys = cond.field.split('.');
|
|
89
|
+
let val = payload;
|
|
90
|
+
for (const k of keys) {
|
|
91
|
+
val = val?.[k];
|
|
92
|
+
}
|
|
93
|
+
if (val !== cond.equals) {
|
|
94
|
+
throw new Error(`Policy violation: Condition '${cond.field} == ${cond.equals}' failed (got '${val}')`);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
if (policy.max_ttl_seconds) {
|
|
99
|
+
const ttl = token.expires_at - token.signed_at;
|
|
100
|
+
if (ttl > policy.max_ttl_seconds) {
|
|
101
|
+
throw new Error(`Policy violation: Token TTL (${ttl}s) exceeds policy max_ttl_seconds (${policy.max_ttl_seconds}s)`);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
// 7. Replay Protection (Single-use)
|
|
105
|
+
if (!usedNonces.use(`${payload.request_id}:${payload.nonce}`)) {
|
|
106
|
+
throw new Error('Replay detected - authorization has already been used');
|
|
107
|
+
}
|
|
108
|
+
return true;
|
|
109
|
+
}
|
|
110
|
+
//# sourceMappingURL=verifier.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"verifier.js","sourceRoot":"","sources":["../../../src/enforcement/verifier.ts"],"names":[],"mappings":"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"}
|
package/dist/src/engine.d.ts
CHANGED
|
@@ -1,7 +1,16 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type Config, type Reversibility } from './config.js';
|
|
2
|
+
export type Lane = 'decide' | 'unblock' | 'verify';
|
|
2
3
|
export interface Decision {
|
|
3
4
|
decision: 'allowed' | 'blocked' | 'held_for_approval';
|
|
4
5
|
rule_id: string | null;
|
|
5
6
|
reason: string;
|
|
7
|
+
lane?: Lane;
|
|
8
|
+
reversibility?: Reversibility;
|
|
9
|
+
countdown_seconds?: number;
|
|
10
|
+
confidence?: number;
|
|
6
11
|
}
|
|
7
|
-
|
|
12
|
+
/**
|
|
13
|
+
* Decide a gated request. `toolName` is the tool for `tools/call` and null for other
|
|
14
|
+
* methods; rules match on method plus (for tools/call) the exact tool name.
|
|
15
|
+
*/
|
|
16
|
+
export declare function evaluate(toolName: string | null, toolArgs: Record<string, unknown> | undefined, config: Config, method?: string): Decision;
|
package/dist/src/engine.js
CHANGED
|
@@ -1,25 +1,31 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.evaluate = evaluate;
|
|
4
|
+
const config_js_1 = require("./config.js");
|
|
5
|
+
const classify_js_1 = require("./classify.js");
|
|
4
6
|
const ACTION_TO_DECISION = {
|
|
5
7
|
allow: 'allowed',
|
|
6
8
|
block: 'blocked',
|
|
7
9
|
require_approval: 'held_for_approval',
|
|
8
10
|
};
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
11
|
+
/**
|
|
12
|
+
* Decide a gated request. `toolName` is the tool for `tools/call` and null for other
|
|
13
|
+
* methods; rules match on method plus (for tools/call) the exact tool name.
|
|
14
|
+
*/
|
|
15
|
+
function evaluate(toolName, toolArgs, config, method = config_js_1.TOOLS_CALL) {
|
|
16
|
+
const matched = config.rules.find((rule) => (rule.method ?? config_js_1.TOOLS_CALL) === method && (method !== config_js_1.TOOLS_CALL || rule.tool === toolName)) ?? null;
|
|
17
|
+
const base = matched
|
|
18
|
+
? {
|
|
19
|
+
decision: ACTION_TO_DECISION[matched.action],
|
|
20
|
+
rule_id: matched.id,
|
|
21
|
+
reason: `Matched rule "${matched.id}"`,
|
|
17
22
|
}
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
23
|
+
: {
|
|
24
|
+
decision: ACTION_TO_DECISION[config.default_action],
|
|
25
|
+
rule_id: null,
|
|
26
|
+
reason: `No matching rule; default action "${config.default_action}"`,
|
|
27
|
+
};
|
|
28
|
+
// Enrich with reversibility/confidence lane routing (Permission Deck).
|
|
29
|
+
return (0, classify_js_1.classify)(base, matched, (0, classify_js_1.extractMeta)(toolArgs), toolArgs);
|
|
24
30
|
}
|
|
25
31
|
//# sourceMappingURL=engine.js.map
|
package/dist/src/engine.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"engine.js","sourceRoot":"","sources":["../../src/engine.ts"],"names":[],"mappings":";;
|
|
1
|
+
{"version":3,"file":"engine.js","sourceRoot":"","sources":["../../src/engine.ts"],"names":[],"mappings":";;AA2BA,4BAuBC;AAlDD,2CAAqF;AACrF,+CAAsD;AAgBtD,MAAM,kBAAkB,GAAG;IACzB,KAAK,EAAE,SAAS;IAChB,KAAK,EAAE,SAAS;IAChB,gBAAgB,EAAE,mBAAmB;CAC7B,CAAC;AAEX;;;GAGG;AACH,SAAgB,QAAQ,CACtB,QAAuB,EACvB,QAA6C,EAC7C,MAAc,EACd,SAAiB,sBAAU;IAE3B,MAAM,OAAO,GACX,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,MAAM,IAAI,sBAAU,CAAC,KAAK,MAAM,IAAI,CAAC,MAAM,KAAK,sBAAU,IAAI,IAAI,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,IAAI,IAAI,CAAC;IAEnI,MAAM,IAAI,GAAa,OAAO;QAC5B,CAAC,CAAC;YACE,QAAQ,EAAE,kBAAkB,CAAC,OAAO,CAAC,MAAM,CAAC;YAC5C,OAAO,EAAE,OAAO,CAAC,EAAE;YACnB,MAAM,EAAE,iBAAiB,OAAO,CAAC,EAAE,GAAG;SACvC;QACH,CAAC,CAAC;YACE,QAAQ,EAAE,kBAAkB,CAAC,MAAM,CAAC,cAAc,CAAC;YACnD,OAAO,EAAE,IAAI;YACb,MAAM,EAAE,qCAAqC,MAAM,CAAC,cAAc,GAAG;SACtE,CAAC;IAEN,uEAAuE;IACvE,OAAO,IAAA,sBAAQ,EAAC,IAAI,EAAE,OAAO,EAAE,IAAA,yBAAW,EAAC,QAAQ,CAAC,EAAE,QAAQ,CAAC,CAAC;AAClE,CAAC"}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Inbound JSON-RPC framing for the stdio proxy.
|
|
3
|
+
*
|
|
4
|
+
* The guard only ever forwards a message it parsed and evaluated, re-serialized from
|
|
5
|
+
* the parsed object, so the server cannot read a line differently than the guard did.
|
|
6
|
+
* Anything that is not a single well-formed JSON-RPC 2.0 object is answered with an
|
|
7
|
+
* error and never forwarded. MCP does not use JSON-RPC batching, so arrays are refused.
|
|
8
|
+
*/
|
|
9
|
+
export type JsonRpcId = string | number | null;
|
|
10
|
+
export interface JsonRpcMessage {
|
|
11
|
+
jsonrpc: '2.0';
|
|
12
|
+
id?: JsonRpcId;
|
|
13
|
+
method?: string;
|
|
14
|
+
params?: unknown;
|
|
15
|
+
result?: unknown;
|
|
16
|
+
error?: unknown;
|
|
17
|
+
}
|
|
18
|
+
export type Frame = {
|
|
19
|
+
kind: 'request';
|
|
20
|
+
msg: JsonRpcMessage & {
|
|
21
|
+
method: string;
|
|
22
|
+
id: JsonRpcId;
|
|
23
|
+
};
|
|
24
|
+
} | {
|
|
25
|
+
kind: 'notification';
|
|
26
|
+
msg: JsonRpcMessage & {
|
|
27
|
+
method: string;
|
|
28
|
+
};
|
|
29
|
+
} | {
|
|
30
|
+
kind: 'response';
|
|
31
|
+
msg: JsonRpcMessage;
|
|
32
|
+
} | {
|
|
33
|
+
kind: 'invalid';
|
|
34
|
+
id: JsonRpcId;
|
|
35
|
+
code: number;
|
|
36
|
+
message: string;
|
|
37
|
+
};
|
|
38
|
+
export declare const PARSE_ERROR = -32700;
|
|
39
|
+
export declare const INVALID_REQUEST = -32600;
|
|
40
|
+
export declare const METHOD_NOT_FOUND = -32601;
|
|
41
|
+
export declare const INVALID_PARAMS = -32602;
|
|
42
|
+
export declare const BLOCKED = -32001;
|
|
43
|
+
export declare const HELD = -32002;
|
|
44
|
+
export declare const DENIED = -32003;
|
|
45
|
+
export declare const EXPIRED = -32004;
|
|
46
|
+
export declare const RELEASE_FAILED = -32005;
|
|
47
|
+
/**
|
|
48
|
+
* Methods that carry no action or data access of their own. Everything else — tools/call,
|
|
49
|
+
* resources/read, prompts/get, resources/subscribe and any method the guard does not
|
|
50
|
+
* know — goes through the policy.
|
|
51
|
+
*/
|
|
52
|
+
export declare const PASSTHROUGH_METHODS: Set<string>;
|
|
53
|
+
export declare function isPassthrough(method: string): boolean;
|
|
54
|
+
export declare function parseFrame(line: string): Frame;
|
|
55
|
+
export declare function errorLine(id: JsonRpcId, code: number, message: string): string;
|
|
56
|
+
/**
|
|
57
|
+
* The tools/call target and arguments, validated. Returns an error message when the
|
|
58
|
+
* params are malformed (the call is then refused, never forwarded).
|
|
59
|
+
*/
|
|
60
|
+
export declare function toolCallTarget(params: unknown): {
|
|
61
|
+
name: string;
|
|
62
|
+
args: Record<string, unknown> | undefined;
|
|
63
|
+
} | {
|
|
64
|
+
error: string;
|
|
65
|
+
};
|
|
66
|
+
/** A copy of the message with Permission Deck agent metadata (`_pp`) removed from tool arguments. */
|
|
67
|
+
export declare function stripAgentMetadata(msg: JsonRpcMessage): JsonRpcMessage;
|