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,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Schema-violation axis.
|
|
3
|
+
*
|
|
4
|
+
* Send args that violate the declared schema in structural ways:
|
|
5
|
+
* extra fields the schema doesn't mention, missing required fields,
|
|
6
|
+
* wrong types in nested positions, deeply-nested objects,
|
|
7
|
+
* prototype pollution shapes.
|
|
8
|
+
*
|
|
9
|
+
* Per docs/SPEC.md §"Fuzzing strategy".
|
|
10
|
+
*/
|
|
11
|
+
/** Generate schema-violation inputs for one tool. */
|
|
12
|
+
export function* generateSchemaViolation(facts, _prng) {
|
|
13
|
+
// Even for argless tools, send unexpected payloads.
|
|
14
|
+
yield {
|
|
15
|
+
args: { __injected: "x" },
|
|
16
|
+
rationale: "schema-violation: extra field on argless tool",
|
|
17
|
+
};
|
|
18
|
+
yield {
|
|
19
|
+
args: { __proto__: { polluted: true } },
|
|
20
|
+
rationale: "schema-violation: __proto__ pollution attempt",
|
|
21
|
+
};
|
|
22
|
+
yield {
|
|
23
|
+
args: deepNest(50),
|
|
24
|
+
rationale: "schema-violation: 50-deep nested object",
|
|
25
|
+
};
|
|
26
|
+
if (facts.isArgless)
|
|
27
|
+
return;
|
|
28
|
+
// Missing-each-required-field — covered by boundary axis too, but
|
|
29
|
+
// the rationale string is different so we keep both for forensic
|
|
30
|
+
// clarity in the fuzz output.
|
|
31
|
+
for (const arg of facts.args) {
|
|
32
|
+
if (!arg.required)
|
|
33
|
+
continue;
|
|
34
|
+
const argName = arg.path[0];
|
|
35
|
+
if (argName === undefined)
|
|
36
|
+
continue;
|
|
37
|
+
const stripped = baseShape(facts);
|
|
38
|
+
delete stripped[argName];
|
|
39
|
+
yield {
|
|
40
|
+
args: stripped,
|
|
41
|
+
rationale: `schema-violation: required "${argName}" omitted`,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
// Extra fields alongside valid args.
|
|
45
|
+
const baseArgs = baseShape(facts);
|
|
46
|
+
yield {
|
|
47
|
+
args: { ...baseArgs, __injected: "x" },
|
|
48
|
+
rationale: "schema-violation: extra field __injected",
|
|
49
|
+
};
|
|
50
|
+
yield {
|
|
51
|
+
args: { ...baseArgs, constructor: { prototype: { polluted: true } } },
|
|
52
|
+
rationale: "schema-violation: constructor.prototype pollution attempt",
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
function baseShape(facts) {
|
|
56
|
+
const out = {};
|
|
57
|
+
for (const arg of facts.args) {
|
|
58
|
+
const argName = arg.path[0];
|
|
59
|
+
if (argName === undefined)
|
|
60
|
+
continue;
|
|
61
|
+
if (!arg.required)
|
|
62
|
+
continue;
|
|
63
|
+
out[argName] = arg.declaredType === "string" ? "x" : 0;
|
|
64
|
+
}
|
|
65
|
+
return out;
|
|
66
|
+
}
|
|
67
|
+
function deepNest(depth) {
|
|
68
|
+
let cur = "leaf";
|
|
69
|
+
for (let i = 0; i < depth; i++) {
|
|
70
|
+
cur = { nested: cur };
|
|
71
|
+
}
|
|
72
|
+
return { deep: cur };
|
|
73
|
+
}
|
|
74
|
+
//# sourceMappingURL=schema-violation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema-violation.js","sourceRoot":"","sources":["../../../src/fuzz/axes/schema-violation.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAKH,qDAAqD;AACrD,MAAM,SAAS,CAAC,CAAC,uBAAuB,CACtC,KAAgB,EAChB,KAAW;IAEX,oDAAoD;IACpD,MAAM;QACJ,IAAI,EAAE,EAAE,UAAU,EAAE,GAAG,EAA6B;QACpD,SAAS,EAAE,+CAA+C;KAC3D,CAAC;IACF,MAAM;QACJ,IAAI,EAAE,EAAE,SAAS,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,EAA6B;QAClE,SAAS,EAAE,+CAA+C;KAC3D,CAAC;IACF,MAAM;QACJ,IAAI,EAAE,QAAQ,CAAC,EAAE,CAAC;QAClB,SAAS,EAAE,yCAAyC;KACrD,CAAC;IAEF,IAAI,KAAK,CAAC,SAAS;QAAE,OAAO;IAE5B,kEAAkE;IAClE,iEAAiE;IACjE,8BAA8B;IAC9B,KAAK,MAAM,GAAG,IAAI,KAAK,CAAC,IAAI,EAAE,CAAC;QAC7B,IAAI,CAAC,GAAG,CAAC,QAAQ;YAAE,SAAS;QAC5B,MAAM,OAAO,GAAG,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAC5B,IAAI,OAAO,KAAK,SAAS;YAAE,SAAS;QACpC,MAAM,QAAQ,GAAG,SAAS,CAAC,KAAK,CAAC,CAAC;QAClC,OAAO,QAAQ,CAAC,OAAO,CAAC,CAAC;QACzB,MAAM;YACJ,IAAI,EAAE,QAAQ;YACd,SAAS,EAAE,+BAA+B,OAAO,WAAW;SAC7D,CAAC;IACJ,CAAC;IAED,qCAAqC;IACrC,MAAM,QAAQ,GAAG,SAAS,CAAC,KAAK,CAAC,CAAC;IAClC,MAAM;QACJ,IAAI,EAAE,EAAE,GAAG,QAAQ,EAAE,UAAU,EAAE,GAAG,EAAE;QACtC,SAAS,EAAE,0CAA0C;KACtD,CAAC;IACF,MAAM;QACJ,IAAI,EAAE,EAAE,GAAG,QAAQ,EAAE,WAAW,EAAE,EAAE,SAAS,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,EAAE,EAAE;QACrE,SAAS,EAAE,2DAA2D;KACvE,CAAC;AACJ,CAAC;AAED,SAAS,SAAS,CAAC,KAAgB;IACjC,MAAM,GAAG,GAA4B,EAAE,CAAC;IACxC,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;QACpC,IAAI,CAAC,GAAG,CAAC,QAAQ;YAAE,SAAS;QAC5B,GAAG,CAAC,OAAO,CAAC,GAAG,GAAG,CAAC,YAAY,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;IACzD,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,SAAS,QAAQ,CAAC,KAAa;IAC7B,IAAI,GAAG,GAAY,MAAM,CAAC;IAC1B,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,EAAE,CAAC,EAAE,EAAE,CAAC;QAC/B,GAAG,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC;IACxB,CAAC;IACD,OAAO,EAAE,IAAI,EAAE,GAAG,EAAE,CAAC;AACvB,CAAC"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Type-confusion axis.
|
|
3
|
+
*
|
|
4
|
+
* For each typed arg, send a value of the wrong type. Detects servers
|
|
5
|
+
* that don't validate types and pass through to weakly-typed runtimes
|
|
6
|
+
* (e.g. JS runtime that coerces silently).
|
|
7
|
+
*
|
|
8
|
+
* Per docs/SPEC.md §"Fuzzing strategy".
|
|
9
|
+
*/
|
|
10
|
+
import type { Prng } from "../prng.js";
|
|
11
|
+
import type { ToolFacts } from "../schema.js";
|
|
12
|
+
/** Generate type-confused inputs for one tool. */
|
|
13
|
+
export declare function generateTypeConfusion(facts: ToolFacts, _prng: Prng): Generator<{
|
|
14
|
+
args: Record<string, unknown>;
|
|
15
|
+
rationale: string;
|
|
16
|
+
}>;
|
|
17
|
+
//# sourceMappingURL=type-confusion.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"type-confusion.d.ts","sourceRoot":"","sources":["../../../src/fuzz/axes/type-confusion.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AACvC,OAAO,KAAK,EAAc,SAAS,EAAE,MAAM,cAAc,CAAC;AAuB1D,kDAAkD;AAClD,wBAAiB,qBAAqB,CACpC,KAAK,EAAE,SAAS,EAChB,KAAK,EAAE,IAAI,GACV,SAAS,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,CAAC,CAkBjE"}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Type-confusion axis.
|
|
3
|
+
*
|
|
4
|
+
* For each typed arg, send a value of the wrong type. Detects servers
|
|
5
|
+
* that don't validate types and pass through to weakly-typed runtimes
|
|
6
|
+
* (e.g. JS runtime that coerces silently).
|
|
7
|
+
*
|
|
8
|
+
* Per docs/SPEC.md §"Fuzzing strategy".
|
|
9
|
+
*/
|
|
10
|
+
const ALL_TYPES = [
|
|
11
|
+
"string",
|
|
12
|
+
"number",
|
|
13
|
+
"integer",
|
|
14
|
+
"boolean",
|
|
15
|
+
"array",
|
|
16
|
+
"object",
|
|
17
|
+
"null",
|
|
18
|
+
];
|
|
19
|
+
/** A representative value of each primitive type for substitution. */
|
|
20
|
+
const SAMPLE_OF = {
|
|
21
|
+
string: "wrong-type-here",
|
|
22
|
+
number: 3.14,
|
|
23
|
+
integer: 42,
|
|
24
|
+
boolean: true,
|
|
25
|
+
array: ["element"],
|
|
26
|
+
object: { nested: true },
|
|
27
|
+
null: null,
|
|
28
|
+
};
|
|
29
|
+
/** Generate type-confused inputs for one tool. */
|
|
30
|
+
export function* generateTypeConfusion(facts, _prng) {
|
|
31
|
+
if (facts.isArgless)
|
|
32
|
+
return;
|
|
33
|
+
for (const arg of facts.args) {
|
|
34
|
+
const argName = arg.path[0];
|
|
35
|
+
if (argName === undefined)
|
|
36
|
+
continue;
|
|
37
|
+
if (arg.declaredType === "unknown")
|
|
38
|
+
continue;
|
|
39
|
+
// Substitute a value of every other type.
|
|
40
|
+
for (const otherType of ALL_TYPES) {
|
|
41
|
+
if (otherType === arg.declaredType)
|
|
42
|
+
continue;
|
|
43
|
+
const baseArgs = baseShape(facts);
|
|
44
|
+
yield {
|
|
45
|
+
args: { ...baseArgs, [argName]: SAMPLE_OF[otherType] },
|
|
46
|
+
rationale: `type-confusion arg "${argName}" declared ${arg.declaredType}, sent ${otherType}`,
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
function baseShape(facts) {
|
|
52
|
+
const out = {};
|
|
53
|
+
for (const arg of facts.args) {
|
|
54
|
+
const argName = arg.path[0];
|
|
55
|
+
if (argName === undefined)
|
|
56
|
+
continue;
|
|
57
|
+
if (!arg.required)
|
|
58
|
+
continue;
|
|
59
|
+
if (arg.declaredType === "unknown") {
|
|
60
|
+
out[argName] = "x";
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
63
|
+
out[argName] = SAMPLE_OF[arg.declaredType];
|
|
64
|
+
}
|
|
65
|
+
return out;
|
|
66
|
+
}
|
|
67
|
+
//# sourceMappingURL=type-confusion.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"type-confusion.js","sourceRoot":"","sources":["../../../src/fuzz/axes/type-confusion.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAKH,MAAM,SAAS,GAAiB;IAC9B,QAAQ;IACR,QAAQ;IACR,SAAS;IACT,SAAS;IACT,OAAO;IACP,QAAQ;IACR,MAAM;CACP,CAAC;AAEF,sEAAsE;AACtE,MAAM,SAAS,GAAgC;IAC7C,MAAM,EAAE,iBAAiB;IACzB,MAAM,EAAE,IAAI;IACZ,OAAO,EAAE,EAAE;IACX,OAAO,EAAE,IAAI;IACb,KAAK,EAAE,CAAC,SAAS,CAAC;IAClB,MAAM,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE;IACxB,IAAI,EAAE,IAAI;CACX,CAAC;AAEF,kDAAkD;AAClD,MAAM,SAAS,CAAC,CAAC,qBAAqB,CACpC,KAAgB,EAChB,KAAW;IAEX,IAAI,KAAK,CAAC,SAAS;QAAE,OAAO;IAE5B,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;QACpC,IAAI,GAAG,CAAC,YAAY,KAAK,SAAS;YAAE,SAAS;QAE7C,0CAA0C;QAC1C,KAAK,MAAM,SAAS,IAAI,SAAS,EAAE,CAAC;YAClC,IAAI,SAAS,KAAK,GAAG,CAAC,YAAY;gBAAE,SAAS;YAC7C,MAAM,QAAQ,GAAG,SAAS,CAAC,KAAK,CAAC,CAAC;YAClC,MAAM;gBACJ,IAAI,EAAE,EAAE,GAAG,QAAQ,EAAE,CAAC,OAAO,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,EAAE;gBACtD,SAAS,EAAE,uBAAuB,OAAO,cAAc,GAAG,CAAC,YAAY,UAAU,SAAS,EAAE;aAC7F,CAAC;QACJ,CAAC;IACH,CAAC;AACH,CAAC;AAED,SAAS,SAAS,CAAC,KAAgB;IACjC,MAAM,GAAG,GAA4B,EAAE,CAAC;IACxC,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;QACpC,IAAI,CAAC,GAAG,CAAC,QAAQ;YAAE,SAAS;QAC5B,IAAI,GAAG,CAAC,YAAY,KAAK,SAAS,EAAE,CAAC;YACnC,GAAG,CAAC,OAAO,CAAC,GAAG,GAAG,CAAC;YACnB,SAAS;QACX,CAAC;QACD,GAAG,CAAC,OAAO,CAAC,GAAG,SAAS,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC;IAC7C,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* URL-hostility axis.
|
|
3
|
+
*
|
|
4
|
+
* For each URL-shaped arg (heuristic: name contains `url`, `origin`,
|
|
5
|
+
* `endpoint`, etc.), send URL-shaped payloads designed to confuse
|
|
6
|
+
* URL parsers and canonicalisers.
|
|
7
|
+
*
|
|
8
|
+
* Per docs/SPEC.md §"Fuzzing strategy".
|
|
9
|
+
*/
|
|
10
|
+
import type { Prng } from "../prng.js";
|
|
11
|
+
import type { ToolFacts } from "../schema.js";
|
|
12
|
+
/** Generate URL-hostility inputs for one tool. */
|
|
13
|
+
export declare function generateUrlHostility(facts: ToolFacts, _prng: Prng): Generator<{
|
|
14
|
+
args: Record<string, unknown>;
|
|
15
|
+
rationale: string;
|
|
16
|
+
}>;
|
|
17
|
+
//# sourceMappingURL=url-hostility.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"url-hostility.d.ts","sourceRoot":"","sources":["../../../src/fuzz/axes/url-hostility.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AACvC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAqB9C,kDAAkD;AAClD,wBAAiB,oBAAoB,CACnC,KAAK,EAAE,SAAS,EAChB,KAAK,EAAE,IAAI,GACV,SAAS,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,CAAC,CAiBjE"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* URL-hostility axis.
|
|
3
|
+
*
|
|
4
|
+
* For each URL-shaped arg (heuristic: name contains `url`, `origin`,
|
|
5
|
+
* `endpoint`, etc.), send URL-shaped payloads designed to confuse
|
|
6
|
+
* URL parsers and canonicalisers.
|
|
7
|
+
*
|
|
8
|
+
* Per docs/SPEC.md §"Fuzzing strategy".
|
|
9
|
+
*/
|
|
10
|
+
const URL_PAYLOADS = [
|
|
11
|
+
{ value: "https://attacker.example@victim.example/", reason: "userinfo-splitting" },
|
|
12
|
+
{ value: "https://victim.example/@attacker.example/", reason: "fake-userinfo segment" },
|
|
13
|
+
// Cyrillic-а homograph in `аpi.example.com`
|
|
14
|
+
{ value: "https://аpi.example.com/x", reason: "Cyrillic-a IDN homograph" },
|
|
15
|
+
{ value: "https://xn--pi-6kc.example.com/x", reason: "punycode IDN form" },
|
|
16
|
+
{ value: "javascript:alert(1)", reason: "javascript: scheme" },
|
|
17
|
+
{ value: "data:text/plain;base64,SGVsbG8=", reason: "data: scheme" },
|
|
18
|
+
{ value: "file:///etc/passwd", reason: "file:// scheme" },
|
|
19
|
+
{ value: "http://localhost:80/", reason: "loopback default port" },
|
|
20
|
+
{ value: "http://127.0.0.1/", reason: "loopback IP literal" },
|
|
21
|
+
{ value: "http://169.254.169.254/latest/meta-data/", reason: "AWS instance metadata" },
|
|
22
|
+
{ value: "http://[::]/", reason: "IPv6 unspecified" },
|
|
23
|
+
{ value: "http://0.0.0.0/", reason: "IPv4 unspecified" },
|
|
24
|
+
{ value: "http://example.com\\.attacker.com/", reason: "backslash-host trick" },
|
|
25
|
+
{ value: "http:////example.com/x", reason: "extra slashes" },
|
|
26
|
+
{ value: "not-a-url-at-all", reason: "unparseable string" },
|
|
27
|
+
];
|
|
28
|
+
/** Generate URL-hostility inputs for one tool. */
|
|
29
|
+
export function* generateUrlHostility(facts, _prng) {
|
|
30
|
+
if (facts.isArgless)
|
|
31
|
+
return;
|
|
32
|
+
for (const arg of facts.args) {
|
|
33
|
+
const argName = arg.path[0];
|
|
34
|
+
if (argName === undefined)
|
|
35
|
+
continue;
|
|
36
|
+
if (!arg.isUrlShaped)
|
|
37
|
+
continue;
|
|
38
|
+
if (arg.declaredType !== "string" && arg.declaredType !== "unknown")
|
|
39
|
+
continue;
|
|
40
|
+
const baseArgs = baseShape(facts);
|
|
41
|
+
for (const { value, reason } of URL_PAYLOADS) {
|
|
42
|
+
yield {
|
|
43
|
+
args: { ...baseArgs, [argName]: value },
|
|
44
|
+
rationale: `url-hostility "${argName}": ${reason}`,
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
function baseShape(facts) {
|
|
50
|
+
const out = {};
|
|
51
|
+
for (const arg of facts.args) {
|
|
52
|
+
const argName = arg.path[0];
|
|
53
|
+
if (argName === undefined)
|
|
54
|
+
continue;
|
|
55
|
+
if (!arg.required)
|
|
56
|
+
continue;
|
|
57
|
+
out[argName] = arg.declaredType === "string" ? "x" : 0;
|
|
58
|
+
}
|
|
59
|
+
return out;
|
|
60
|
+
}
|
|
61
|
+
//# sourceMappingURL=url-hostility.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"url-hostility.js","sourceRoot":"","sources":["../../../src/fuzz/axes/url-hostility.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAKH,MAAM,YAAY,GAA6C;IAC7D,EAAE,KAAK,EAAE,0CAA0C,EAAE,MAAM,EAAE,oBAAoB,EAAE;IACnF,EAAE,KAAK,EAAE,2CAA2C,EAAE,MAAM,EAAE,uBAAuB,EAAE;IACvF,4CAA4C;IAC5C,EAAE,KAAK,EAAE,2BAA2B,EAAE,MAAM,EAAE,0BAA0B,EAAE;IAC1E,EAAE,KAAK,EAAE,kCAAkC,EAAE,MAAM,EAAE,mBAAmB,EAAE;IAC1E,EAAE,KAAK,EAAE,qBAAqB,EAAE,MAAM,EAAE,oBAAoB,EAAE;IAC9D,EAAE,KAAK,EAAE,iCAAiC,EAAE,MAAM,EAAE,cAAc,EAAE;IACpE,EAAE,KAAK,EAAE,oBAAoB,EAAE,MAAM,EAAE,gBAAgB,EAAE;IACzD,EAAE,KAAK,EAAE,sBAAsB,EAAE,MAAM,EAAE,uBAAuB,EAAE;IAClE,EAAE,KAAK,EAAE,mBAAmB,EAAE,MAAM,EAAE,qBAAqB,EAAE;IAC7D,EAAE,KAAK,EAAE,0CAA0C,EAAE,MAAM,EAAE,uBAAuB,EAAE;IACtF,EAAE,KAAK,EAAE,cAAc,EAAE,MAAM,EAAE,kBAAkB,EAAE;IACrD,EAAE,KAAK,EAAE,iBAAiB,EAAE,MAAM,EAAE,kBAAkB,EAAE;IACxD,EAAE,KAAK,EAAE,oCAAoC,EAAE,MAAM,EAAE,sBAAsB,EAAE;IAC/E,EAAE,KAAK,EAAE,wBAAwB,EAAE,MAAM,EAAE,eAAe,EAAE;IAC5D,EAAE,KAAK,EAAE,kBAAkB,EAAE,MAAM,EAAE,oBAAoB,EAAE;CAC5D,CAAC;AAEF,kDAAkD;AAClD,MAAM,SAAS,CAAC,CAAC,oBAAoB,CACnC,KAAgB,EAChB,KAAW;IAEX,IAAI,KAAK,CAAC,SAAS;QAAE,OAAO;IAE5B,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;QACpC,IAAI,CAAC,GAAG,CAAC,WAAW;YAAE,SAAS;QAC/B,IAAI,GAAG,CAAC,YAAY,KAAK,QAAQ,IAAI,GAAG,CAAC,YAAY,KAAK,SAAS;YAAE,SAAS;QAE9E,MAAM,QAAQ,GAAG,SAAS,CAAC,KAAK,CAAC,CAAC;QAClC,KAAK,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,YAAY,EAAE,CAAC;YAC7C,MAAM;gBACJ,IAAI,EAAE,EAAE,GAAG,QAAQ,EAAE,CAAC,OAAO,CAAC,EAAE,KAAK,EAAE;gBACvC,SAAS,EAAE,kBAAkB,OAAO,MAAM,MAAM,EAAE;aACnD,CAAC;QACJ,CAAC;IACH,CAAC;AACH,CAAC;AAED,SAAS,SAAS,CAAC,KAAgB;IACjC,MAAM,GAAG,GAA4B,EAAE,CAAC;IACxC,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;QACpC,IAAI,CAAC,GAAG,CAAC,QAAQ;YAAE,SAAS;QAC5B,GAAG,CAAC,OAAO,CAAC,GAAG,GAAG,CAAC,YAAY,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;IACzD,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `fuzz` — runs the schema-aware adversarial fuzzer against an MCP
|
|
3
|
+
* server and emits a v0.1 fuzz document.
|
|
4
|
+
*
|
|
5
|
+
* Per docs/SPEC.md §"Fuzzing strategy":
|
|
6
|
+
*
|
|
7
|
+
* - Six axes (boundary / type-confusion / encoding / path-traversal /
|
|
8
|
+
* url-hostility / schema-violation)
|
|
9
|
+
* - Default budget: 200 calls per tool
|
|
10
|
+
* - Deterministic PRNG, default seed 0xC0FFEE
|
|
11
|
+
* - Records what happened — does NOT classify "success"
|
|
12
|
+
*
|
|
13
|
+
* The output JSON is consumed by the classifier (week 3) and the
|
|
14
|
+
* reporter (week 3). Every entry has a `rationale` so a human
|
|
15
|
+
* reviewer can grep for the interesting cases without re-running.
|
|
16
|
+
*/
|
|
17
|
+
import type { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
18
|
+
import type { ToolInventory } from "../enumerate.js";
|
|
19
|
+
import type { FuzzResults } from "./types.js";
|
|
20
|
+
export { FUZZ_SCHEMA } from "./types.js";
|
|
21
|
+
export type { FuzzAxis, FuzzCall, FuzzOutcome, FuzzResults, FuzzToolSummary, } from "./types.js";
|
|
22
|
+
export interface FuzzOptions {
|
|
23
|
+
/** Per-tool call budget. Default: 200. */
|
|
24
|
+
budget?: number;
|
|
25
|
+
/** PRNG seed for deterministic re-runs. Default: 0xC0FFEE. */
|
|
26
|
+
seed?: number;
|
|
27
|
+
/** If set, only fuzz these tool names (rest are skipped). */
|
|
28
|
+
onlyTools?: readonly string[];
|
|
29
|
+
/** Optional per-call timeout in ms. Default: 5000. */
|
|
30
|
+
timeoutMs?: number;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Run the fuzzer against `client`, given a previously-emitted
|
|
34
|
+
* `inventory`. Returns a fuzz document.
|
|
35
|
+
*
|
|
36
|
+
* The `inventory` parameter (instead of re-enumerating internally)
|
|
37
|
+
* is deliberate: the user can inspect / filter tools first, then
|
|
38
|
+
* pass the resulting subset.
|
|
39
|
+
*/
|
|
40
|
+
export declare function fuzz(client: Client, inventory: ToolInventory, options?: FuzzOptions): Promise<FuzzResults>;
|
|
41
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/fuzz/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAC;AAExE,OAAO,KAAK,EAAkB,aAAa,EAAE,MAAM,iBAAiB,CAAC;AACrE,OAAO,KAAK,EAAmC,WAAW,EAAE,MAAM,YAAY,CAAC;AAU/E,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AACzC,YAAY,EACV,QAAQ,EACR,QAAQ,EACR,WAAW,EACX,WAAW,EACX,eAAe,GAChB,MAAM,YAAY,CAAC;AAEpB,MAAM,WAAW,WAAW;IAC1B,0CAA0C;IAC1C,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,8DAA8D;IAC9D,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,6DAA6D;IAC7D,SAAS,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC9B,sDAAsD;IACtD,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;;;;;GAOG;AACH,wBAAsB,IAAI,CACxB,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,aAAa,EACxB,OAAO,GAAE,WAAgB,GACxB,OAAO,CAAC,WAAW,CAAC,CAqDtB"}
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `fuzz` — runs the schema-aware adversarial fuzzer against an MCP
|
|
3
|
+
* server and emits a v0.1 fuzz document.
|
|
4
|
+
*
|
|
5
|
+
* Per docs/SPEC.md §"Fuzzing strategy":
|
|
6
|
+
*
|
|
7
|
+
* - Six axes (boundary / type-confusion / encoding / path-traversal /
|
|
8
|
+
* url-hostility / schema-violation)
|
|
9
|
+
* - Default budget: 200 calls per tool
|
|
10
|
+
* - Deterministic PRNG, default seed 0xC0FFEE
|
|
11
|
+
* - Records what happened — does NOT classify "success"
|
|
12
|
+
*
|
|
13
|
+
* The output JSON is consumed by the classifier (week 3) and the
|
|
14
|
+
* reporter (week 3). Every entry has a `rationale` so a human
|
|
15
|
+
* reviewer can grep for the interesting cases without re-running.
|
|
16
|
+
*/
|
|
17
|
+
import { Prng } from "./prng.js";
|
|
18
|
+
import { DEFAULT_FUZZ_BUDGET, DEFAULT_FUZZ_SEED, extractToolFacts } from "./schema.js";
|
|
19
|
+
import { generateBoundary } from "./axes/boundary.js";
|
|
20
|
+
import { generateEncoding } from "./axes/encoding.js";
|
|
21
|
+
import { generatePathTraversal } from "./axes/path-traversal.js";
|
|
22
|
+
import { generateSchemaViolation } from "./axes/schema-violation.js";
|
|
23
|
+
import { generateTypeConfusion } from "./axes/type-confusion.js";
|
|
24
|
+
import { generateUrlHostility } from "./axes/url-hostility.js";
|
|
25
|
+
export { FUZZ_SCHEMA } from "./types.js";
|
|
26
|
+
/**
|
|
27
|
+
* Run the fuzzer against `client`, given a previously-emitted
|
|
28
|
+
* `inventory`. Returns a fuzz document.
|
|
29
|
+
*
|
|
30
|
+
* The `inventory` parameter (instead of re-enumerating internally)
|
|
31
|
+
* is deliberate: the user can inspect / filter tools first, then
|
|
32
|
+
* pass the resulting subset.
|
|
33
|
+
*/
|
|
34
|
+
export async function fuzz(client, inventory, options = {}) {
|
|
35
|
+
const budget = options.budget ?? DEFAULT_FUZZ_BUDGET;
|
|
36
|
+
const seed = options.seed ?? DEFAULT_FUZZ_SEED;
|
|
37
|
+
const timeoutMs = options.timeoutMs ?? 5_000;
|
|
38
|
+
const allowedTools = options.onlyTools ? new Set(options.onlyTools) : null;
|
|
39
|
+
const calls = [];
|
|
40
|
+
const perTool = {};
|
|
41
|
+
const prng = new Prng(seed);
|
|
42
|
+
for (const tool of inventory.tools) {
|
|
43
|
+
if (allowedTools && !allowedTools.has(tool.name))
|
|
44
|
+
continue;
|
|
45
|
+
const facts = extractToolFacts(tool.name, tool.inputSchema);
|
|
46
|
+
perTool[tool.name] = { ok: 0, protocol_error: 0, runtime_error: 0 };
|
|
47
|
+
let calledForTool = 0;
|
|
48
|
+
for (const { axis, args, rationale } of generateAllAxes(facts, prng)) {
|
|
49
|
+
if (calledForTool >= budget)
|
|
50
|
+
break;
|
|
51
|
+
calledForTool++;
|
|
52
|
+
const outcome = await callOnce(client, tool, args, timeoutMs);
|
|
53
|
+
// narrow to the index signature we just wrote above
|
|
54
|
+
const tally = perTool[tool.name];
|
|
55
|
+
tally[outcome.kind]++;
|
|
56
|
+
calls.push({
|
|
57
|
+
tool: tool.name,
|
|
58
|
+
axis,
|
|
59
|
+
rationale,
|
|
60
|
+
arguments: args,
|
|
61
|
+
outcome,
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return {
|
|
66
|
+
schema: "mcp-recon/v0.1/fuzz",
|
|
67
|
+
scanned_at: new Date().toISOString(),
|
|
68
|
+
server: inventory.server,
|
|
69
|
+
seed,
|
|
70
|
+
budget,
|
|
71
|
+
summary: Object.entries(perTool).map(([tool, t]) => ({
|
|
72
|
+
tool,
|
|
73
|
+
total: t.ok + t.protocol_error + t.runtime_error,
|
|
74
|
+
ok: t.ok,
|
|
75
|
+
protocol_error: t.protocol_error,
|
|
76
|
+
runtime_error: t.runtime_error,
|
|
77
|
+
})),
|
|
78
|
+
calls,
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
/** Concatenate all six axis-generators into one stream of (axis, input) pairs. */
|
|
82
|
+
function* generateAllAxes(facts, prng) {
|
|
83
|
+
for (const v of generateBoundary(facts, prng))
|
|
84
|
+
yield { axis: "boundary_values", ...v };
|
|
85
|
+
for (const v of generateTypeConfusion(facts, prng))
|
|
86
|
+
yield { axis: "type_confusion", ...v };
|
|
87
|
+
for (const v of generateEncoding(facts, prng))
|
|
88
|
+
yield { axis: "encoding_tricks", ...v };
|
|
89
|
+
for (const v of generatePathTraversal(facts, prng))
|
|
90
|
+
yield { axis: "path_traversal", ...v };
|
|
91
|
+
for (const v of generateUrlHostility(facts, prng))
|
|
92
|
+
yield { axis: "url_hostility", ...v };
|
|
93
|
+
for (const v of generateSchemaViolation(facts, prng))
|
|
94
|
+
yield { axis: "schema_violation", ...v };
|
|
95
|
+
}
|
|
96
|
+
async function callOnce(client, tool, args, timeoutMs) {
|
|
97
|
+
// Race the call against a timeout so a hanging server doesn't
|
|
98
|
+
// freeze the whole fuzz run.
|
|
99
|
+
let timeoutHandle;
|
|
100
|
+
const timeout = new Promise((resolve) => {
|
|
101
|
+
timeoutHandle = setTimeout(() => {
|
|
102
|
+
resolve({ kind: "runtime_error", message: `timeout after ${timeoutMs}ms` });
|
|
103
|
+
}, timeoutMs);
|
|
104
|
+
});
|
|
105
|
+
const call = (async () => {
|
|
106
|
+
try {
|
|
107
|
+
const result = await client.callTool({ name: tool.name, arguments: args });
|
|
108
|
+
// The MCP SDK returns `{ content, isError }`. `isError === true`
|
|
109
|
+
// means the tool returned a structured error — that's a
|
|
110
|
+
// protocol error in our taxonomy (the tool said "no" cleanly).
|
|
111
|
+
const r = result;
|
|
112
|
+
if (r.isError) {
|
|
113
|
+
return {
|
|
114
|
+
kind: "protocol_error",
|
|
115
|
+
message: snippet(JSON.stringify(r.content ?? "")),
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
return { kind: "ok", snippet: snippet(JSON.stringify(r.content ?? "")) };
|
|
119
|
+
}
|
|
120
|
+
catch (err) {
|
|
121
|
+
// SDK threw — usually a transport-level or JSON-RPC-level
|
|
122
|
+
// error. We treat MCP-level error responses as `protocol_error`
|
|
123
|
+
// and everything else as `runtime_error`. The SDK throws an
|
|
124
|
+
// `McpError` for protocol errors; in lieu of import-time
|
|
125
|
+
// coupling to that class, we string-match on the message.
|
|
126
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
127
|
+
const isProtocolError = /MCP error|JSON-RPC|invalid params|method not found|invalid request/i
|
|
128
|
+
.test(message);
|
|
129
|
+
return isProtocolError
|
|
130
|
+
? { kind: "protocol_error", message: snippet(message) }
|
|
131
|
+
: { kind: "runtime_error", message: snippet(message) };
|
|
132
|
+
}
|
|
133
|
+
})();
|
|
134
|
+
try {
|
|
135
|
+
return await Promise.race([call, timeout]);
|
|
136
|
+
}
|
|
137
|
+
finally {
|
|
138
|
+
if (timeoutHandle !== undefined)
|
|
139
|
+
clearTimeout(timeoutHandle);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
function snippet(s, max = 200) {
|
|
143
|
+
if (s.length <= max)
|
|
144
|
+
return s;
|
|
145
|
+
return `${s.slice(0, max)}…[len=${s.length}]`;
|
|
146
|
+
}
|
|
147
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/fuzz/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Seedable deterministic PRNG.
|
|
3
|
+
*
|
|
4
|
+
* The fuzzer's reproducibility contract requires that running with the
|
|
5
|
+
* same seed produces bit-identical inputs. A `Math.random()`-style
|
|
6
|
+
* non-seeded source breaks that.
|
|
7
|
+
*
|
|
8
|
+
* Implementation: mulberry32. 32-bit state, decent statistical
|
|
9
|
+
* quality for non-cryptographic use, fits in 8 lines. Period 2^32 —
|
|
10
|
+
* more than enough for a fuzz budget that's typically <10^4 calls.
|
|
11
|
+
*
|
|
12
|
+
* Reference: https://gist.github.com/tommyettinger/46a3a48676ef5ec4a4e4
|
|
13
|
+
*/
|
|
14
|
+
export declare class Prng {
|
|
15
|
+
private state;
|
|
16
|
+
constructor(seed?: number);
|
|
17
|
+
/** Uniform [0, 1). */
|
|
18
|
+
next(): number;
|
|
19
|
+
/** Integer in [lo, hi). */
|
|
20
|
+
intInRange(lo: number, hi: number): number;
|
|
21
|
+
/** Pick a random element of an array. Throws on empty. */
|
|
22
|
+
pick<T>(items: readonly T[]): T;
|
|
23
|
+
/** Bias coin — true with probability p (0..1). */
|
|
24
|
+
bool(p?: number): boolean;
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=prng.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prng.d.ts","sourceRoot":"","sources":["../../src/fuzz/prng.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAIH,qBAAa,IAAI;IACf,OAAO,CAAC,KAAK,CAAS;gBAEV,IAAI,GAAE,MAA0B;IAK5C,sBAAsB;IACtB,IAAI,IAAI,MAAM;IAOd,2BAA2B;IAC3B,UAAU,CAAC,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,MAAM;IAO1C,0DAA0D;IAC1D,IAAI,CAAC,CAAC,EAAE,KAAK,EAAE,SAAS,CAAC,EAAE,GAAG,CAAC;IAY/B,kDAAkD;IAClD,IAAI,CAAC,CAAC,SAAM,GAAG,OAAO;CAGvB"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Seedable deterministic PRNG.
|
|
3
|
+
*
|
|
4
|
+
* The fuzzer's reproducibility contract requires that running with the
|
|
5
|
+
* same seed produces bit-identical inputs. A `Math.random()`-style
|
|
6
|
+
* non-seeded source breaks that.
|
|
7
|
+
*
|
|
8
|
+
* Implementation: mulberry32. 32-bit state, decent statistical
|
|
9
|
+
* quality for non-cryptographic use, fits in 8 lines. Period 2^32 —
|
|
10
|
+
* more than enough for a fuzz budget that's typically <10^4 calls.
|
|
11
|
+
*
|
|
12
|
+
* Reference: https://gist.github.com/tommyettinger/46a3a48676ef5ec4a4e4
|
|
13
|
+
*/
|
|
14
|
+
import { DEFAULT_FUZZ_SEED } from "./schema.js";
|
|
15
|
+
export class Prng {
|
|
16
|
+
state;
|
|
17
|
+
constructor(seed = DEFAULT_FUZZ_SEED) {
|
|
18
|
+
// Allow seed = 0 by mixing in a constant.
|
|
19
|
+
this.state = (seed | 0) ^ 0x6d2b79f5;
|
|
20
|
+
}
|
|
21
|
+
/** Uniform [0, 1). */
|
|
22
|
+
next() {
|
|
23
|
+
this.state = (this.state + 0x6d2b79f5) | 0;
|
|
24
|
+
let t = Math.imul(this.state ^ (this.state >>> 15), 1 | this.state);
|
|
25
|
+
t = (t + Math.imul(t ^ (t >>> 7), 61 | t)) ^ t;
|
|
26
|
+
return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
|
|
27
|
+
}
|
|
28
|
+
/** Integer in [lo, hi). */
|
|
29
|
+
intInRange(lo, hi) {
|
|
30
|
+
if (hi <= lo) {
|
|
31
|
+
throw new Error(`Prng.intInRange: hi (${hi}) must be > lo (${lo})`);
|
|
32
|
+
}
|
|
33
|
+
return Math.floor(this.next() * (hi - lo)) + lo;
|
|
34
|
+
}
|
|
35
|
+
/** Pick a random element of an array. Throws on empty. */
|
|
36
|
+
pick(items) {
|
|
37
|
+
if (items.length === 0) {
|
|
38
|
+
throw new Error("Prng.pick: empty array");
|
|
39
|
+
}
|
|
40
|
+
const item = items[this.intInRange(0, items.length)];
|
|
41
|
+
if (item === undefined) {
|
|
42
|
+
// unreachable given the in-range index, but TS doesn't know that
|
|
43
|
+
throw new Error("Prng.pick: undefined slot");
|
|
44
|
+
}
|
|
45
|
+
return item;
|
|
46
|
+
}
|
|
47
|
+
/** Bias coin — true with probability p (0..1). */
|
|
48
|
+
bool(p = 0.5) {
|
|
49
|
+
return this.next() < p;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
//# sourceMappingURL=prng.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prng.js","sourceRoot":"","sources":["../../src/fuzz/prng.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAEhD,MAAM,OAAO,IAAI;IACP,KAAK,CAAS;IAEtB,YAAY,OAAe,iBAAiB;QAC1C,0CAA0C;QAC1C,IAAI,CAAC,KAAK,GAAG,CAAC,IAAI,GAAG,CAAC,CAAC,GAAG,UAAU,CAAC;IACvC,CAAC;IAED,sBAAsB;IACtB,IAAI;QACF,IAAI,CAAC,KAAK,GAAG,CAAC,IAAI,CAAC,KAAK,GAAG,UAAU,CAAC,GAAG,CAAC,CAAC;QAC3C,IAAI,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,GAAG,CAAC,IAAI,CAAC,KAAK,KAAK,EAAE,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC;QACpE,CAAC,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;QAC/C,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,GAAG,UAAU,CAAC;IAC/C,CAAC;IAED,2BAA2B;IAC3B,UAAU,CAAC,EAAU,EAAE,EAAU;QAC/B,IAAI,EAAE,IAAI,EAAE,EAAE,CAAC;YACb,MAAM,IAAI,KAAK,CAAC,wBAAwB,EAAE,mBAAmB,EAAE,GAAG,CAAC,CAAC;QACtE,CAAC;QACD,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,EAAE,GAAG,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC,GAAG,EAAE,CAAC;IAClD,CAAC;IAED,0DAA0D;IAC1D,IAAI,CAAI,KAAmB;QACzB,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACvB,MAAM,IAAI,KAAK,CAAC,wBAAwB,CAAC,CAAC;QAC5C,CAAC;QACD,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC;QACrD,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;YACvB,iEAAiE;YACjE,MAAM,IAAI,KAAK,CAAC,2BAA2B,CAAC,CAAC;QAC/C,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;IAED,kDAAkD;IAClD,IAAI,CAAC,CAAC,GAAG,GAAG;QACV,OAAO,IAAI,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;IACzB,CAAC;CACF"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* JSON Schema walker — extracts what the fuzzer needs to know about a
|
|
3
|
+
* tool's argument shape.
|
|
4
|
+
*
|
|
5
|
+
* v0.1 supports JSON Schema Draft 7 (the dialect the official MCP
|
|
6
|
+
* filesystem server uses). We don't validate the schema — we extract
|
|
7
|
+
* structural facts: which keys exist, what types they declare, which
|
|
8
|
+
* are required, which look like paths or URLs.
|
|
9
|
+
*
|
|
10
|
+
* Heuristic shape-detection: arg names containing `path` are flagged
|
|
11
|
+
* path-shaped; arg names containing `url`, `origin`, `endpoint` are
|
|
12
|
+
* URL-shaped. The classifier (week 3) will refine these heuristics.
|
|
13
|
+
*/
|
|
14
|
+
/** Default seed for the deterministic fuzz PRNG (matches docs/SPEC.md). */
|
|
15
|
+
export declare const DEFAULT_FUZZ_SEED = 12648430;
|
|
16
|
+
/** Default per-tool fuzz budget (matches docs/SPEC.md). */
|
|
17
|
+
export declare const DEFAULT_FUZZ_BUDGET = 200;
|
|
18
|
+
/** Recognised JSON Schema primitive types we generate inputs for. */
|
|
19
|
+
export type SchemaType = "string" | "number" | "integer" | "boolean" | "array" | "object" | "null";
|
|
20
|
+
export interface ToolArgFact {
|
|
21
|
+
/** Path within the args object — e.g. ["path"], ["options", "recursive"]. */
|
|
22
|
+
path: string[];
|
|
23
|
+
/** Declared JSON Schema type. `unknown` if the schema didn't say. */
|
|
24
|
+
declaredType: SchemaType | "unknown";
|
|
25
|
+
/** Whether this arg is required (per the schema's `required` array). */
|
|
26
|
+
required: boolean;
|
|
27
|
+
/** Heuristic: is this arg name path-shaped (filesystem)? */
|
|
28
|
+
isPathShaped: boolean;
|
|
29
|
+
/** Heuristic: is this arg name URL-shaped (network)? */
|
|
30
|
+
isUrlShaped: boolean;
|
|
31
|
+
/** Heuristic: is this arg name command-shaped (shell)? */
|
|
32
|
+
isCommandShaped: boolean;
|
|
33
|
+
/** If declared as enum, the allowed values. */
|
|
34
|
+
enumValues?: readonly unknown[];
|
|
35
|
+
}
|
|
36
|
+
export interface ToolFacts {
|
|
37
|
+
/** Tool name. */
|
|
38
|
+
name: string;
|
|
39
|
+
/** All discovered arg facts (top-level only in v0.1). */
|
|
40
|
+
args: ToolArgFact[];
|
|
41
|
+
/** Whether the schema declares no args at all (an args-less tool). */
|
|
42
|
+
isArgless: boolean;
|
|
43
|
+
}
|
|
44
|
+
/** Walk one tool's `inputSchema`. Returns structural facts. */
|
|
45
|
+
export declare function extractToolFacts(name: string, inputSchema: unknown): ToolFacts;
|
|
46
|
+
//# sourceMappingURL=schema.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../src/fuzz/schema.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,2EAA2E;AAC3E,eAAO,MAAM,iBAAiB,WAAY,CAAC;AAE3C,2DAA2D;AAC3D,eAAO,MAAM,mBAAmB,MAAM,CAAC;AAEvC,qEAAqE;AACrE,MAAM,MAAM,UAAU,GAAG,QAAQ,GAAG,QAAQ,GAAG,SAAS,GAAG,SAAS,GAAG,OAAO,GAAG,QAAQ,GAAG,MAAM,CAAC;AAEnG,MAAM,WAAW,WAAW;IAC1B,6EAA6E;IAC7E,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,qEAAqE;IACrE,YAAY,EAAE,UAAU,GAAG,SAAS,CAAC;IACrC,wEAAwE;IACxE,QAAQ,EAAE,OAAO,CAAC;IAClB,4DAA4D;IAC5D,YAAY,EAAE,OAAO,CAAC;IACtB,wDAAwD;IACxD,WAAW,EAAE,OAAO,CAAC;IACrB,0DAA0D;IAC1D,eAAe,EAAE,OAAO,CAAC;IACzB,+CAA+C;IAC/C,UAAU,CAAC,EAAE,SAAS,OAAO,EAAE,CAAC;CACjC;AAED,MAAM,WAAW,SAAS;IACxB,iBAAiB;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,yDAAyD;IACzD,IAAI,EAAE,WAAW,EAAE,CAAC;IACpB,sEAAsE;IACtE,SAAS,EAAE,OAAO,CAAC;CACpB;AAED,+DAA+D;AAC/D,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,OAAO,GAAG,SAAS,CA4B9E"}
|