mcp-recon 0.2.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/LICENSE +19 -0
- package/README.md +271 -0
- package/dist/bin/recon.d.ts +18 -0
- package/dist/bin/recon.d.ts.map +1 -0
- package/dist/bin/recon.js +361 -0
- package/dist/bin/recon.js.map +1 -0
- package/dist/caveats/index.d.ts +46 -0
- package/dist/caveats/index.d.ts.map +1 -0
- package/dist/caveats/index.js +186 -0
- package/dist/caveats/index.js.map +1 -0
- package/dist/caveats/render.d.ts +25 -0
- package/dist/caveats/render.d.ts.map +1 -0
- package/dist/caveats/render.js +100 -0
- package/dist/caveats/render.js.map +1 -0
- package/dist/caveats/types.d.ts +94 -0
- package/dist/caveats/types.d.ts.map +1 -0
- package/dist/caveats/types.js +17 -0
- package/dist/caveats/types.js.map +1 -0
- package/dist/classify/caveat.d.ts +29 -0
- package/dist/classify/caveat.d.ts.map +1 -0
- package/dist/classify/caveat.js +103 -0
- package/dist/classify/caveat.js.map +1 -0
- package/dist/classify/index.d.ts +21 -0
- package/dist/classify/index.d.ts.map +1 -0
- package/dist/classify/index.js +186 -0
- package/dist/classify/index.js.map +1 -0
- package/dist/classify/rules.d.ts +62 -0
- package/dist/classify/rules.d.ts.map +1 -0
- package/dist/classify/rules.js +219 -0
- package/dist/classify/rules.js.map +1 -0
- package/dist/classify/types.d.ts +45 -0
- package/dist/classify/types.d.ts.map +1 -0
- package/dist/classify/types.js +9 -0
- package/dist/classify/types.js.map +1 -0
- package/dist/enumerate.d.ts +79 -0
- package/dist/enumerate.d.ts.map +1 -0
- package/dist/enumerate.js +62 -0
- package/dist/enumerate.js.map +1 -0
- package/dist/fuzz/axes/boundary.d.ts +17 -0
- package/dist/fuzz/axes/boundary.d.ts.map +1 -0
- package/dist/fuzz/axes/boundary.js +143 -0
- package/dist/fuzz/axes/boundary.js.map +1 -0
- package/dist/fuzz/axes/encoding.d.ts +17 -0
- package/dist/fuzz/axes/encoding.d.ts.map +1 -0
- package/dist/fuzz/axes/encoding.js +59 -0
- package/dist/fuzz/axes/encoding.js.map +1 -0
- package/dist/fuzz/axes/path-traversal.d.ts +17 -0
- package/dist/fuzz/axes/path-traversal.d.ts.map +1 -0
- package/dist/fuzz/axes/path-traversal.js +56 -0
- package/dist/fuzz/axes/path-traversal.js.map +1 -0
- package/dist/fuzz/axes/schema-violation.d.ts +18 -0
- package/dist/fuzz/axes/schema-violation.d.ts.map +1 -0
- package/dist/fuzz/axes/schema-violation.js +74 -0
- package/dist/fuzz/axes/schema-violation.js.map +1 -0
- package/dist/fuzz/axes/type-confusion.d.ts +17 -0
- package/dist/fuzz/axes/type-confusion.d.ts.map +1 -0
- package/dist/fuzz/axes/type-confusion.js +67 -0
- package/dist/fuzz/axes/type-confusion.js.map +1 -0
- package/dist/fuzz/axes/url-hostility.d.ts +17 -0
- package/dist/fuzz/axes/url-hostility.d.ts.map +1 -0
- package/dist/fuzz/axes/url-hostility.js +61 -0
- package/dist/fuzz/axes/url-hostility.js.map +1 -0
- package/dist/fuzz/index.d.ts +41 -0
- package/dist/fuzz/index.d.ts.map +1 -0
- package/dist/fuzz/index.js +147 -0
- package/dist/fuzz/index.js.map +1 -0
- package/dist/fuzz/prng.d.ts +26 -0
- package/dist/fuzz/prng.d.ts.map +1 -0
- package/dist/fuzz/prng.js +52 -0
- package/dist/fuzz/prng.js.map +1 -0
- package/dist/fuzz/schema.d.ts +46 -0
- package/dist/fuzz/schema.d.ts.map +1 -0
- package/dist/fuzz/schema.js +84 -0
- package/dist/fuzz/schema.js.map +1 -0
- package/dist/fuzz/types.d.ts +53 -0
- package/dist/fuzz/types.d.ts.map +1 -0
- package/dist/fuzz/types.js +11 -0
- package/dist/fuzz/types.js.map +1 -0
- package/dist/index.d.ts +25 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +25 -0
- package/dist/index.js.map +1 -0
- package/dist/report/index.d.ts +25 -0
- package/dist/report/index.d.ts.map +1 -0
- package/dist/report/index.js +133 -0
- package/dist/report/index.js.map +1 -0
- package/dist/scan/index.d.ts +52 -0
- package/dist/scan/index.d.ts.map +1 -0
- package/dist/scan/index.js +81 -0
- package/dist/scan/index.js.map +1 -0
- package/dist/transport.d.ts +43 -0
- package/dist/transport.d.ts.map +1 -0
- package/dist/transport.js +74 -0
- package/dist/transport.js.map +1 -0
- package/package.json +72 -0
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Caveat synthesizer — the bridge from mcp-recon to capnagent.
|
|
3
|
+
*
|
|
4
|
+
* For each classified tool, generate a copy-pasteable capnagent
|
|
5
|
+
* caveat string that bounds the tool's authority to the smallest
|
|
6
|
+
* surface that preserves utility. The caveat is always a *suggestion*;
|
|
7
|
+
* the operator should review and tighten further to fit their
|
|
8
|
+
* deployment.
|
|
9
|
+
*
|
|
10
|
+
* Convention: every suggestion is a single DSL predicate that can be
|
|
11
|
+
* passed to `Issuer.issue(...).caveat(...)` or `cap.attenuate(...)`.
|
|
12
|
+
* The caveat language is documented in capnagent's
|
|
13
|
+
* `caveat_dsl.rs`.
|
|
14
|
+
*/
|
|
15
|
+
/**
|
|
16
|
+
* Suggest a capnagent caveat for the given classification + facts.
|
|
17
|
+
* The suggestion places `<placeholder>` markers where the operator
|
|
18
|
+
* must substitute deployment-specific values.
|
|
19
|
+
*/
|
|
20
|
+
export function synthesizeCaveat(input) {
|
|
21
|
+
const { tool, data_class, authority_level, facts } = input;
|
|
22
|
+
// Privileged tools: deny outright. Leaving exec/shell exposed is
|
|
23
|
+
// almost never the right answer; if it is, the operator can
|
|
24
|
+
// hand-write a tighter argv-allowlist caveat.
|
|
25
|
+
if (authority_level === "privileged") {
|
|
26
|
+
return `tool != "${tool}" // PRIVILEGED — recommend deny outright; operator should hand-write argv allowlist if exposing`;
|
|
27
|
+
}
|
|
28
|
+
// Destructive tools: caller-bind + bounded args + expiry.
|
|
29
|
+
if (authority_level === "destructive") {
|
|
30
|
+
const argClauses = boundedArgClauses(tool, facts, "destructive");
|
|
31
|
+
return [
|
|
32
|
+
`tool == "${tool}"`,
|
|
33
|
+
`caller == "<your-caller-id>"`,
|
|
34
|
+
...argClauses,
|
|
35
|
+
`now <= @<your-cap-expiry>`,
|
|
36
|
+
].join(" AND ");
|
|
37
|
+
}
|
|
38
|
+
// Per-class write/read defaults.
|
|
39
|
+
const argClauses = boundedArgClauses(tool, facts, authority_level);
|
|
40
|
+
const baseClauses = [
|
|
41
|
+
`tool == "${tool}"`,
|
|
42
|
+
`caller == "<your-caller-id>"`,
|
|
43
|
+
...argClauses,
|
|
44
|
+
`now <= @<your-cap-expiry>`,
|
|
45
|
+
];
|
|
46
|
+
// Add a class-flavoured comment so the operator knows what
|
|
47
|
+
// surface they're bounding.
|
|
48
|
+
const flavor = flavorComment(data_class, authority_level);
|
|
49
|
+
return `${baseClauses.join(" AND ")} // ${flavor}`;
|
|
50
|
+
}
|
|
51
|
+
function boundedArgClauses(_tool, facts, authority) {
|
|
52
|
+
const clauses = [];
|
|
53
|
+
for (const arg of facts.args) {
|
|
54
|
+
const argName = arg.path[0];
|
|
55
|
+
if (argName === undefined)
|
|
56
|
+
continue;
|
|
57
|
+
if (arg.isPathShaped && arg.declaredType === "string") {
|
|
58
|
+
// For path-shaped args, the recommended caveat is the same
|
|
59
|
+
// shape capnagent's mcp-fs-agent uses post-v0.5.
|
|
60
|
+
clauses.push(`arg.${argName} starts_with "<your-sandbox-prefix>/"`);
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
63
|
+
if (arg.isUrlShaped && arg.declaredType === "string") {
|
|
64
|
+
clauses.push(`arg.${argName} == "https://<your-allowed-origin>"`);
|
|
65
|
+
continue;
|
|
66
|
+
}
|
|
67
|
+
if (arg.isCommandShaped && arg.declaredType === "string") {
|
|
68
|
+
clauses.push(`arg.${argName} starts_with "<your-allowed-command-prefix>"`);
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
if (arg.enumValues !== undefined) {
|
|
72
|
+
// Enums constrain themselves; no extra caveat needed in v0.1.
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
if (authority === "destructive" &&
|
|
76
|
+
(arg.declaredType === "string" || arg.declaredType === "unknown")) {
|
|
77
|
+
// Destructive + free-form string arg = double-bound.
|
|
78
|
+
clauses.push(`arg.${argName} starts_with "<your-bounded-prefix>/"`);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
return clauses;
|
|
82
|
+
}
|
|
83
|
+
function flavorComment(dc, auth) {
|
|
84
|
+
switch (dc) {
|
|
85
|
+
case "filesystem":
|
|
86
|
+
return `${auth.toUpperCase()} filesystem; bound the sandbox prefix tightly (capnagent round 07/10)`;
|
|
87
|
+
case "network":
|
|
88
|
+
return `${auth.toUpperCase()} network; allowlist exact origins (capnagent round 05/09)`;
|
|
89
|
+
case "shell":
|
|
90
|
+
return `${auth.toUpperCase()} shell; argv allowlist mandatory (capnagent shell-agent example)`;
|
|
91
|
+
case "payments":
|
|
92
|
+
return `${auth.toUpperCase()} payments; bound amount + merchant + caller`;
|
|
93
|
+
case "messaging":
|
|
94
|
+
return `${auth.toUpperCase()} messaging; bound recipient list + caller`;
|
|
95
|
+
case "system":
|
|
96
|
+
return `${auth.toUpperCase()} system; usually safe but verify env-var exposure`;
|
|
97
|
+
case "metadata":
|
|
98
|
+
return `${auth.toUpperCase()} metadata`;
|
|
99
|
+
case "unknown":
|
|
100
|
+
return `${auth.toUpperCase()} unclassified — operator must review`;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
//# sourceMappingURL=caveat.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"caveat.js","sourceRoot":"","sources":["../../src/classify/caveat.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAYH;;;;GAIG;AACH,MAAM,UAAU,gBAAgB,CAAC,KAAkB;IACjD,MAAM,EAAE,IAAI,EAAE,UAAU,EAAE,eAAe,EAAE,KAAK,EAAE,GAAG,KAAK,CAAC;IAE3D,iEAAiE;IACjE,4DAA4D;IAC5D,8CAA8C;IAC9C,IAAI,eAAe,KAAK,YAAY,EAAE,CAAC;QACrC,OAAO,YAAY,IAAI,mGAAmG,CAAC;IAC7H,CAAC;IAED,0DAA0D;IAC1D,IAAI,eAAe,KAAK,aAAa,EAAE,CAAC;QACtC,MAAM,UAAU,GAAG,iBAAiB,CAAC,IAAI,EAAE,KAAK,EAAE,aAAa,CAAC,CAAC;QACjE,OAAO;YACL,YAAY,IAAI,GAAG;YACnB,8BAA8B;YAC9B,GAAG,UAAU;YACb,2BAA2B;SAC5B,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAClB,CAAC;IAED,iCAAiC;IACjC,MAAM,UAAU,GAAG,iBAAiB,CAAC,IAAI,EAAE,KAAK,EAAE,eAAe,CAAC,CAAC;IACnE,MAAM,WAAW,GAAG;QAClB,YAAY,IAAI,GAAG;QACnB,8BAA8B;QAC9B,GAAG,UAAU;QACb,2BAA2B;KAC5B,CAAC;IAEF,2DAA2D;IAC3D,4BAA4B;IAC5B,MAAM,MAAM,GAAG,aAAa,CAAC,UAAU,EAAE,eAAe,CAAC,CAAC;IAC1D,OAAO,GAAG,WAAW,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,MAAM,EAAE,CAAC;AACtD,CAAC;AAED,SAAS,iBAAiB,CACxB,KAAa,EACb,KAAgB,EAChB,SAAyB;IAEzB,MAAM,OAAO,GAAa,EAAE,CAAC;IAE7B,KAAK,MAAM,GAAG,IAAI,KAAK,CAAC,IAAI,EAAE,CAAC;QAC7B,MAAM,OAAO,GAAG,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAC5B,IAAI,OAAO,KAAK,SAAS;YAAE,SAAS;QAEpC,IAAI,GAAG,CAAC,YAAY,IAAI,GAAG,CAAC,YAAY,KAAK,QAAQ,EAAE,CAAC;YACtD,2DAA2D;YAC3D,iDAAiD;YACjD,OAAO,CAAC,IAAI,CAAC,OAAO,OAAO,uCAAuC,CAAC,CAAC;YACpE,SAAS;QACX,CAAC;QACD,IAAI,GAAG,CAAC,WAAW,IAAI,GAAG,CAAC,YAAY,KAAK,QAAQ,EAAE,CAAC;YACrD,OAAO,CAAC,IAAI,CAAC,OAAO,OAAO,qCAAqC,CAAC,CAAC;YAClE,SAAS;QACX,CAAC;QACD,IAAI,GAAG,CAAC,eAAe,IAAI,GAAG,CAAC,YAAY,KAAK,QAAQ,EAAE,CAAC;YACzD,OAAO,CAAC,IAAI,CAAC,OAAO,OAAO,8CAA8C,CAAC,CAAC;YAC3E,SAAS;QACX,CAAC;QACD,IAAI,GAAG,CAAC,UAAU,KAAK,SAAS,EAAE,CAAC;YACjC,8DAA8D;YAC9D,SAAS;QACX,CAAC;QACD,IACE,SAAS,KAAK,aAAa;YAC3B,CAAC,GAAG,CAAC,YAAY,KAAK,QAAQ,IAAI,GAAG,CAAC,YAAY,KAAK,SAAS,CAAC,EACjE,CAAC;YACD,qDAAqD;YACrD,OAAO,CAAC,IAAI,CAAC,OAAO,OAAO,uCAAuC,CAAC,CAAC;QACtE,CAAC;IACH,CAAC;IAED,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,SAAS,aAAa,CAAC,EAAa,EAAE,IAAoB;IACxD,QAAQ,EAAE,EAAE,CAAC;QACX,KAAK,YAAY;YACf,OAAO,GAAG,IAAI,CAAC,WAAW,EAAE,uEAAuE,CAAC;QACtG,KAAK,SAAS;YACZ,OAAO,GAAG,IAAI,CAAC,WAAW,EAAE,2DAA2D,CAAC;QAC1F,KAAK,OAAO;YACV,OAAO,GAAG,IAAI,CAAC,WAAW,EAAE,kEAAkE,CAAC;QACjG,KAAK,UAAU;YACb,OAAO,GAAG,IAAI,CAAC,WAAW,EAAE,6CAA6C,CAAC;QAC5E,KAAK,WAAW;YACd,OAAO,GAAG,IAAI,CAAC,WAAW,EAAE,2CAA2C,CAAC;QAC1E,KAAK,QAAQ;YACX,OAAO,GAAG,IAAI,CAAC,WAAW,EAAE,mDAAmD,CAAC;QAClF,KAAK,UAAU;YACb,OAAO,GAAG,IAAI,CAAC,WAAW,EAAE,WAAW,CAAC;QAC1C,KAAK,SAAS;YACZ,OAAO,GAAG,IAAI,CAAC,WAAW,EAAE,sCAAsC,CAAC;IACvE,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `classify` — turn an inventory (+ optional fuzz results) into a
|
|
3
|
+
* v0.1 classification document.
|
|
4
|
+
*
|
|
5
|
+
* Methodology in docs/METHODOLOGY.md. The rules live in `./rules.ts`.
|
|
6
|
+
* Confidence combines via noisy-OR; authority is the lattice-join of
|
|
7
|
+
* rule floors plus side-effect-verb escalations.
|
|
8
|
+
*/
|
|
9
|
+
import type { ToolInventory } from "../enumerate.js";
|
|
10
|
+
import type { FuzzResults } from "../fuzz/types.js";
|
|
11
|
+
import { type ToolFacts } from "../fuzz/schema.js";
|
|
12
|
+
import { type ClassificationResults } from "./types.js";
|
|
13
|
+
export { CLASSIFICATION_SCHEMA } from "./types.js";
|
|
14
|
+
export type { AuthorityLevel, Classification, ClassificationResults, DataClass, } from "./types.js";
|
|
15
|
+
/** Classify every tool in `inventory`. Optionally fold in `fuzz` for confidence. */
|
|
16
|
+
export declare function classify(inventory: ToolInventory, fuzz?: FuzzResults): ClassificationResults;
|
|
17
|
+
/** Noisy-OR combiner: 1 - ∏(1 - c_i). */
|
|
18
|
+
export declare function noisyOr(confidences: readonly number[]): number;
|
|
19
|
+
export { synthesizeCaveat } from "./caveat.js";
|
|
20
|
+
export type { ToolFacts };
|
|
21
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/classify/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAkB,aAAa,EAAE,MAAM,iBAAiB,CAAC;AACrE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,EAAoB,KAAK,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAerE,OAAO,EAIL,KAAK,qBAAqB,EAE3B,MAAM,YAAY,CAAC;AAEpB,OAAO,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC;AACnD,YAAY,EACV,cAAc,EACd,cAAc,EACd,qBAAqB,EACrB,SAAS,GACV,MAAM,YAAY,CAAC;AAEpB,oFAAoF;AACpF,wBAAgB,QAAQ,CACtB,SAAS,EAAE,aAAa,EACxB,IAAI,CAAC,EAAE,WAAW,GACjB,qBAAqB,CAsBvB;AA2KD,yCAAyC;AACzC,wBAAgB,OAAO,CAAC,WAAW,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,CAO9D;AAcD,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAC/C,YAAY,EAAE,SAAS,EAAE,CAAC"}
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `classify` — turn an inventory (+ optional fuzz results) into a
|
|
3
|
+
* v0.1 classification document.
|
|
4
|
+
*
|
|
5
|
+
* Methodology in docs/METHODOLOGY.md. The rules live in `./rules.ts`.
|
|
6
|
+
* Confidence combines via noisy-OR; authority is the lattice-join of
|
|
7
|
+
* rule floors plus side-effect-verb escalations.
|
|
8
|
+
*/
|
|
9
|
+
import { extractToolFacts } from "../fuzz/schema.js";
|
|
10
|
+
import { synthesizeCaveat } from "./caveat.js";
|
|
11
|
+
import { DESCRIPTION_MATCH_WEIGHT, DESTRUCTIVE_VERBS, FUZZ_INFORMED_BONUS, NAME_MATCH_WEIGHT, PRIVILEGED_VERBS, RULES, SCHEMA_MATCH_WEIGHT, SIDE_EFFECT_VERBS, escalateAuthority, maxAuthority, } from "./rules.js";
|
|
12
|
+
import { CLASSIFICATION_SCHEMA, } from "./types.js";
|
|
13
|
+
export { CLASSIFICATION_SCHEMA } from "./types.js";
|
|
14
|
+
/** Classify every tool in `inventory`. Optionally fold in `fuzz` for confidence. */
|
|
15
|
+
export function classify(inventory, fuzz) {
|
|
16
|
+
const fuzzByTool = new Map();
|
|
17
|
+
if (fuzz) {
|
|
18
|
+
for (const s of fuzz.summary) {
|
|
19
|
+
fuzzByTool.set(s.tool, {
|
|
20
|
+
ok: s.ok,
|
|
21
|
+
total: s.ok + s.protocol_error + s.runtime_error,
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
const classifications = inventory.tools.map((tool) => classifyTool(tool, fuzzByTool.get(tool.name)));
|
|
26
|
+
return {
|
|
27
|
+
schema: CLASSIFICATION_SCHEMA,
|
|
28
|
+
scanned_at: new Date().toISOString(),
|
|
29
|
+
server: inventory.server,
|
|
30
|
+
fuzz_informed: fuzz !== undefined,
|
|
31
|
+
classifications,
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
function classifyTool(tool, fuzzStats) {
|
|
35
|
+
const facts = extractToolFacts(tool.name, tool.inputSchema);
|
|
36
|
+
const description = tool.description ?? "";
|
|
37
|
+
const name = tool.name;
|
|
38
|
+
const fired = [];
|
|
39
|
+
for (const rule of RULES) {
|
|
40
|
+
if (rule.scope !== "description" && rule.pattern.test(name)) {
|
|
41
|
+
fired.push({ rule, weight: NAME_MATCH_WEIGHT, where: "name" });
|
|
42
|
+
}
|
|
43
|
+
if (rule.scope !== "name" && rule.pattern.test(description)) {
|
|
44
|
+
fired.push({ rule, weight: DESCRIPTION_MATCH_WEIGHT, where: "description" });
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
// Schema-shape signal — adds to filesystem / network if arg names
|
|
48
|
+
// are path-shaped or URL-shaped.
|
|
49
|
+
const schemaFired = [];
|
|
50
|
+
for (const arg of facts.args) {
|
|
51
|
+
if (arg.isPathShaped) {
|
|
52
|
+
schemaFired.push({
|
|
53
|
+
data_class: "filesystem",
|
|
54
|
+
weight: SCHEMA_MATCH_WEIGHT,
|
|
55
|
+
reason: `arg "${arg.path[0]}" is path-shaped`,
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
if (arg.isUrlShaped) {
|
|
59
|
+
schemaFired.push({
|
|
60
|
+
data_class: "network",
|
|
61
|
+
weight: SCHEMA_MATCH_WEIGHT,
|
|
62
|
+
reason: `arg "${arg.path[0]}" is URL-shaped`,
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
if (arg.isCommandShaped) {
|
|
66
|
+
schemaFired.push({
|
|
67
|
+
data_class: "shell",
|
|
68
|
+
weight: SCHEMA_MATCH_WEIGHT,
|
|
69
|
+
reason: `arg "${arg.path[0]}" is command-shaped`,
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
// Bucket evidence by data_class. Per-bucket noisy-OR over rule
|
|
74
|
+
// confidences gives us the final per-class confidence; the winner
|
|
75
|
+
// is the highest.
|
|
76
|
+
const evidenceByClass = new Map();
|
|
77
|
+
const floorsByClass = new Map();
|
|
78
|
+
const rationaleParts = [];
|
|
79
|
+
for (const f of fired) {
|
|
80
|
+
pushEvidence(evidenceByClass, f.rule.data_class, f.weight);
|
|
81
|
+
floorsByClass.set(f.rule.data_class, maxAuthority(floorsByClass.get(f.rule.data_class) ?? f.rule.authority_floor, f.rule.authority_floor));
|
|
82
|
+
rationaleParts.push(`${f.where} match "${f.rule.pattern.source}" → ${f.rule.data_class}/${f.rule.authority_floor} (${f.weight.toFixed(2)})`);
|
|
83
|
+
}
|
|
84
|
+
for (const s of schemaFired) {
|
|
85
|
+
pushEvidence(evidenceByClass, s.data_class, s.weight);
|
|
86
|
+
rationaleParts.push(`schema: ${s.reason} → ${s.data_class} (${s.weight.toFixed(2)})`);
|
|
87
|
+
}
|
|
88
|
+
// Pick the highest-confidence data-class.
|
|
89
|
+
let winnerClass = "unknown";
|
|
90
|
+
let winnerConfidence = 0;
|
|
91
|
+
for (const [dc, confidences] of evidenceByClass.entries()) {
|
|
92
|
+
const combined = noisyOr(confidences);
|
|
93
|
+
if (combined > winnerConfidence) {
|
|
94
|
+
winnerClass = dc;
|
|
95
|
+
winnerConfidence = combined;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
// Apply fuzz-informed bonus: if the server actually accepted any of
|
|
99
|
+
// our adversarial inputs, raise confidence we know what kind of
|
|
100
|
+
// tool this is (the fuzzer crossed enough surface to confirm a
|
|
101
|
+
// real interaction, even if accepted inputs are not "successes").
|
|
102
|
+
if (fuzzStats && fuzzStats.total > 0 && fuzzStats.ok > 0) {
|
|
103
|
+
winnerConfidence = noisyOr([winnerConfidence, FUZZ_INFORMED_BONUS]);
|
|
104
|
+
rationaleParts.push(`fuzz: ${fuzzStats.ok}/${fuzzStats.total} accepted → +${FUZZ_INFORMED_BONUS}`);
|
|
105
|
+
}
|
|
106
|
+
// Authority: the floor for the winning class, escalated by any
|
|
107
|
+
// side-effect verbs in the description.
|
|
108
|
+
let authority = floorsByClass.get(winnerClass) ?? "read";
|
|
109
|
+
for (const verb of PRIVILEGED_VERBS) {
|
|
110
|
+
if (verb.test(description)) {
|
|
111
|
+
authority = "privileged";
|
|
112
|
+
rationaleParts.push(`description has privileged verb → escalate to privileged`);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
for (const verb of DESTRUCTIVE_VERBS) {
|
|
116
|
+
if (verb.test(description)) {
|
|
117
|
+
authority = maxAuthority(authority, "destructive");
|
|
118
|
+
rationaleParts.push(`description has destructive verb → escalate to destructive`);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
// Generic side-effect verbs only escalate by ONE step, and only if
|
|
122
|
+
// we haven't already saturated.
|
|
123
|
+
for (const verb of SIDE_EFFECT_VERBS) {
|
|
124
|
+
if (verb.test(description) && authority === "read") {
|
|
125
|
+
authority = escalateAuthority(authority);
|
|
126
|
+
rationaleParts.push(`description has side-effect verb → escalate one step`);
|
|
127
|
+
break;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
// Confused-deputy flag: tool takes user-controllable string args
|
|
131
|
+
// AND has write/destructive/privileged authority. (METHODOLOGY.md
|
|
132
|
+
// §"The confused-deputy flag")
|
|
133
|
+
const hasUserStringArg = facts.args.some((a) => (a.declaredType === "string" || a.declaredType === "unknown") &&
|
|
134
|
+
a.enumValues === undefined);
|
|
135
|
+
const writeOrAbove = authority !== "read";
|
|
136
|
+
const confusedDeputy = hasUserStringArg && writeOrAbove;
|
|
137
|
+
if (confusedDeputy) {
|
|
138
|
+
rationaleParts.push("user-controllable string arg + non-read authority → confused-deputy candidate");
|
|
139
|
+
}
|
|
140
|
+
const recommended_caveat = synthesizeCaveat({
|
|
141
|
+
tool: name,
|
|
142
|
+
data_class: winnerClass,
|
|
143
|
+
authority_level: authority,
|
|
144
|
+
facts,
|
|
145
|
+
});
|
|
146
|
+
return {
|
|
147
|
+
tool: name,
|
|
148
|
+
data_class: winnerClass,
|
|
149
|
+
authority_level: authority,
|
|
150
|
+
confused_deputy_candidate: confusedDeputy,
|
|
151
|
+
confidence: round(winnerConfidence, 3),
|
|
152
|
+
rationale: rationaleParts.length === 0 ? "no rules fired" : rationaleParts.join("; "),
|
|
153
|
+
recommended_caveat,
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
function pushEvidence(map, dc, w) {
|
|
157
|
+
const arr = map.get(dc);
|
|
158
|
+
if (arr)
|
|
159
|
+
arr.push(w);
|
|
160
|
+
else
|
|
161
|
+
map.set(dc, [w]);
|
|
162
|
+
}
|
|
163
|
+
/** Noisy-OR combiner: 1 - ∏(1 - c_i). */
|
|
164
|
+
export function noisyOr(confidences) {
|
|
165
|
+
if (confidences.length === 0)
|
|
166
|
+
return 0;
|
|
167
|
+
let product = 1;
|
|
168
|
+
for (const c of confidences) {
|
|
169
|
+
product *= 1 - clamp(c, 0, 1);
|
|
170
|
+
}
|
|
171
|
+
return clamp(1 - product, 0, 1);
|
|
172
|
+
}
|
|
173
|
+
function clamp(x, lo, hi) {
|
|
174
|
+
if (x < lo)
|
|
175
|
+
return lo;
|
|
176
|
+
if (x > hi)
|
|
177
|
+
return hi;
|
|
178
|
+
return x;
|
|
179
|
+
}
|
|
180
|
+
function round(x, decimals) {
|
|
181
|
+
const f = 10 ** decimals;
|
|
182
|
+
return Math.round(x * f) / f;
|
|
183
|
+
}
|
|
184
|
+
// Re-export the synthesizer / facts for downstream tools that want it.
|
|
185
|
+
export { synthesizeCaveat } from "./caveat.js";
|
|
186
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/classify/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rule table — the single source of truth the classifier reads.
|
|
3
|
+
*
|
|
4
|
+
* Every rule is a triple: (pattern, data-class, authority-level)
|
|
5
|
+
* plus a confidence weight. Rules over tool *names* and *descriptions*
|
|
6
|
+
* use regex match; rules over *schemas* use heuristic shape detection
|
|
7
|
+
* already implemented in `../fuzz/schema.ts`.
|
|
8
|
+
*
|
|
9
|
+
* Per docs/METHODOLOGY.md §"Confidence scoring":
|
|
10
|
+
* - Tool name match — 0.7 (strong cue, gameable)
|
|
11
|
+
* - Description match — 0.5 (helpful, often missing)
|
|
12
|
+
* - Schema match — 0.4 (structural, gameable too)
|
|
13
|
+
* - Side-effect verb in desc — 0.6 (raises authority by one step)
|
|
14
|
+
*
|
|
15
|
+
* Adding a rule? Update this file AND docs/METHODOLOGY.md's change-log
|
|
16
|
+
* with date + reason. No silent drift.
|
|
17
|
+
*/
|
|
18
|
+
import type { AuthorityLevel, DataClass } from "./types.js";
|
|
19
|
+
/** Single rule predicting (data_class, authority_level) given a name/description match. */
|
|
20
|
+
export interface NameOrDescRule {
|
|
21
|
+
/** Regex pattern (case-insensitive). */
|
|
22
|
+
pattern: RegExp;
|
|
23
|
+
data_class: DataClass;
|
|
24
|
+
/**
|
|
25
|
+
* Authority floor — the rule asserts at least this authority level.
|
|
26
|
+
* Final authority can be escalated by side-effect verbs.
|
|
27
|
+
*/
|
|
28
|
+
authority_floor: AuthorityLevel;
|
|
29
|
+
/** Where this rule applies. */
|
|
30
|
+
scope: "name" | "description" | "either";
|
|
31
|
+
}
|
|
32
|
+
/** Confidence weights per rule scope. */
|
|
33
|
+
export declare const NAME_MATCH_WEIGHT = 0.7;
|
|
34
|
+
export declare const DESCRIPTION_MATCH_WEIGHT = 0.5;
|
|
35
|
+
export declare const SCHEMA_MATCH_WEIGHT = 0.4;
|
|
36
|
+
/**
|
|
37
|
+
* Bonus added when fuzz results show the tool actually accepts adversarial
|
|
38
|
+
* inputs (i.e. > 1 ok response in the fuzz stream). Caps confidence higher.
|
|
39
|
+
*/
|
|
40
|
+
export declare const FUZZ_INFORMED_BONUS = 0.1;
|
|
41
|
+
/** Side-effect verbs that raise authority by one step when seen in description. */
|
|
42
|
+
export declare const SIDE_EFFECT_VERBS: RegExp[];
|
|
43
|
+
/**
|
|
44
|
+
* Side-effect verbs strong enough to assert *destructive* directly
|
|
45
|
+
* (matches push authority to "destructive", not just "write").
|
|
46
|
+
*/
|
|
47
|
+
export declare const DESTRUCTIVE_VERBS: RegExp[];
|
|
48
|
+
/**
|
|
49
|
+
* Side-effect verbs strong enough to assert *privileged* directly
|
|
50
|
+
* (subprocess spawn / shell execution).
|
|
51
|
+
*/
|
|
52
|
+
export declare const PRIVILEGED_VERBS: RegExp[];
|
|
53
|
+
/**
|
|
54
|
+
* Rule list. Ordered from most specific to most general — the
|
|
55
|
+
* classifier evaluates all rules; this ordering is for human review.
|
|
56
|
+
*/
|
|
57
|
+
export declare const RULES: readonly NameOrDescRule[];
|
|
58
|
+
/** Return the higher of two authority levels (lattice join). */
|
|
59
|
+
export declare function maxAuthority(a: AuthorityLevel, b: AuthorityLevel): AuthorityLevel;
|
|
60
|
+
/** Escalate authority by one step (read → write → destructive → privileged). */
|
|
61
|
+
export declare function escalateAuthority(a: AuthorityLevel): AuthorityLevel;
|
|
62
|
+
//# sourceMappingURL=rules.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rules.d.ts","sourceRoot":"","sources":["../../src/classify/rules.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAE5D,2FAA2F;AAC3F,MAAM,WAAW,cAAc;IAC7B,wCAAwC;IACxC,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,SAAS,CAAC;IACtB;;;OAGG;IACH,eAAe,EAAE,cAAc,CAAC;IAChC,+BAA+B;IAC/B,KAAK,EAAE,MAAM,GAAG,aAAa,GAAG,QAAQ,CAAC;CAC1C;AAED,yCAAyC;AACzC,eAAO,MAAM,iBAAiB,MAAM,CAAC;AACrC,eAAO,MAAM,wBAAwB,MAAM,CAAC;AAC5C,eAAO,MAAM,mBAAmB,MAAM,CAAC;AACvC;;;GAGG;AACH,eAAO,MAAM,mBAAmB,MAAM,CAAC;AAEvC,mFAAmF;AACnF,eAAO,MAAM,iBAAiB,UAM7B,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,iBAAiB,UAE7B,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,gBAAgB,UAA2D,CAAC;AAEzF;;;GAGG;AACH,eAAO,MAAM,KAAK,EAAE,SAAS,cAAc,EA+I1C,CAAC;AAUF,gEAAgE;AAChE,wBAAgB,YAAY,CAAC,CAAC,EAAE,cAAc,EAAE,CAAC,EAAE,cAAc,GAAG,cAAc,CAEjF;AAED,gFAAgF;AAChF,wBAAgB,iBAAiB,CAAC,CAAC,EAAE,cAAc,GAAG,cAAc,CAWnE"}
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rule table — the single source of truth the classifier reads.
|
|
3
|
+
*
|
|
4
|
+
* Every rule is a triple: (pattern, data-class, authority-level)
|
|
5
|
+
* plus a confidence weight. Rules over tool *names* and *descriptions*
|
|
6
|
+
* use regex match; rules over *schemas* use heuristic shape detection
|
|
7
|
+
* already implemented in `../fuzz/schema.ts`.
|
|
8
|
+
*
|
|
9
|
+
* Per docs/METHODOLOGY.md §"Confidence scoring":
|
|
10
|
+
* - Tool name match — 0.7 (strong cue, gameable)
|
|
11
|
+
* - Description match — 0.5 (helpful, often missing)
|
|
12
|
+
* - Schema match — 0.4 (structural, gameable too)
|
|
13
|
+
* - Side-effect verb in desc — 0.6 (raises authority by one step)
|
|
14
|
+
*
|
|
15
|
+
* Adding a rule? Update this file AND docs/METHODOLOGY.md's change-log
|
|
16
|
+
* with date + reason. No silent drift.
|
|
17
|
+
*/
|
|
18
|
+
/** Confidence weights per rule scope. */
|
|
19
|
+
export const NAME_MATCH_WEIGHT = 0.7;
|
|
20
|
+
export const DESCRIPTION_MATCH_WEIGHT = 0.5;
|
|
21
|
+
export const SCHEMA_MATCH_WEIGHT = 0.4;
|
|
22
|
+
/**
|
|
23
|
+
* Bonus added when fuzz results show the tool actually accepts adversarial
|
|
24
|
+
* inputs (i.e. > 1 ok response in the fuzz stream). Caps confidence higher.
|
|
25
|
+
*/
|
|
26
|
+
export const FUZZ_INFORMED_BONUS = 0.1;
|
|
27
|
+
/** Side-effect verbs that raise authority by one step when seen in description. */
|
|
28
|
+
export const SIDE_EFFECT_VERBS = [
|
|
29
|
+
/\b(write|create|insert|append|update|modify|edit|set|put|post|patch)\b/i,
|
|
30
|
+
/\b(delete|remove|drop|unlink|destroy|cancel|revoke)\b/i,
|
|
31
|
+
/\b(send|email|notify|page|alert|publish|broadcast)\b/i,
|
|
32
|
+
/\b(spawn|exec|launch|run|invoke|fork|shell)\b/i,
|
|
33
|
+
/\b(charge|pay|transfer|wire|refund|debit|credit)\b/i,
|
|
34
|
+
];
|
|
35
|
+
/**
|
|
36
|
+
* Side-effect verbs strong enough to assert *destructive* directly
|
|
37
|
+
* (matches push authority to "destructive", not just "write").
|
|
38
|
+
*/
|
|
39
|
+
export const DESTRUCTIVE_VERBS = [
|
|
40
|
+
/\b(delete|remove|drop|unlink|destroy|cancel|revoke|wipe|purge|truncate)\b/i,
|
|
41
|
+
];
|
|
42
|
+
/**
|
|
43
|
+
* Side-effect verbs strong enough to assert *privileged* directly
|
|
44
|
+
* (subprocess spawn / shell execution).
|
|
45
|
+
*/
|
|
46
|
+
export const PRIVILEGED_VERBS = [/\b(spawn|exec|shell|bash|cmd|invoke_program|fork)\b/i];
|
|
47
|
+
/**
|
|
48
|
+
* Rule list. Ordered from most specific to most general — the
|
|
49
|
+
* classifier evaluates all rules; this ordering is for human review.
|
|
50
|
+
*/
|
|
51
|
+
export const RULES = [
|
|
52
|
+
// ─── filesystem ──────────────────────────────────────────────
|
|
53
|
+
{
|
|
54
|
+
pattern: /\b(read[_-]?(file|text[_-]?file|media[_-]?file|multiple[_-]?files))\b/i,
|
|
55
|
+
data_class: "filesystem",
|
|
56
|
+
authority_floor: "read",
|
|
57
|
+
scope: "name",
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
pattern: /\b(list[_-]?(directory|directory[_-]?with[_-]?sizes)|directory[_-]?tree)\b/i,
|
|
61
|
+
data_class: "filesystem",
|
|
62
|
+
authority_floor: "read",
|
|
63
|
+
scope: "name",
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
pattern: /\b(get[_-]?file[_-]?info|search[_-]?files|list[_-]?allowed[_-]?directories)\b/i,
|
|
67
|
+
data_class: "filesystem",
|
|
68
|
+
authority_floor: "read",
|
|
69
|
+
scope: "name",
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
pattern: /\b(write[_-]?file|edit[_-]?file|create[_-]?directory|move[_-]?file)\b/i,
|
|
73
|
+
data_class: "filesystem",
|
|
74
|
+
authority_floor: "write",
|
|
75
|
+
scope: "name",
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
pattern: /\b(delete[_-]?(path|file|directory|dir))\b/i,
|
|
79
|
+
data_class: "filesystem",
|
|
80
|
+
authority_floor: "destructive",
|
|
81
|
+
scope: "name",
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
pattern: /\b(file|directory|folder|filesystem|filepath|dirent)\b/i,
|
|
85
|
+
data_class: "filesystem",
|
|
86
|
+
authority_floor: "read",
|
|
87
|
+
scope: "description",
|
|
88
|
+
},
|
|
89
|
+
// ─── network ─────────────────────────────────────────────────
|
|
90
|
+
{
|
|
91
|
+
pattern: /\b(http[_-]?(get|post|put|delete)|fetch|request)\b/i,
|
|
92
|
+
data_class: "network",
|
|
93
|
+
authority_floor: "read",
|
|
94
|
+
scope: "name",
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
pattern: /\b(http|https|fetch|request|webhook|api[_-]?call|url|origin)\b/i,
|
|
98
|
+
data_class: "network",
|
|
99
|
+
authority_floor: "read",
|
|
100
|
+
scope: "description",
|
|
101
|
+
},
|
|
102
|
+
// ─── shell ──────────────────────────────────────────────────
|
|
103
|
+
{
|
|
104
|
+
pattern: /\b(exec|shell|run[_-]?command|spawn|process)\b/i,
|
|
105
|
+
data_class: "shell",
|
|
106
|
+
authority_floor: "privileged",
|
|
107
|
+
scope: "name",
|
|
108
|
+
},
|
|
109
|
+
{
|
|
110
|
+
pattern: /\b(execute|shell|subprocess|invoke|launch[_-]?process)\b/i,
|
|
111
|
+
data_class: "shell",
|
|
112
|
+
authority_floor: "privileged",
|
|
113
|
+
scope: "description",
|
|
114
|
+
},
|
|
115
|
+
// ─── payments ───────────────────────────────────────────────
|
|
116
|
+
{
|
|
117
|
+
pattern: /\b(charge|refund|purchase|wire|payment|invoice|debit|credit)\b/i,
|
|
118
|
+
data_class: "payments",
|
|
119
|
+
authority_floor: "write",
|
|
120
|
+
scope: "either",
|
|
121
|
+
},
|
|
122
|
+
// ─── messaging ──────────────────────────────────────────────
|
|
123
|
+
{
|
|
124
|
+
pattern: /\b(send[_-]?(email|message|sms|notification)|notify|publish|broadcast)\b/i,
|
|
125
|
+
data_class: "messaging",
|
|
126
|
+
authority_floor: "write",
|
|
127
|
+
scope: "name",
|
|
128
|
+
},
|
|
129
|
+
{
|
|
130
|
+
pattern: /\b(email|notification|message|chat|page|alert)\b/i,
|
|
131
|
+
data_class: "messaging",
|
|
132
|
+
authority_floor: "read",
|
|
133
|
+
scope: "description",
|
|
134
|
+
},
|
|
135
|
+
// ─── system ─────────────────────────────────────────────────
|
|
136
|
+
{
|
|
137
|
+
pattern: /\b(get[_-]?env|environ(ment)?|process[_-]?(info|status)|sysinfo|system[_-]?info|hostname)\b/i,
|
|
138
|
+
data_class: "system",
|
|
139
|
+
authority_floor: "read",
|
|
140
|
+
scope: "either",
|
|
141
|
+
},
|
|
142
|
+
// ─── metadata / read-only ───────────────────────────────────
|
|
143
|
+
{
|
|
144
|
+
pattern: /\b(read[_-]?graph|search[_-]?nodes|open[_-]?nodes)\b/i,
|
|
145
|
+
data_class: "metadata",
|
|
146
|
+
authority_floor: "read",
|
|
147
|
+
scope: "name",
|
|
148
|
+
},
|
|
149
|
+
{
|
|
150
|
+
pattern: /\b(echo|get[_-]?(structured[_-]?content|annotated[_-]?message|resource[_-]?(links|reference)|sum|tiny[_-]?image))\b/i,
|
|
151
|
+
data_class: "metadata",
|
|
152
|
+
authority_floor: "read",
|
|
153
|
+
scope: "name",
|
|
154
|
+
},
|
|
155
|
+
{
|
|
156
|
+
pattern: /\b(toggle[_-]?(simulated[_-]?logging|subscriber[_-]?updates))\b/i,
|
|
157
|
+
data_class: "system",
|
|
158
|
+
authority_floor: "write",
|
|
159
|
+
scope: "name",
|
|
160
|
+
},
|
|
161
|
+
// ─── memory-server CRUD on a knowledge graph ─────────────────
|
|
162
|
+
{
|
|
163
|
+
pattern: /\b(create[_-]?(entities|relations))\b/i,
|
|
164
|
+
data_class: "metadata",
|
|
165
|
+
authority_floor: "write",
|
|
166
|
+
scope: "name",
|
|
167
|
+
},
|
|
168
|
+
{
|
|
169
|
+
pattern: /\b(add[_-]?observations)\b/i,
|
|
170
|
+
data_class: "metadata",
|
|
171
|
+
authority_floor: "write",
|
|
172
|
+
scope: "name",
|
|
173
|
+
},
|
|
174
|
+
{
|
|
175
|
+
pattern: /\b(delete[_-]?(entities|relations|observations))\b/i,
|
|
176
|
+
data_class: "metadata",
|
|
177
|
+
authority_floor: "destructive",
|
|
178
|
+
scope: "name",
|
|
179
|
+
},
|
|
180
|
+
// ─── thinking / opaque reasoning tools ──────────────────────
|
|
181
|
+
{
|
|
182
|
+
pattern: /\b(sequentialthinking|reason|plan|think)\b/i,
|
|
183
|
+
data_class: "metadata",
|
|
184
|
+
authority_floor: "read",
|
|
185
|
+
scope: "name",
|
|
186
|
+
},
|
|
187
|
+
// ─── trigger-shaped + simulate-shaped (test surfaces) ────────
|
|
188
|
+
{
|
|
189
|
+
pattern: /\b(trigger[_-]?(long[_-]?running[_-]?operation)?|simulate[_-]?(research[_-]?query))\b/i,
|
|
190
|
+
data_class: "metadata",
|
|
191
|
+
authority_floor: "write",
|
|
192
|
+
scope: "name",
|
|
193
|
+
},
|
|
194
|
+
];
|
|
195
|
+
/** Rank the four authority levels so we can take the strongest signal. */
|
|
196
|
+
const AUTHORITY_RANK = {
|
|
197
|
+
read: 0,
|
|
198
|
+
write: 1,
|
|
199
|
+
destructive: 2,
|
|
200
|
+
privileged: 3,
|
|
201
|
+
};
|
|
202
|
+
/** Return the higher of two authority levels (lattice join). */
|
|
203
|
+
export function maxAuthority(a, b) {
|
|
204
|
+
return AUTHORITY_RANK[a] >= AUTHORITY_RANK[b] ? a : b;
|
|
205
|
+
}
|
|
206
|
+
/** Escalate authority by one step (read → write → destructive → privileged). */
|
|
207
|
+
export function escalateAuthority(a) {
|
|
208
|
+
switch (a) {
|
|
209
|
+
case "read":
|
|
210
|
+
return "write";
|
|
211
|
+
case "write":
|
|
212
|
+
return "destructive";
|
|
213
|
+
case "destructive":
|
|
214
|
+
return "privileged";
|
|
215
|
+
case "privileged":
|
|
216
|
+
return "privileged"; // saturates
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
//# sourceMappingURL=rules.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rules.js","sourceRoot":"","sources":["../../src/classify/rules.ts"],"names":[],"mappings":"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"}
|