@usetorii/gateway 0.0.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 +116 -0
- package/dist/audit/index.d.ts +5 -0
- package/dist/audit/index.d.ts.map +1 -0
- package/dist/audit/index.js +27 -0
- package/dist/audit/index.js.map +1 -0
- package/dist/core/config/loader.d.ts +8 -0
- package/dist/core/config/loader.d.ts.map +1 -0
- package/dist/core/config/loader.js +61 -0
- package/dist/core/config/loader.js.map +1 -0
- package/dist/core/config/schema.d.ts +541 -0
- package/dist/core/config/schema.d.ts.map +1 -0
- package/dist/core/config/schema.js +90 -0
- package/dist/core/config/schema.js.map +1 -0
- package/dist/core/index.d.ts +4 -0
- package/dist/core/index.d.ts.map +1 -0
- package/dist/core/index.js +3 -0
- package/dist/core/index.js.map +1 -0
- package/dist/core/types.d.ts +45 -0
- package/dist/core/types.d.ts.map +1 -0
- package/dist/core/types.js +2 -0
- package/dist/core/types.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +43 -0
- package/dist/index.js.map +1 -0
- package/dist/mux/index.d.ts +8 -0
- package/dist/mux/index.d.ts.map +1 -0
- package/dist/mux/index.js +120 -0
- package/dist/mux/index.js.map +1 -0
- package/dist/process/manager.d.ts +12 -0
- package/dist/process/manager.d.ts.map +1 -0
- package/dist/process/manager.js +79 -0
- package/dist/process/manager.js.map +1 -0
- package/dist/runners/stdio.d.ts +7 -0
- package/dist/runners/stdio.d.ts.map +1 -0
- package/dist/runners/stdio.js +63 -0
- package/dist/runners/stdio.js.map +1 -0
- package/dist/security/consent.d.ts +5 -0
- package/dist/security/consent.d.ts.map +1 -0
- package/dist/security/consent.js +49 -0
- package/dist/security/consent.js.map +1 -0
- package/dist/security/policy.d.ts +22 -0
- package/dist/security/policy.d.ts.map +1 -0
- package/dist/security/policy.js +114 -0
- package/dist/security/policy.js.map +1 -0
- package/dist/security/scanner.d.ts +39 -0
- package/dist/security/scanner.d.ts.map +1 -0
- package/dist/security/scanner.js +96 -0
- package/dist/security/scanner.js.map +1 -0
- package/package.json +46 -0
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
2
|
+
// Torii — Policy Engine (Layer 4)
|
|
3
|
+
//
|
|
4
|
+
// Evaluates declarative allow/deny rules before tool execution.
|
|
5
|
+
// Policies are fetched from the API at startup and refreshed every 60s.
|
|
6
|
+
// First matching enabled policy wins (ordered by priority desc).
|
|
7
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
8
|
+
// ── In-memory cache ───────────────────────────────────────────────
|
|
9
|
+
let cache = [];
|
|
10
|
+
let refreshTimer = null;
|
|
11
|
+
// ── Pattern matching ──────────────────────────────────────────────
|
|
12
|
+
function matchesPattern(pattern, value) {
|
|
13
|
+
if (pattern === "*")
|
|
14
|
+
return true;
|
|
15
|
+
// Regex: /pattern/
|
|
16
|
+
if (pattern.startsWith("/") && pattern.lastIndexOf("/") > 0) {
|
|
17
|
+
const lastSlash = pattern.lastIndexOf("/");
|
|
18
|
+
const flags = pattern.slice(lastSlash + 1);
|
|
19
|
+
const src = pattern.slice(1, lastSlash);
|
|
20
|
+
try {
|
|
21
|
+
return new RegExp(src, flags || "i").test(value);
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
return false;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
// Glob: contains *
|
|
28
|
+
if (pattern.includes("*")) {
|
|
29
|
+
const escaped = pattern.replace(/[.+?^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*");
|
|
30
|
+
return new RegExp(`^${escaped}$`, "i").test(value);
|
|
31
|
+
}
|
|
32
|
+
// Exact (case-insensitive)
|
|
33
|
+
return pattern.toLowerCase() === value.toLowerCase();
|
|
34
|
+
}
|
|
35
|
+
function getByPath(obj, path) {
|
|
36
|
+
return path.split(".").reduce((acc, key) => {
|
|
37
|
+
if (acc !== null && typeof acc === "object") {
|
|
38
|
+
return acc[key];
|
|
39
|
+
}
|
|
40
|
+
return undefined;
|
|
41
|
+
}, obj);
|
|
42
|
+
}
|
|
43
|
+
function matchesCondition(condition, args) {
|
|
44
|
+
if (args === null || typeof args !== "object")
|
|
45
|
+
return false;
|
|
46
|
+
const raw = getByPath(args, condition.field);
|
|
47
|
+
const actual = String(raw ?? "");
|
|
48
|
+
switch (condition.operator) {
|
|
49
|
+
case "equals": return actual === condition.value;
|
|
50
|
+
case "contains": return actual.includes(condition.value);
|
|
51
|
+
case "startsWith": return actual.startsWith(condition.value);
|
|
52
|
+
case "endsWith": return actual.endsWith(condition.value);
|
|
53
|
+
case "matches": {
|
|
54
|
+
try {
|
|
55
|
+
return new RegExp(condition.value, "i").test(actual);
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
default: return false;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
// ── Evaluation ────────────────────────────────────────────────────
|
|
65
|
+
export function evaluatePolicy(serverId, toolName, args) {
|
|
66
|
+
const active = cache
|
|
67
|
+
.filter((p) => p.enabled)
|
|
68
|
+
.sort((a, b) => b.priority - a.priority);
|
|
69
|
+
for (const policy of active) {
|
|
70
|
+
if (!matchesPattern(policy.serverPattern, serverId))
|
|
71
|
+
continue;
|
|
72
|
+
if (!matchesPattern(policy.toolPattern, toolName))
|
|
73
|
+
continue;
|
|
74
|
+
if (policy.argsCondition && !matchesCondition(policy.argsCondition, args))
|
|
75
|
+
continue;
|
|
76
|
+
return policy;
|
|
77
|
+
}
|
|
78
|
+
return null;
|
|
79
|
+
}
|
|
80
|
+
// ── Fetch & cache ─────────────────────────────────────────────────
|
|
81
|
+
export async function loadPolicies() {
|
|
82
|
+
const apiUrl = process.env.TORII_API_URL;
|
|
83
|
+
const apiKey = process.env.TORII_API_KEY;
|
|
84
|
+
if (!apiUrl || !apiKey)
|
|
85
|
+
return;
|
|
86
|
+
try {
|
|
87
|
+
const res = await fetch(`${apiUrl}/v1/policies`, {
|
|
88
|
+
headers: { "X-Torii-Key": apiKey },
|
|
89
|
+
signal: AbortSignal.timeout(5_000),
|
|
90
|
+
});
|
|
91
|
+
if (!res.ok)
|
|
92
|
+
return;
|
|
93
|
+
cache = (await res.json());
|
|
94
|
+
if (cache.length > 0) {
|
|
95
|
+
process.stderr.write(`[torii/policy] loaded ${cache.length} polic${cache.length === 1 ? "y" : "ies"}\n`);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
catch {
|
|
99
|
+
process.stderr.write("[torii/policy] warn: could not fetch policies — running without them\n");
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
export function startPolicyRefresh(intervalMs = 60_000) {
|
|
103
|
+
if (refreshTimer)
|
|
104
|
+
clearInterval(refreshTimer);
|
|
105
|
+
refreshTimer = setInterval(() => {
|
|
106
|
+
loadPolicies().catch(() => { });
|
|
107
|
+
}, intervalMs);
|
|
108
|
+
// Allow the process to exit even if this interval is active
|
|
109
|
+
refreshTimer.unref?.();
|
|
110
|
+
}
|
|
111
|
+
export function getPolicies() {
|
|
112
|
+
return cache;
|
|
113
|
+
}
|
|
114
|
+
//# sourceMappingURL=policy.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"policy.js","sourceRoot":"","sources":["../../src/security/policy.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tool-response scanner — thin client.
|
|
3
|
+
*
|
|
4
|
+
* Sends the raw text content of every MCP tool result to the Torii backend
|
|
5
|
+
* for analysis. The backend owns ALL policy decisions: which categories
|
|
6
|
+
* count, what severities are blocking, where the thresholds live, how the
|
|
7
|
+
* verdict maps to a threat level. The gateway only relays and enforces.
|
|
8
|
+
*
|
|
9
|
+
* Required env:
|
|
10
|
+
* TORII_API_KEY — your gateway key from the dashboard
|
|
11
|
+
* Optional env:
|
|
12
|
+
* TORII_API_URL — backend URL (default https://api.usetorii.dev)
|
|
13
|
+
*
|
|
14
|
+
* If either env is missing or the backend is unreachable, the scanner is
|
|
15
|
+
* a no-op — the call proceeds without analysis (graceful degradation).
|
|
16
|
+
*/
|
|
17
|
+
export interface ScanFinding {
|
|
18
|
+
id: string;
|
|
19
|
+
category: string;
|
|
20
|
+
severity: "low" | "medium" | "high" | "critical";
|
|
21
|
+
description: string;
|
|
22
|
+
matched_text?: string;
|
|
23
|
+
}
|
|
24
|
+
export interface ScanResult {
|
|
25
|
+
safe: boolean;
|
|
26
|
+
blocked: boolean;
|
|
27
|
+
threat_level: "none" | "low" | "medium" | "high" | "critical";
|
|
28
|
+
findings: ScanFinding[];
|
|
29
|
+
scanned_chars: number;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Sends the tool result to the backend for inspection. Returns whatever the
|
|
33
|
+
* backend decides; no local categorisation, no local thresholds, no local
|
|
34
|
+
* severity mapping. If the backend hasn't shipped the endpoint yet (404),
|
|
35
|
+
* if the network fails, or if the request times out, returns null and the
|
|
36
|
+
* gateway lets the call through.
|
|
37
|
+
*/
|
|
38
|
+
export declare function scanToolResult(serverId: string, toolName: string, result: unknown): Promise<ScanResult | null>;
|
|
39
|
+
//# sourceMappingURL=scanner.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scanner.d.ts","sourceRoot":"","sources":["../../src/security/scanner.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,KAAK,GAAG,QAAQ,GAAG,MAAM,GAAG,UAAU,CAAC;IACjD,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,OAAO,CAAC;IACd,OAAO,EAAE,OAAO,CAAC;IACjB,YAAY,EAAE,MAAM,GAAG,KAAK,GAAG,QAAQ,GAAG,MAAM,GAAG,UAAU,CAAC;IAC9D,QAAQ,EAAE,WAAW,EAAE,CAAC;IACxB,aAAa,EAAE,MAAM,CAAC;CACvB;AAyBD;;;;;;GAMG;AACH,wBAAsB,cAAc,CAClC,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,OAAO,GACd,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC,CAuD5B"}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tool-response scanner — thin client.
|
|
3
|
+
*
|
|
4
|
+
* Sends the raw text content of every MCP tool result to the Torii backend
|
|
5
|
+
* for analysis. The backend owns ALL policy decisions: which categories
|
|
6
|
+
* count, what severities are blocking, where the thresholds live, how the
|
|
7
|
+
* verdict maps to a threat level. The gateway only relays and enforces.
|
|
8
|
+
*
|
|
9
|
+
* Required env:
|
|
10
|
+
* TORII_API_KEY — your gateway key from the dashboard
|
|
11
|
+
* Optional env:
|
|
12
|
+
* TORII_API_URL — backend URL (default https://api.usetorii.dev)
|
|
13
|
+
*
|
|
14
|
+
* If either env is missing or the backend is unreachable, the scanner is
|
|
15
|
+
* a no-op — the call proceeds without analysis (graceful degradation).
|
|
16
|
+
*/
|
|
17
|
+
const DEFAULT_API_URL = "https://api.usetorii.dev";
|
|
18
|
+
const SCAN_TIMEOUT_MS = 5_000;
|
|
19
|
+
const MAX_PAYLOAD_BYTES = 32_768;
|
|
20
|
+
/**
|
|
21
|
+
* Pulls all text content from an MCP tool result. Protocol-level extraction,
|
|
22
|
+
* not policy — kept client-side because the backend shouldn't have to know
|
|
23
|
+
* MCP's content[] shape.
|
|
24
|
+
*/
|
|
25
|
+
function extractText(result) {
|
|
26
|
+
if (typeof result === "string")
|
|
27
|
+
return result;
|
|
28
|
+
if (!result || typeof result !== "object")
|
|
29
|
+
return "";
|
|
30
|
+
const r = result;
|
|
31
|
+
if (Array.isArray(r.content)) {
|
|
32
|
+
const text = r.content
|
|
33
|
+
.filter((c) => c.type === "text" && typeof c.text === "string")
|
|
34
|
+
.map((c) => c.text)
|
|
35
|
+
.join("\n");
|
|
36
|
+
if (text)
|
|
37
|
+
return text;
|
|
38
|
+
}
|
|
39
|
+
return JSON.stringify(result).slice(0, MAX_PAYLOAD_BYTES);
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Sends the tool result to the backend for inspection. Returns whatever the
|
|
43
|
+
* backend decides; no local categorisation, no local thresholds, no local
|
|
44
|
+
* severity mapping. If the backend hasn't shipped the endpoint yet (404),
|
|
45
|
+
* if the network fails, or if the request times out, returns null and the
|
|
46
|
+
* gateway lets the call through.
|
|
47
|
+
*/
|
|
48
|
+
export async function scanToolResult(serverId, toolName, result) {
|
|
49
|
+
const apiKey = process.env.TORII_API_KEY;
|
|
50
|
+
if (!apiKey)
|
|
51
|
+
return null;
|
|
52
|
+
const text = extractText(result);
|
|
53
|
+
if (!text.trim())
|
|
54
|
+
return null;
|
|
55
|
+
const baseUrl = (process.env.TORII_API_URL ?? DEFAULT_API_URL).replace(/\/$/, "");
|
|
56
|
+
try {
|
|
57
|
+
const res = await fetch(`${baseUrl}/v1/scan/response`, {
|
|
58
|
+
method: "POST",
|
|
59
|
+
headers: {
|
|
60
|
+
"Content-Type": "application/json",
|
|
61
|
+
"X-Torii-Key": apiKey,
|
|
62
|
+
},
|
|
63
|
+
body: JSON.stringify({ serverId, toolName, text }),
|
|
64
|
+
signal: AbortSignal.timeout(SCAN_TIMEOUT_MS),
|
|
65
|
+
});
|
|
66
|
+
if (res.status === 404) {
|
|
67
|
+
// Endpoint not deployed yet (feature is "Coming soon" on the roadmap).
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
70
|
+
if (!res.ok) {
|
|
71
|
+
process.stderr.write(`[torii/security] backend returned ${res.status} for ${serverId}__${toolName}; skipping\n`);
|
|
72
|
+
return null;
|
|
73
|
+
}
|
|
74
|
+
const data = (await res.json());
|
|
75
|
+
// Trust the backend's verdict shape; only fill safe defaults if a field
|
|
76
|
+
// is missing.
|
|
77
|
+
return {
|
|
78
|
+
safe: data.safe ?? !(data.blocked ?? false),
|
|
79
|
+
blocked: data.blocked ?? false,
|
|
80
|
+
threat_level: data.threat_level ?? "none",
|
|
81
|
+
findings: data.findings ?? [],
|
|
82
|
+
scanned_chars: text.length,
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
catch (err) {
|
|
86
|
+
const name = err.name;
|
|
87
|
+
if (name === "TimeoutError" || name === "AbortError") {
|
|
88
|
+
process.stderr.write(`[torii/security] scan timed out after ${SCAN_TIMEOUT_MS}ms; skipping\n`);
|
|
89
|
+
}
|
|
90
|
+
else {
|
|
91
|
+
process.stderr.write(`[torii/security] backend unreachable (${err.message}); skipping scan\n`);
|
|
92
|
+
}
|
|
93
|
+
return null;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
//# sourceMappingURL=scanner.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scanner.js","sourceRoot":"","sources":["../../src/security/scanner.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@usetorii/gateway",
|
|
3
|
+
"version": "0.0.0",
|
|
4
|
+
"description": "The secure gateway for AI tools. Sits between AI assistants (Claude, Cursor) and MCP servers, providing static analysis, threat modelling, policy enforcement, consent, and audit.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"torii": "./dist/index.js"
|
|
8
|
+
},
|
|
9
|
+
"main": "./dist/index.js",
|
|
10
|
+
"exports": {
|
|
11
|
+
".": "./dist/index.js"
|
|
12
|
+
},
|
|
13
|
+
"files": [
|
|
14
|
+
"dist",
|
|
15
|
+
"README.md",
|
|
16
|
+
"LICENSE"
|
|
17
|
+
],
|
|
18
|
+
"engines": {
|
|
19
|
+
"node": ">=18"
|
|
20
|
+
},
|
|
21
|
+
"scripts": {
|
|
22
|
+
"build": "tsc",
|
|
23
|
+
"dev": "tsc --watch",
|
|
24
|
+
"start": "node dist/index.js",
|
|
25
|
+
"typecheck": "tsc --noEmit",
|
|
26
|
+
"clean": "rm -rf dist"
|
|
27
|
+
},
|
|
28
|
+
"dependencies": {
|
|
29
|
+
"@modelcontextprotocol/sdk": "^1.10.0",
|
|
30
|
+
"yaml": "^2.4.5",
|
|
31
|
+
"zod": "^3.23.8"
|
|
32
|
+
},
|
|
33
|
+
"devDependencies": {
|
|
34
|
+
"@types/node": "^20.14.0",
|
|
35
|
+
"typescript": "^5.5.0"
|
|
36
|
+
},
|
|
37
|
+
"homepage": "https://usetorii.dev",
|
|
38
|
+
"repository": {
|
|
39
|
+
"type": "git",
|
|
40
|
+
"url": "https://github.com/scumfrog/Torii-gateway.git"
|
|
41
|
+
},
|
|
42
|
+
"license": "MIT",
|
|
43
|
+
"publishConfig": {
|
|
44
|
+
"access": "public"
|
|
45
|
+
}
|
|
46
|
+
}
|