mcp-rce-guard 0.1.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/LICENSE +21 -0
- package/README.md +155 -0
- package/dist/audit/log.d.ts +75 -0
- package/dist/audit/log.d.ts.map +1 -0
- package/dist/audit/log.js +191 -0
- package/dist/audit/log.js.map +1 -0
- package/dist/canary/tracker.d.ts +38 -0
- package/dist/canary/tracker.d.ts.map +1 -0
- package/dist/canary/tracker.js +40 -0
- package/dist/canary/tracker.js.map +1 -0
- package/dist/cli.d.ts +14 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +128 -0
- package/dist/cli.js.map +1 -0
- package/dist/cve/replay.d.ts +44 -0
- package/dist/cve/replay.d.ts.map +1 -0
- package/dist/cve/replay.js +221 -0
- package/dist/cve/replay.js.map +1 -0
- package/dist/egress/policy.d.ts +27 -0
- package/dist/egress/policy.d.ts.map +1 -0
- package/dist/egress/policy.js +62 -0
- package/dist/egress/policy.js.map +1 -0
- package/dist/index.d.ts +27 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +38 -0
- package/dist/index.js.map +1 -0
- package/dist/isolation/cgroups.d.ts +20 -0
- package/dist/isolation/cgroups.d.ts.map +1 -0
- package/dist/isolation/cgroups.js +33 -0
- package/dist/isolation/cgroups.js.map +1 -0
- package/dist/isolation/landlock.d.ts +42 -0
- package/dist/isolation/landlock.d.ts.map +1 -0
- package/dist/isolation/landlock.js +67 -0
- package/dist/isolation/landlock.js.map +1 -0
- package/dist/isolation/platform.d.ts +27 -0
- package/dist/isolation/platform.d.ts.map +1 -0
- package/dist/isolation/platform.js +96 -0
- package/dist/isolation/platform.js.map +1 -0
- package/dist/isolation/sandbox-exec.d.ts +17 -0
- package/dist/isolation/sandbox-exec.d.ts.map +1 -0
- package/dist/isolation/sandbox-exec.js +58 -0
- package/dist/isolation/sandbox-exec.js.map +1 -0
- package/dist/normalize.d.ts +32 -0
- package/dist/normalize.d.ts.map +1 -0
- package/dist/normalize.js +66 -0
- package/dist/normalize.js.map +1 -0
- package/dist/server.d.ts +15 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +152 -0
- package/dist/server.js.map +1 -0
- package/dist/state.d.ts +34 -0
- package/dist/state.d.ts.map +1 -0
- package/dist/state.js +104 -0
- package/dist/state.js.map +1 -0
- package/dist/tools/audit.d.ts +26 -0
- package/dist/tools/audit.d.ts.map +1 -0
- package/dist/tools/audit.js +97 -0
- package/dist/tools/audit.js.map +1 -0
- package/dist/tools/getAuditLog.d.ts +34 -0
- package/dist/tools/getAuditLog.d.ts.map +1 -0
- package/dist/tools/getAuditLog.js +65 -0
- package/dist/tools/getAuditLog.js.map +1 -0
- package/dist/tools/injectEgress.d.ts +21 -0
- package/dist/tools/injectEgress.d.ts.map +1 -0
- package/dist/tools/injectEgress.js +49 -0
- package/dist/tools/injectEgress.js.map +1 -0
- package/dist/tools/register.d.ts +16 -0
- package/dist/tools/register.d.ts.map +1 -0
- package/dist/tools/register.js +44 -0
- package/dist/tools/register.js.map +1 -0
- package/dist/tools/scanCve.d.ts +14 -0
- package/dist/tools/scanCve.d.ts.map +1 -0
- package/dist/tools/scanCve.js +29 -0
- package/dist/tools/scanCve.js.map +1 -0
- package/dist/tools/trackCanary.d.ts +23 -0
- package/dist/tools/trackCanary.d.ts.map +1 -0
- package/dist/tools/trackCanary.js +44 -0
- package/dist/tools/trackCanary.js.map +1 -0
- package/dist/trust-tiers.d.ts +18 -0
- package/dist/trust-tiers.d.ts.map +1 -0
- package/dist/trust-tiers.js +73 -0
- package/dist/trust-tiers.js.map +1 -0
- package/dist/types.d.ts +187 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +82 -0
- package/dist/types.js.map +1 -0
- package/dist/version.d.ts +7 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +14 -0
- package/dist/version.js.map +1 -0
- package/package.json +74 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
|
package/dist/state.d.ts
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* In-memory subprocess registry + canary chain registry.
|
|
3
|
+
*
|
|
4
|
+
* Process-local state. The persistent audit-log lives in audit/log.ts (NDJSON).
|
|
5
|
+
*
|
|
6
|
+
* v0.1 trade-off: registry is in-memory and resets on server restart. v0.2 will
|
|
7
|
+
* persist to ~/.mcp-rce-guard/registry.json with optional Pillar-1 signing.
|
|
8
|
+
*/
|
|
9
|
+
import type { EgressMode, IsolationProfile, RegisteredSubprocess } from "./types.js";
|
|
10
|
+
interface CanaryChain {
|
|
11
|
+
chainId: string;
|
|
12
|
+
sourceServerId: string;
|
|
13
|
+
downstreamServerIds: string[];
|
|
14
|
+
canaryToken: string;
|
|
15
|
+
canaryPattern: string;
|
|
16
|
+
createdAt: string;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Compute a stable fingerprint for a profile. Two registrations with the
|
|
20
|
+
* same effective profile must produce the same fingerprint.
|
|
21
|
+
*/
|
|
22
|
+
export declare function profileFingerprint(profile: IsolationProfile): string;
|
|
23
|
+
export declare function generateHandle(prefix?: string): string;
|
|
24
|
+
export declare function registerSubprocess(serverId: string, binary: string, args: string[], trustTier: RegisteredSubprocess["trustTier"], profile: IsolationProfile): RegisteredSubprocess;
|
|
25
|
+
export declare function getSubprocess(handle: string): RegisteredSubprocess | undefined;
|
|
26
|
+
export declare function listSubprocesses(): readonly RegisteredSubprocess[];
|
|
27
|
+
export declare function updateEgressPolicy(handle: string, allowlist: string[], mode: EgressMode): RegisteredSubprocess | undefined;
|
|
28
|
+
export declare function bumpBlockedEgress(handle: string, by?: number): void;
|
|
29
|
+
export declare function clearRegistry(): void;
|
|
30
|
+
export declare function generateCanaryToken(): string;
|
|
31
|
+
export declare function registerCanaryChain(chainId: string, sourceServerId: string, downstreamServerIds: string[], canaryPattern: string, canaryToken: string): CanaryChain;
|
|
32
|
+
export declare function getCanaryChain(chainId: string): CanaryChain | undefined;
|
|
33
|
+
export {};
|
|
34
|
+
//# sourceMappingURL=state.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"state.d.ts","sourceRoot":"","sources":["../src/state.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,KAAK,EACV,UAAU,EACV,gBAAgB,EAChB,oBAAoB,EACrB,MAAM,YAAY,CAAC;AAIpB,UAAU,WAAW;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,cAAc,EAAE,MAAM,CAAC;IACvB,mBAAmB,EAAE,MAAM,EAAE,CAAC;IAC9B,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,EAAE,MAAM,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;CACnB;AAID;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,gBAAgB,GAAG,MAAM,CAUpE;AAED,wBAAgB,cAAc,CAAC,MAAM,SAAO,GAAG,MAAM,CAEpD;AAED,wBAAgB,kBAAkB,CAChC,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,EAAE,EACd,SAAS,EAAE,oBAAoB,CAAC,WAAW,CAAC,EAC5C,OAAO,EAAE,gBAAgB,GACxB,oBAAoB,CAkBtB;AAED,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,oBAAoB,GAAG,SAAS,CAE9E;AAED,wBAAgB,gBAAgB,IAAI,SAAS,oBAAoB,EAAE,CAElE;AAED,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,MAAM,EAAE,EACnB,IAAI,EAAE,UAAU,GACf,oBAAoB,GAAG,SAAS,CAgBlC;AAED,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,EAAE,EAAE,SAAI,GAAG,IAAI,CAI9D;AAED,wBAAgB,aAAa,IAAI,IAAI,CAGpC;AAID,wBAAgB,mBAAmB,IAAI,MAAM,CAI5C;AAED,wBAAgB,mBAAmB,CACjC,OAAO,EAAE,MAAM,EACf,cAAc,EAAE,MAAM,EACtB,mBAAmB,EAAE,MAAM,EAAE,EAC7B,aAAa,EAAE,MAAM,EACrB,WAAW,EAAE,MAAM,GAClB,WAAW,CAWb;AAED,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,WAAW,GAAG,SAAS,CAEvE"}
|
package/dist/state.js
ADDED
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* In-memory subprocess registry + canary chain registry.
|
|
3
|
+
*
|
|
4
|
+
* Process-local state. The persistent audit-log lives in audit/log.ts (NDJSON).
|
|
5
|
+
*
|
|
6
|
+
* v0.1 trade-off: registry is in-memory and resets on server restart. v0.2 will
|
|
7
|
+
* persist to ~/.mcp-rce-guard/registry.json with optional Pillar-1 signing.
|
|
8
|
+
*/
|
|
9
|
+
import { createHash, randomBytes } from "node:crypto";
|
|
10
|
+
const subprocesses = new Map();
|
|
11
|
+
const canaryChains = new Map();
|
|
12
|
+
/**
|
|
13
|
+
* Compute a stable fingerprint for a profile. Two registrations with the
|
|
14
|
+
* same effective profile must produce the same fingerprint.
|
|
15
|
+
*/
|
|
16
|
+
export function profileFingerprint(profile) {
|
|
17
|
+
const canonical = JSON.stringify({
|
|
18
|
+
fsReadOnly: [...profile.fsReadOnly].sort(),
|
|
19
|
+
fsWritable: [...profile.fsWritable].sort(),
|
|
20
|
+
cpuMs: profile.cpuMs ?? null,
|
|
21
|
+
memMB: profile.memMB ?? null,
|
|
22
|
+
pidMax: profile.pidMax ?? null,
|
|
23
|
+
egressAllowlist: [...profile.egressAllowlist].sort()
|
|
24
|
+
});
|
|
25
|
+
return createHash("sha256").update(canonical).digest("hex").slice(0, 32);
|
|
26
|
+
}
|
|
27
|
+
export function generateHandle(prefix = "sp") {
|
|
28
|
+
return `${prefix}_${randomBytes(12).toString("hex")}`;
|
|
29
|
+
}
|
|
30
|
+
export function registerSubprocess(serverId, binary, args, trustTier, profile) {
|
|
31
|
+
const subprocessHandle = generateHandle();
|
|
32
|
+
const now = new Date().toISOString();
|
|
33
|
+
const record = {
|
|
34
|
+
subprocessHandle,
|
|
35
|
+
serverId,
|
|
36
|
+
binary,
|
|
37
|
+
args: [...args],
|
|
38
|
+
trustTier,
|
|
39
|
+
profile,
|
|
40
|
+
profileFingerprint: profileFingerprint(profile),
|
|
41
|
+
egressMode: "default-deny",
|
|
42
|
+
egressModeSetAt: now,
|
|
43
|
+
blockedEgressAttempts: 0,
|
|
44
|
+
registeredAt: now
|
|
45
|
+
};
|
|
46
|
+
subprocesses.set(subprocessHandle, record);
|
|
47
|
+
return record;
|
|
48
|
+
}
|
|
49
|
+
export function getSubprocess(handle) {
|
|
50
|
+
return subprocesses.get(handle);
|
|
51
|
+
}
|
|
52
|
+
export function listSubprocesses() {
|
|
53
|
+
return Array.from(subprocesses.values());
|
|
54
|
+
}
|
|
55
|
+
export function updateEgressPolicy(handle, allowlist, mode) {
|
|
56
|
+
const record = subprocesses.get(handle);
|
|
57
|
+
if (!record)
|
|
58
|
+
return undefined;
|
|
59
|
+
record.profile = {
|
|
60
|
+
...record.profile,
|
|
61
|
+
egressAllowlist: [...allowlist]
|
|
62
|
+
};
|
|
63
|
+
record.profileFingerprint = profileFingerprint(record.profile);
|
|
64
|
+
// Only bump egressModeSetAt when the mode actually transitions. Pure
|
|
65
|
+
// allowlist edits without mode-change keep the original timestamp so the
|
|
66
|
+
// staleness WARN measures actual mode-dwell time, not edit churn.
|
|
67
|
+
if (record.egressMode !== mode) {
|
|
68
|
+
record.egressMode = mode;
|
|
69
|
+
record.egressModeSetAt = new Date().toISOString();
|
|
70
|
+
}
|
|
71
|
+
return record;
|
|
72
|
+
}
|
|
73
|
+
export function bumpBlockedEgress(handle, by = 1) {
|
|
74
|
+
const record = subprocesses.get(handle);
|
|
75
|
+
if (!record)
|
|
76
|
+
return;
|
|
77
|
+
record.blockedEgressAttempts += by;
|
|
78
|
+
}
|
|
79
|
+
export function clearRegistry() {
|
|
80
|
+
subprocesses.clear();
|
|
81
|
+
canaryChains.clear();
|
|
82
|
+
}
|
|
83
|
+
/* canary chains */
|
|
84
|
+
export function generateCanaryToken() {
|
|
85
|
+
// 32-byte high-entropy hex; prefix marks it as a canary so legitimate
|
|
86
|
+
// tools can detect + redact in their logs.
|
|
87
|
+
return `__MCP_CANARY_${randomBytes(32).toString("hex")}__`;
|
|
88
|
+
}
|
|
89
|
+
export function registerCanaryChain(chainId, sourceServerId, downstreamServerIds, canaryPattern, canaryToken) {
|
|
90
|
+
const chain = {
|
|
91
|
+
chainId,
|
|
92
|
+
sourceServerId,
|
|
93
|
+
downstreamServerIds: [...downstreamServerIds],
|
|
94
|
+
canaryToken,
|
|
95
|
+
canaryPattern,
|
|
96
|
+
createdAt: new Date().toISOString()
|
|
97
|
+
};
|
|
98
|
+
canaryChains.set(chainId, chain);
|
|
99
|
+
return chain;
|
|
100
|
+
}
|
|
101
|
+
export function getCanaryChain(chainId) {
|
|
102
|
+
return canaryChains.get(chainId);
|
|
103
|
+
}
|
|
104
|
+
//# sourceMappingURL=state.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"state.js","sourceRoot":"","sources":["../src/state.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tool: audit_subprocess
|
|
3
|
+
*
|
|
4
|
+
* Verifies a candidate args set against the registered subprocess profile.
|
|
5
|
+
* Returns approve | block | quarantine + reason.
|
|
6
|
+
*
|
|
7
|
+
* Layer-2 (Pillar 8) interaction: args are normalized via the shared
|
|
8
|
+
* normalize.ts pipeline (NFKC + ZWC + Bidi) before any comparison.
|
|
9
|
+
*
|
|
10
|
+
* Layer-1 (Pillar 1) interaction: when MCP_RCE_GUARD_PILLAR1_FINGERPRINT is
|
|
11
|
+
* set in the environment, the tool reflects that into pillar1Fingerprint
|
|
12
|
+
* for cross-pillar audit traceability. v0.1 trust-on-first-use, v0.2 will
|
|
13
|
+
* verify against a Sigstore key.
|
|
14
|
+
*
|
|
15
|
+
* Anti-Pattern provenance: MCP-SDK-RCE 22.04.2026 (Tool-Args ungesanitized
|
|
16
|
+
* in subprocess command-strings).
|
|
17
|
+
*/
|
|
18
|
+
import { type AuditVerdictType } from "../types.js";
|
|
19
|
+
export interface AuditSubprocessResult {
|
|
20
|
+
verdict: AuditVerdictType;
|
|
21
|
+
reason: string;
|
|
22
|
+
pillar1Fingerprint?: string;
|
|
23
|
+
pillar8Allowlist: "pass" | "fail";
|
|
24
|
+
}
|
|
25
|
+
export declare function auditSubprocessTool(rawArgs: unknown): Promise<AuditSubprocessResult>;
|
|
26
|
+
//# sourceMappingURL=audit.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"audit.d.ts","sourceRoot":"","sources":["../../src/tools/audit.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,EAGL,KAAK,gBAAgB,EACtB,MAAM,aAAa,CAAC;AAKrB,MAAM,WAAW,qBAAqB;IACpC,OAAO,EAAE,gBAAgB,CAAC;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,gBAAgB,EAAE,MAAM,GAAG,MAAM,CAAC;CACnC;AAED,wBAAsB,mBAAmB,CACvC,OAAO,EAAE,OAAO,GACf,OAAO,CAAC,qBAAqB,CAAC,CAgFhC"}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tool: audit_subprocess
|
|
3
|
+
*
|
|
4
|
+
* Verifies a candidate args set against the registered subprocess profile.
|
|
5
|
+
* Returns approve | block | quarantine + reason.
|
|
6
|
+
*
|
|
7
|
+
* Layer-2 (Pillar 8) interaction: args are normalized via the shared
|
|
8
|
+
* normalize.ts pipeline (NFKC + ZWC + Bidi) before any comparison.
|
|
9
|
+
*
|
|
10
|
+
* Layer-1 (Pillar 1) interaction: when MCP_RCE_GUARD_PILLAR1_FINGERPRINT is
|
|
11
|
+
* set in the environment, the tool reflects that into pillar1Fingerprint
|
|
12
|
+
* for cross-pillar audit traceability. v0.1 trust-on-first-use, v0.2 will
|
|
13
|
+
* verify against a Sigstore key.
|
|
14
|
+
*
|
|
15
|
+
* Anti-Pattern provenance: MCP-SDK-RCE 22.04.2026 (Tool-Args ungesanitized
|
|
16
|
+
* in subprocess command-strings).
|
|
17
|
+
*/
|
|
18
|
+
import { AuditSubprocessArgsSchema } from "../types.js";
|
|
19
|
+
import { getSubprocess } from "../state.js";
|
|
20
|
+
import { normalizeArgs, hasInvisibleCodepoints } from "../normalize.js";
|
|
21
|
+
import { appendAudit } from "../audit/log.js";
|
|
22
|
+
export async function auditSubprocessTool(rawArgs) {
|
|
23
|
+
const args = AuditSubprocessArgsSchema.parse(rawArgs);
|
|
24
|
+
const record = getSubprocess(args.subprocessHandle);
|
|
25
|
+
if (!record) {
|
|
26
|
+
const result = {
|
|
27
|
+
verdict: "block",
|
|
28
|
+
reason: `unknown subprocess handle: ${args.subprocessHandle}`,
|
|
29
|
+
pillar8Allowlist: "fail"
|
|
30
|
+
};
|
|
31
|
+
await appendAudit({
|
|
32
|
+
subprocessHandle: args.subprocessHandle,
|
|
33
|
+
action: "audit_subprocess",
|
|
34
|
+
verdict: "block",
|
|
35
|
+
reason: result.reason
|
|
36
|
+
});
|
|
37
|
+
return result;
|
|
38
|
+
}
|
|
39
|
+
// Pillar 8: normalize + allowlist check.
|
|
40
|
+
const normalizedRequested = normalizeArgs(args.requestedArgs);
|
|
41
|
+
const normalizedRegistered = normalizeArgs(record.args);
|
|
42
|
+
// Detection signal: any invisible codepoint in requested args raises
|
|
43
|
+
// suspicion even if normalization makes the args comparable.
|
|
44
|
+
const suspicious = args.requestedArgs.some(hasInvisibleCodepoints);
|
|
45
|
+
// Allowlist semantics: requested args must be a prefix/exact-match of
|
|
46
|
+
// registered args (the registered args are the *trusted invocation*).
|
|
47
|
+
let pillar8 = "pass";
|
|
48
|
+
let mismatchReason = "";
|
|
49
|
+
if (normalizedRequested.length !== normalizedRegistered.length) {
|
|
50
|
+
pillar8 = "fail";
|
|
51
|
+
mismatchReason = `arg-count mismatch: requested=${normalizedRequested.length}, registered=${normalizedRegistered.length}`;
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
for (let i = 0; i < normalizedRequested.length; i++) {
|
|
55
|
+
if (normalizedRequested[i] !== normalizedRegistered[i]) {
|
|
56
|
+
pillar8 = "fail";
|
|
57
|
+
mismatchReason = `arg[${i}] mismatch: requested=${JSON.stringify(normalizedRequested[i])}, registered=${JSON.stringify(normalizedRegistered[i])}`;
|
|
58
|
+
break;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
let verdict;
|
|
63
|
+
let reason;
|
|
64
|
+
if (pillar8 === "fail") {
|
|
65
|
+
verdict = "block";
|
|
66
|
+
reason = `Pillar-8 allowlist fail: ${mismatchReason}`;
|
|
67
|
+
}
|
|
68
|
+
else if (suspicious) {
|
|
69
|
+
verdict = "quarantine";
|
|
70
|
+
reason = "args contain invisible/bidi codepoints; quarantine for human review";
|
|
71
|
+
}
|
|
72
|
+
else {
|
|
73
|
+
verdict = "approve";
|
|
74
|
+
reason = "Pillar-8 allowlist pass + no suspicious codepoints";
|
|
75
|
+
}
|
|
76
|
+
const pillar1 = process.env["MCP_RCE_GUARD_PILLAR1_FINGERPRINT"];
|
|
77
|
+
const result = {
|
|
78
|
+
verdict,
|
|
79
|
+
reason,
|
|
80
|
+
pillar8Allowlist: pillar8,
|
|
81
|
+
...(pillar1 ? { pillar1Fingerprint: pillar1 } : {})
|
|
82
|
+
};
|
|
83
|
+
await appendAudit({
|
|
84
|
+
subprocessHandle: record.subprocessHandle,
|
|
85
|
+
serverId: record.serverId,
|
|
86
|
+
action: "audit_subprocess",
|
|
87
|
+
verdict,
|
|
88
|
+
reason,
|
|
89
|
+
meta: {
|
|
90
|
+
pillar8: pillar8,
|
|
91
|
+
suspicious,
|
|
92
|
+
profileFingerprint: record.profileFingerprint
|
|
93
|
+
}
|
|
94
|
+
});
|
|
95
|
+
return result;
|
|
96
|
+
}
|
|
97
|
+
//# sourceMappingURL=audit.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"audit.js","sourceRoot":"","sources":["../../src/tools/audit.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tool: get_audit_log
|
|
3
|
+
*
|
|
4
|
+
* Read-only access to the NDJSON audit log. Filterable by subprocessHandle,
|
|
5
|
+
* since-timestamp, and limit.
|
|
6
|
+
*
|
|
7
|
+
* Additionally emits a `staleEgressModeWarnings` array: every currently-
|
|
8
|
+
* registered subprocess that has been left in egressMode="audit-only" longer
|
|
9
|
+
* than `staleEgressModeThresholdDays` (default 7) is surfaced as a WARN.
|
|
10
|
+
* This implements the PLAN.md §Predicted-Impact §inject_egress_policy
|
|
11
|
+
* at-risk-regression-mitigation ("Reviewer-Pass im Pillar-9-Audit-Tool
|
|
12
|
+
* flaggt 'Mode: audit-only seit >7d' als WARN") so operators do not
|
|
13
|
+
* silently sit on a soft policy.
|
|
14
|
+
*
|
|
15
|
+
* Log rotation lives in audit/log.ts (auto-rotate on 100MB threshold,
|
|
16
|
+
* 10 backups, `mcp-rce-guard cleanup` CLI for manual purge).
|
|
17
|
+
*/
|
|
18
|
+
import { type AuditLogEntry, type StaleEgressModeWarning } from "../types.js";
|
|
19
|
+
export interface GetAuditLogResult {
|
|
20
|
+
entries: AuditLogEntry[];
|
|
21
|
+
staleEgressModeWarnings: StaleEgressModeWarning[];
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Build the staleness-WARN list. A subprocess is stale if:
|
|
25
|
+
* - egressMode === "audit-only", AND
|
|
26
|
+
* - now() - egressModeSetAt > thresholdDays
|
|
27
|
+
*
|
|
28
|
+
* Threshold 0 disables the check (returns empty array). Subprocesses with
|
|
29
|
+
* an unparseable egressModeSetAt are skipped silently — better to under-warn
|
|
30
|
+
* than to false-positive on a malformed timestamp.
|
|
31
|
+
*/
|
|
32
|
+
export declare function computeStaleEgressModeWarnings(thresholdDays: number, now?: Date): StaleEgressModeWarning[];
|
|
33
|
+
export declare function getAuditLogTool(rawArgs: unknown): Promise<GetAuditLogResult>;
|
|
34
|
+
//# sourceMappingURL=getAuditLog.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"getAuditLog.d.ts","sourceRoot":"","sources":["../../src/tools/getAuditLog.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,EAGL,KAAK,aAAa,EAClB,KAAK,sBAAsB,EAC5B,MAAM,aAAa,CAAC;AAIrB,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,aAAa,EAAE,CAAC;IACzB,uBAAuB,EAAE,sBAAsB,EAAE,CAAC;CACnD;AAID;;;;;;;;GAQG;AACH,wBAAgB,8BAA8B,CAC5C,aAAa,EAAE,MAAM,EACrB,GAAG,GAAE,IAAiB,GACrB,sBAAsB,EAAE,CAmB1B;AAED,wBAAsB,eAAe,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAiBlF"}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tool: get_audit_log
|
|
3
|
+
*
|
|
4
|
+
* Read-only access to the NDJSON audit log. Filterable by subprocessHandle,
|
|
5
|
+
* since-timestamp, and limit.
|
|
6
|
+
*
|
|
7
|
+
* Additionally emits a `staleEgressModeWarnings` array: every currently-
|
|
8
|
+
* registered subprocess that has been left in egressMode="audit-only" longer
|
|
9
|
+
* than `staleEgressModeThresholdDays` (default 7) is surfaced as a WARN.
|
|
10
|
+
* This implements the PLAN.md §Predicted-Impact §inject_egress_policy
|
|
11
|
+
* at-risk-regression-mitigation ("Reviewer-Pass im Pillar-9-Audit-Tool
|
|
12
|
+
* flaggt 'Mode: audit-only seit >7d' als WARN") so operators do not
|
|
13
|
+
* silently sit on a soft policy.
|
|
14
|
+
*
|
|
15
|
+
* Log rotation lives in audit/log.ts (auto-rotate on 100MB threshold,
|
|
16
|
+
* 10 backups, `mcp-rce-guard cleanup` CLI for manual purge).
|
|
17
|
+
*/
|
|
18
|
+
import { GetAuditLogArgsSchema } from "../types.js";
|
|
19
|
+
import { readAudit } from "../audit/log.js";
|
|
20
|
+
import { listSubprocesses } from "../state.js";
|
|
21
|
+
const MS_PER_DAY = 24 * 60 * 60 * 1000;
|
|
22
|
+
/**
|
|
23
|
+
* Build the staleness-WARN list. A subprocess is stale if:
|
|
24
|
+
* - egressMode === "audit-only", AND
|
|
25
|
+
* - now() - egressModeSetAt > thresholdDays
|
|
26
|
+
*
|
|
27
|
+
* Threshold 0 disables the check (returns empty array). Subprocesses with
|
|
28
|
+
* an unparseable egressModeSetAt are skipped silently — better to under-warn
|
|
29
|
+
* than to false-positive on a malformed timestamp.
|
|
30
|
+
*/
|
|
31
|
+
export function computeStaleEgressModeWarnings(thresholdDays, now = new Date()) {
|
|
32
|
+
if (thresholdDays <= 0)
|
|
33
|
+
return [];
|
|
34
|
+
const warnings = [];
|
|
35
|
+
for (const sp of listSubprocesses()) {
|
|
36
|
+
if (sp.egressMode !== "audit-only")
|
|
37
|
+
continue;
|
|
38
|
+
const setAt = Date.parse(sp.egressModeSetAt);
|
|
39
|
+
if (!Number.isFinite(setAt))
|
|
40
|
+
continue;
|
|
41
|
+
const ageDays = (now.getTime() - setAt) / MS_PER_DAY;
|
|
42
|
+
if (ageDays > thresholdDays) {
|
|
43
|
+
warnings.push({
|
|
44
|
+
subprocessHandle: sp.subprocessHandle,
|
|
45
|
+
serverId: sp.serverId,
|
|
46
|
+
egressMode: sp.egressMode,
|
|
47
|
+
egressModeSetAt: sp.egressModeSetAt,
|
|
48
|
+
ageDays: Math.round(ageDays * 100) / 100
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return warnings;
|
|
53
|
+
}
|
|
54
|
+
export async function getAuditLogTool(rawArgs) {
|
|
55
|
+
const args = GetAuditLogArgsSchema.parse(rawArgs);
|
|
56
|
+
const filter = { limit: args.limit };
|
|
57
|
+
if (args.subprocessHandle !== undefined)
|
|
58
|
+
filter.subprocessHandle = args.subprocessHandle;
|
|
59
|
+
if (args.since !== undefined)
|
|
60
|
+
filter.since = args.since;
|
|
61
|
+
const entries = await readAudit(filter);
|
|
62
|
+
const staleEgressModeWarnings = computeStaleEgressModeWarnings(args.staleEgressModeThresholdDays);
|
|
63
|
+
return { entries, staleEgressModeWarnings };
|
|
64
|
+
}
|
|
65
|
+
//# sourceMappingURL=getAuditLog.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"getAuditLog.js","sourceRoot":"","sources":["../../src/tools/getAuditLog.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,EACL,qBAAqB,EAItB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAO/C,MAAM,UAAU,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;AAEvC;;;;;;;;GAQG;AACH,MAAM,UAAU,8BAA8B,CAC5C,aAAqB,EACrB,MAAY,IAAI,IAAI,EAAE;IAEtB,IAAI,aAAa,IAAI,CAAC;QAAE,OAAO,EAAE,CAAC;IAClC,MAAM,QAAQ,GAA6B,EAAE,CAAC;IAC9C,KAAK,MAAM,EAAE,IAAI,gBAAgB,EAAE,EAAE,CAAC;QACpC,IAAI,EAAE,CAAC,UAAU,KAAK,YAAY;YAAE,SAAS;QAC7C,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,eAAe,CAAC,CAAC;QAC7C,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC;YAAE,SAAS;QACtC,MAAM,OAAO,GAAG,CAAC,GAAG,CAAC,OAAO,EAAE,GAAG,KAAK,CAAC,GAAG,UAAU,CAAC;QACrD,IAAI,OAAO,GAAG,aAAa,EAAE,CAAC;YAC5B,QAAQ,CAAC,IAAI,CAAC;gBACZ,gBAAgB,EAAE,EAAE,CAAC,gBAAgB;gBACrC,QAAQ,EAAE,EAAE,CAAC,QAAQ;gBACrB,UAAU,EAAE,EAAE,CAAC,UAAU;gBACzB,eAAe,EAAE,EAAE,CAAC,eAAe;gBACnC,OAAO,EAAE,IAAI,CAAC,KAAK,CAAC,OAAO,GAAG,GAAG,CAAC,GAAG,GAAG;aACzC,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IACD,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,OAAgB;IACpD,MAAM,IAAI,GAAoB,qBAAqB,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IAEnE,MAAM,MAAM,GAIR,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,CAAC;IAC1B,IAAI,IAAI,CAAC,gBAAgB,KAAK,SAAS;QAAE,MAAM,CAAC,gBAAgB,GAAG,IAAI,CAAC,gBAAgB,CAAC;IACzF,IAAI,IAAI,CAAC,KAAK,KAAK,SAAS;QAAE,MAAM,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC;IAExD,MAAM,OAAO,GAAG,MAAM,SAAS,CAAC,MAAM,CAAC,CAAC;IACxC,MAAM,uBAAuB,GAAG,8BAA8B,CAC5D,IAAI,CAAC,4BAA4B,CAClC,CAAC;IAEF,OAAO,EAAE,OAAO,EAAE,uBAAuB,EAAE,CAAC;AAC9C,CAAC"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tool: inject_egress_policy
|
|
3
|
+
*
|
|
4
|
+
* Updates a registered subprocess's egress allowlist + mode. Destructive:
|
|
5
|
+
* modifies runtime policy. Returns applied + cumulative blocked-attempts
|
|
6
|
+
* counter (since registration).
|
|
7
|
+
*
|
|
8
|
+
* Anti-Pattern provenance: Nginx-MCP RCE CVSS 9.8 (subprocess kann beliebig
|
|
9
|
+
* egress → exfiltration via curl/wget/raw-socket vom kompromittierten
|
|
10
|
+
* subprocess).
|
|
11
|
+
*
|
|
12
|
+
* Mode trade-off: audit-only erlaubt Operators die Policy zu bauen ohne
|
|
13
|
+
* Production-Traffic zu brechen, aber ist eine "soft policy" — get_audit_log
|
|
14
|
+
* markiert mode prominent damit Operator nicht in audit-only haengen bleiben.
|
|
15
|
+
*/
|
|
16
|
+
export interface InjectEgressPolicyResult {
|
|
17
|
+
applied: boolean;
|
|
18
|
+
blockedAttempts: number;
|
|
19
|
+
}
|
|
20
|
+
export declare function injectEgressPolicyTool(rawArgs: unknown): Promise<InjectEgressPolicyResult>;
|
|
21
|
+
//# sourceMappingURL=injectEgress.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"injectEgress.d.ts","sourceRoot":"","sources":["../../src/tools/injectEgress.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AASH,MAAM,WAAW,wBAAwB;IACvC,OAAO,EAAE,OAAO,CAAC;IACjB,eAAe,EAAE,MAAM,CAAC;CACzB;AAED,wBAAsB,sBAAsB,CAC1C,OAAO,EAAE,OAAO,GACf,OAAO,CAAC,wBAAwB,CAAC,CAgCnC"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tool: inject_egress_policy
|
|
3
|
+
*
|
|
4
|
+
* Updates a registered subprocess's egress allowlist + mode. Destructive:
|
|
5
|
+
* modifies runtime policy. Returns applied + cumulative blocked-attempts
|
|
6
|
+
* counter (since registration).
|
|
7
|
+
*
|
|
8
|
+
* Anti-Pattern provenance: Nginx-MCP RCE CVSS 9.8 (subprocess kann beliebig
|
|
9
|
+
* egress → exfiltration via curl/wget/raw-socket vom kompromittierten
|
|
10
|
+
* subprocess).
|
|
11
|
+
*
|
|
12
|
+
* Mode trade-off: audit-only erlaubt Operators die Policy zu bauen ohne
|
|
13
|
+
* Production-Traffic zu brechen, aber ist eine "soft policy" — get_audit_log
|
|
14
|
+
* markiert mode prominent damit Operator nicht in audit-only haengen bleiben.
|
|
15
|
+
*/
|
|
16
|
+
import { InjectEgressPolicyArgsSchema } from "../types.js";
|
|
17
|
+
import { updateEgressPolicy, getSubprocess } from "../state.js";
|
|
18
|
+
import { appendAudit } from "../audit/log.js";
|
|
19
|
+
export async function injectEgressPolicyTool(rawArgs) {
|
|
20
|
+
const args = InjectEgressPolicyArgsSchema.parse(rawArgs);
|
|
21
|
+
const updated = updateEgressPolicy(args.subprocessHandle, args.allowlist, args.mode);
|
|
22
|
+
if (!updated) {
|
|
23
|
+
await appendAudit({
|
|
24
|
+
subprocessHandle: args.subprocessHandle,
|
|
25
|
+
action: "inject_egress_policy",
|
|
26
|
+
verdict: "block",
|
|
27
|
+
reason: "unknown subprocess handle"
|
|
28
|
+
});
|
|
29
|
+
return { applied: false, blockedAttempts: 0 };
|
|
30
|
+
}
|
|
31
|
+
await appendAudit({
|
|
32
|
+
subprocessHandle: updated.subprocessHandle,
|
|
33
|
+
serverId: updated.serverId,
|
|
34
|
+
action: "inject_egress_policy",
|
|
35
|
+
verdict: "approve",
|
|
36
|
+
meta: {
|
|
37
|
+
mode: updated.egressMode,
|
|
38
|
+
allowlistSize: args.allowlist.length,
|
|
39
|
+
profileFingerprint: updated.profileFingerprint
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
// Re-read counter (could have been bumped by a concurrent egress evaluator).
|
|
43
|
+
const fresh = getSubprocess(updated.subprocessHandle);
|
|
44
|
+
return {
|
|
45
|
+
applied: true,
|
|
46
|
+
blockedAttempts: fresh?.blockedEgressAttempts ?? 0
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
//# sourceMappingURL=injectEgress.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"injectEgress.js","sourceRoot":"","sources":["../../src/tools/injectEgress.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EACL,4BAA4B,EAE7B,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,kBAAkB,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAChE,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAO9C,MAAM,CAAC,KAAK,UAAU,sBAAsB,CAC1C,OAAgB;IAEhB,MAAM,IAAI,GAA2B,4BAA4B,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IAEjF,MAAM,OAAO,GAAG,kBAAkB,CAAC,IAAI,CAAC,gBAAgB,EAAE,IAAI,CAAC,SAAS,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC;IACrF,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,MAAM,WAAW,CAAC;YAChB,gBAAgB,EAAE,IAAI,CAAC,gBAAgB;YACvC,MAAM,EAAE,sBAAsB;YAC9B,OAAO,EAAE,OAAO;YAChB,MAAM,EAAE,2BAA2B;SACpC,CAAC,CAAC;QACH,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,eAAe,EAAE,CAAC,EAAE,CAAC;IAChD,CAAC;IAED,MAAM,WAAW,CAAC;QAChB,gBAAgB,EAAE,OAAO,CAAC,gBAAgB;QAC1C,QAAQ,EAAE,OAAO,CAAC,QAAQ;QAC1B,MAAM,EAAE,sBAAsB;QAC9B,OAAO,EAAE,SAAS;QAClB,IAAI,EAAE;YACJ,IAAI,EAAE,OAAO,CAAC,UAAU;YACxB,aAAa,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM;YACpC,kBAAkB,EAAE,OAAO,CAAC,kBAAkB;SAC/C;KACF,CAAC,CAAC;IAEH,6EAA6E;IAC7E,MAAM,KAAK,GAAG,aAAa,CAAC,OAAO,CAAC,gBAAgB,CAAC,CAAC;IACtD,OAAO;QACL,OAAO,EAAE,IAAI;QACb,eAAe,EAAE,KAAK,EAAE,qBAAqB,IAAI,CAAC;KACnD,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tool: register_subprocess
|
|
3
|
+
*
|
|
4
|
+
* Registers a subprocess spec + isolation profile and returns a handle.
|
|
5
|
+
* Profile defaults are filled in from the trust tier; caller-supplied
|
|
6
|
+
* fields override the tier defaults.
|
|
7
|
+
*
|
|
8
|
+
* Anti-Pattern provenance: Nginx-MCP RCE CVSS 9.8 (subprocess inheritert
|
|
9
|
+
* volle parent-permissions weil keine Profile-Defaults existieren).
|
|
10
|
+
*/
|
|
11
|
+
export interface RegisterSubprocessResult {
|
|
12
|
+
subprocessHandle: string;
|
|
13
|
+
profileFingerprint: string;
|
|
14
|
+
}
|
|
15
|
+
export declare function registerSubprocessTool(rawArgs: unknown): Promise<RegisterSubprocessResult>;
|
|
16
|
+
//# sourceMappingURL=register.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"register.d.ts","sourceRoot":"","sources":["../../src/tools/register.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAWH,MAAM,WAAW,wBAAwB;IACvC,gBAAgB,EAAE,MAAM,CAAC;IACzB,kBAAkB,EAAE,MAAM,CAAC;CAC5B;AAED,wBAAsB,sBAAsB,CAC1C,OAAO,EAAE,OAAO,GACf,OAAO,CAAC,wBAAwB,CAAC,CAqCnC"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tool: register_subprocess
|
|
3
|
+
*
|
|
4
|
+
* Registers a subprocess spec + isolation profile and returns a handle.
|
|
5
|
+
* Profile defaults are filled in from the trust tier; caller-supplied
|
|
6
|
+
* fields override the tier defaults.
|
|
7
|
+
*
|
|
8
|
+
* Anti-Pattern provenance: Nginx-MCP RCE CVSS 9.8 (subprocess inheritert
|
|
9
|
+
* volle parent-permissions weil keine Profile-Defaults existieren).
|
|
10
|
+
*/
|
|
11
|
+
import { RegisterSubprocessArgsSchema } from "../types.js";
|
|
12
|
+
import { resolveProfile } from "../trust-tiers.js";
|
|
13
|
+
import { registerSubprocess } from "../state.js";
|
|
14
|
+
import { appendAudit } from "../audit/log.js";
|
|
15
|
+
import { normalizeArg, normalizeArgs } from "../normalize.js";
|
|
16
|
+
export async function registerSubprocessTool(rawArgs) {
|
|
17
|
+
const args = RegisterSubprocessArgsSchema.parse(rawArgs);
|
|
18
|
+
// Pre-Publish-Audit F6: NFKC + ZWC-strip + Bidi-block on binary + args at
|
|
19
|
+
// registration time so the stored allowlist is already in canonical form.
|
|
20
|
+
// audit_subprocess normalizes the candidate args the same way; storing the
|
|
21
|
+
// canonical form here removes a fullwidth-unicode bypass class where an
|
|
22
|
+
// attacker registers a "binary" that includes invisible codepoints and
|
|
23
|
+
// later spawns a normalized lookalike that compares equal.
|
|
24
|
+
const normalizedBinary = normalizeArg(args.binary);
|
|
25
|
+
const normalizedArgs = normalizeArgs(args.args);
|
|
26
|
+
const profile = resolveProfile(args.trustTier, args.isolationProfile);
|
|
27
|
+
const record = registerSubprocess(args.serverId, normalizedBinary, normalizedArgs, args.trustTier, profile);
|
|
28
|
+
await appendAudit({
|
|
29
|
+
subprocessHandle: record.subprocessHandle,
|
|
30
|
+
serverId: record.serverId,
|
|
31
|
+
action: "register_subprocess",
|
|
32
|
+
verdict: "approve",
|
|
33
|
+
meta: {
|
|
34
|
+
binary: record.binary,
|
|
35
|
+
trustTier: record.trustTier,
|
|
36
|
+
profileFingerprint: record.profileFingerprint
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
return {
|
|
40
|
+
subprocessHandle: record.subprocessHandle,
|
|
41
|
+
profileFingerprint: record.profileFingerprint
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
//# sourceMappingURL=register.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"register.js","sourceRoot":"","sources":["../../src/tools/register.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EACL,4BAA4B,EAE7B,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACnD,OAAO,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AACjD,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAC9C,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAO9D,MAAM,CAAC,KAAK,UAAU,sBAAsB,CAC1C,OAAgB;IAEhB,MAAM,IAAI,GAA2B,4BAA4B,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IAEjF,0EAA0E;IAC1E,0EAA0E;IAC1E,2EAA2E;IAC3E,wEAAwE;IACxE,uEAAuE;IACvE,2DAA2D;IAC3D,MAAM,gBAAgB,GAAG,YAAY,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IACnD,MAAM,cAAc,GAAG,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAEhD,MAAM,OAAO,GAAG,cAAc,CAAC,IAAI,CAAC,SAAS,EAAE,IAAI,CAAC,gBAAgB,CAAC,CAAC;IACtE,MAAM,MAAM,GAAG,kBAAkB,CAC/B,IAAI,CAAC,QAAQ,EACb,gBAAgB,EAChB,cAAc,EACd,IAAI,CAAC,SAAS,EACd,OAAO,CACR,CAAC;IAEF,MAAM,WAAW,CAAC;QAChB,gBAAgB,EAAE,MAAM,CAAC,gBAAgB;QACzC,QAAQ,EAAE,MAAM,CAAC,QAAQ;QACzB,MAAM,EAAE,qBAAqB;QAC7B,OAAO,EAAE,SAAS;QAClB,IAAI,EAAE;YACJ,MAAM,EAAE,MAAM,CAAC,MAAM;YACrB,SAAS,EAAE,MAAM,CAAC,SAAS;YAC3B,kBAAkB,EAAE,MAAM,CAAC,kBAAkB;SAC9C;KACF,CAAC,CAAC;IAEH,OAAO;QACL,gBAAgB,EAAE,MAAM,CAAC,gBAAgB;QACzC,kBAAkB,EAAE,MAAM,CAAC,kBAAkB;KAC9C,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tool: scan_cve_replay
|
|
3
|
+
*
|
|
4
|
+
* Runs the built-in CVE replay fixtures against a target server command.
|
|
5
|
+
* Returns overall pass/fail + per-CVE detail.
|
|
6
|
+
*
|
|
7
|
+
* Anti-Pattern provenance: alle 3 frischen 2026-CVEs (MCP-SDK-RCE
|
|
8
|
+
* 22.04.2026, CVE-2026-27124 FastMCP Confused Deputy, Nginx-MCP RCE
|
|
9
|
+
* CVSS 9.8). Predicates leben in src/cve/replay.ts und sind als
|
|
10
|
+
* behavioral predicates designed (kein exploit payload).
|
|
11
|
+
*/
|
|
12
|
+
import { type ReplayResult } from "../cve/replay.js";
|
|
13
|
+
export declare function scanCveReplayTool(rawArgs: unknown): Promise<ReplayResult>;
|
|
14
|
+
//# sourceMappingURL=scanCve.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scanCve.d.ts","sourceRoot":"","sources":["../../src/tools/scanCve.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAMH,OAAO,EAAa,KAAK,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAGhE,wBAAsB,iBAAiB,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,YAAY,CAAC,CAgB/E"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tool: scan_cve_replay
|
|
3
|
+
*
|
|
4
|
+
* Runs the built-in CVE replay fixtures against a target server command.
|
|
5
|
+
* Returns overall pass/fail + per-CVE detail.
|
|
6
|
+
*
|
|
7
|
+
* Anti-Pattern provenance: alle 3 frischen 2026-CVEs (MCP-SDK-RCE
|
|
8
|
+
* 22.04.2026, CVE-2026-27124 FastMCP Confused Deputy, Nginx-MCP RCE
|
|
9
|
+
* CVSS 9.8). Predicates leben in src/cve/replay.ts und sind als
|
|
10
|
+
* behavioral predicates designed (kein exploit payload).
|
|
11
|
+
*/
|
|
12
|
+
import { ScanCveReplayArgsSchema } from "../types.js";
|
|
13
|
+
import { runReplay } from "../cve/replay.js";
|
|
14
|
+
import { appendAudit } from "../audit/log.js";
|
|
15
|
+
export async function scanCveReplayTool(rawArgs) {
|
|
16
|
+
const args = ScanCveReplayArgsSchema.parse(rawArgs);
|
|
17
|
+
const result = runReplay(args.targetServerCommand, args.cveSet, args.timeoutMs);
|
|
18
|
+
await appendAudit({
|
|
19
|
+
subprocessHandle: "n/a",
|
|
20
|
+
action: "scan_cve_replay",
|
|
21
|
+
verdict: result.overall,
|
|
22
|
+
meta: {
|
|
23
|
+
targetServerCommand: args.targetServerCommand,
|
|
24
|
+
perCve: result.perCve.map((r) => ({ id: r.id, status: r.status }))
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
return result;
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=scanCve.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scanCve.js","sourceRoot":"","sources":["../../src/tools/scanCve.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EACL,uBAAuB,EAExB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,SAAS,EAAqB,MAAM,kBAAkB,CAAC;AAChE,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAE9C,MAAM,CAAC,KAAK,UAAU,iBAAiB,CAAC,OAAgB;IACtD,MAAM,IAAI,GAAsB,uBAAuB,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IAEvE,MAAM,MAAM,GAAG,SAAS,CAAC,IAAI,CAAC,mBAAmB,EAAE,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC;IAEhF,MAAM,WAAW,CAAC;QAChB,gBAAgB,EAAE,KAAK;QACvB,MAAM,EAAE,iBAAiB;QACzB,OAAO,EAAE,MAAM,CAAC,OAAO;QACvB,IAAI,EAAE;YACJ,mBAAmB,EAAE,IAAI,CAAC,mBAAmB;YAC7C,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC;SACnE;KACF,CAAC,CAAC;IAEH,OAAO,MAAM,CAAC;AAChB,CAAC"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tool: track_canary
|
|
3
|
+
*
|
|
4
|
+
* Issues a canary token + registers a chain. v0.1 returns the token + a
|
|
5
|
+
* pre-evaluated leakDetected=false because actual leak detection requires
|
|
6
|
+
* the calling MCP-aware controller to feed downstream-server output back
|
|
7
|
+
* via a corpus parameter (added in v0.2).
|
|
8
|
+
*
|
|
9
|
+
* Pattern provenance: arXiv 2604.27819 (MCPHunt, April 2026) — taint
|
|
10
|
+
* tracking via canary-token injection across MCP server boundaries.
|
|
11
|
+
*
|
|
12
|
+
* Confused-Deputy defense (CVE-2026-27124): if the chain controller pipes
|
|
13
|
+
* downstream output back to detect_leaks, a leak via egress channel will
|
|
14
|
+
* be flagged.
|
|
15
|
+
*/
|
|
16
|
+
import { type LeakLocation } from "../canary/tracker.js";
|
|
17
|
+
export interface TrackCanaryResult {
|
|
18
|
+
canaryToken: string;
|
|
19
|
+
leakDetected: boolean;
|
|
20
|
+
leakLocations: LeakLocation[];
|
|
21
|
+
}
|
|
22
|
+
export declare function trackCanaryTool(rawArgs: unknown): Promise<TrackCanaryResult>;
|
|
23
|
+
//# sourceMappingURL=trackCanary.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"trackCanary.d.ts","sourceRoot":"","sources":["../../src/tools/trackCanary.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAOH,OAAO,EAAc,KAAK,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAGrE,MAAM,WAAW,iBAAiB;IAChC,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,OAAO,CAAC;IACtB,aAAa,EAAE,YAAY,EAAE,CAAC;CAC/B;AAED,wBAAsB,eAAe,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAgClF"}
|