mcp-trellis 0.2.1 → 2.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/README.md +70 -620
- package/dist/adapters/node-request.d.ts +53 -0
- package/dist/adapters/node-request.d.ts.map +1 -0
- package/dist/adapters/node-request.js +165 -0
- package/dist/adapters/node-request.js.map +1 -0
- package/dist/adapters/node.d.ts +16 -24
- package/dist/adapters/node.d.ts.map +1 -1
- package/dist/adapters/node.js +69 -88
- package/dist/adapters/node.js.map +1 -1
- package/dist/advanced.d.ts +18 -0
- package/dist/advanced.d.ts.map +1 -0
- package/dist/advanced.js +18 -0
- package/dist/advanced.js.map +1 -0
- package/dist/app-options.d.ts +132 -0
- package/dist/app-options.d.ts.map +1 -0
- package/dist/app-options.js +2 -0
- package/dist/app-options.js.map +1 -0
- package/dist/app.d.ts +2 -66
- package/dist/app.d.ts.map +1 -1
- package/dist/app.js +150 -57
- package/dist/app.js.map +1 -1
- package/dist/audit.d.ts +5 -5
- package/dist/audit.d.ts.map +1 -1
- package/dist/audit.js +19 -8
- package/dist/audit.js.map +1 -1
- package/dist/auth/bearer.d.ts +4 -1
- package/dist/auth/bearer.d.ts.map +1 -1
- package/dist/auth/bearer.js +25 -5
- package/dist/auth/bearer.js.map +1 -1
- package/dist/clients.d.ts.map +1 -1
- package/dist/clients.js +3 -6
- package/dist/clients.js.map +1 -1
- package/dist/deprecated.d.ts +42 -0
- package/dist/deprecated.d.ts.map +1 -0
- package/dist/deprecated.js +35 -0
- package/dist/deprecated.js.map +1 -0
- package/dist/http/body.d.ts +69 -0
- package/dist/http/body.d.ts.map +1 -0
- package/dist/http/body.js +165 -0
- package/dist/http/body.js.map +1 -0
- package/dist/http/http.d.ts +42 -0
- package/dist/http/http.d.ts.map +1 -0
- package/dist/http/http.js +100 -0
- package/dist/http/http.js.map +1 -0
- package/dist/http/origins.d.ts +37 -0
- package/dist/http/origins.d.ts.map +1 -0
- package/dist/http/origins.js +87 -0
- package/dist/http/origins.js.map +1 -0
- package/dist/mcp/dispatch.d.ts +7 -0
- package/dist/mcp/dispatch.d.ts.map +1 -0
- package/dist/mcp/dispatch.js +99 -0
- package/dist/mcp/dispatch.js.map +1 -0
- package/dist/{jsonrpc.d.ts → mcp/jsonrpc.d.ts} +10 -0
- package/dist/mcp/jsonrpc.d.ts.map +1 -0
- package/dist/mcp/jsonrpc.js +88 -0
- package/dist/mcp/jsonrpc.js.map +1 -0
- package/dist/mcp/methods.d.ts +133 -0
- package/dist/mcp/methods.d.ts.map +1 -0
- package/dist/mcp/methods.js +84 -0
- package/dist/mcp/methods.js.map +1 -0
- package/dist/mcp/protocol.d.ts +16 -0
- package/dist/mcp/protocol.d.ts.map +1 -0
- package/dist/mcp/protocol.js +22 -0
- package/dist/mcp/protocol.js.map +1 -0
- package/dist/mcp/registry.d.ts +58 -0
- package/dist/mcp/registry.d.ts.map +1 -0
- package/dist/mcp/registry.js +81 -0
- package/dist/mcp/registry.js.map +1 -0
- package/dist/mcp/rpc-handlers.d.ts +11 -0
- package/dist/mcp/rpc-handlers.d.ts.map +1 -0
- package/dist/mcp/rpc-handlers.js +133 -0
- package/dist/mcp/rpc-handlers.js.map +1 -0
- package/dist/{tools.d.ts → mcp/tools.d.ts} +30 -2
- package/dist/mcp/tools.d.ts.map +1 -0
- package/dist/mcp/tools.js +166 -0
- package/dist/mcp/tools.js.map +1 -0
- package/dist/{validate.d.ts → mcp/validate.d.ts} +24 -12
- package/dist/mcp/validate.d.ts.map +1 -0
- package/dist/mcp/validate.js +186 -0
- package/dist/mcp/validate.js.map +1 -0
- package/dist/oauth/audit.d.ts +20 -0
- package/dist/oauth/audit.d.ts.map +1 -0
- package/dist/oauth/audit.js +29 -0
- package/dist/oauth/audit.js.map +1 -0
- package/dist/oauth/cimd/cimd-parse.d.ts +4 -0
- package/dist/oauth/cimd/cimd-parse.d.ts.map +1 -0
- package/dist/oauth/cimd/cimd-parse.js +70 -0
- package/dist/oauth/cimd/cimd-parse.js.map +1 -0
- package/dist/oauth/cimd/cimd-ssrf.d.ts +8 -0
- package/dist/oauth/cimd/cimd-ssrf.d.ts.map +1 -0
- package/dist/oauth/cimd/cimd-ssrf.js +193 -0
- package/dist/oauth/cimd/cimd-ssrf.js.map +1 -0
- package/dist/oauth/cimd/cimd.d.ts +36 -0
- package/dist/oauth/cimd/cimd.d.ts.map +1 -0
- package/dist/oauth/cimd/cimd.js +132 -0
- package/dist/oauth/cimd/cimd.js.map +1 -0
- package/dist/oauth/cimd/types.d.ts +12 -0
- package/dist/oauth/cimd/types.d.ts.map +1 -0
- package/dist/oauth/cimd/types.js +3 -0
- package/dist/oauth/cimd/types.js.map +1 -0
- package/dist/oauth/config.d.ts +51 -0
- package/dist/oauth/config.d.ts.map +1 -0
- package/dist/oauth/config.js +108 -0
- package/dist/oauth/config.js.map +1 -0
- package/dist/oauth/constants.d.ts +15 -0
- package/dist/oauth/constants.d.ts.map +1 -1
- package/dist/oauth/constants.js +15 -0
- package/dist/oauth/constants.js.map +1 -1
- package/dist/oauth/{base64url.d.ts → crypto/base64url.d.ts} +1 -2
- package/dist/oauth/crypto/base64url.d.ts.map +1 -0
- package/dist/oauth/{base64url.js → crypto/base64url.js} +1 -5
- package/dist/oauth/crypto/base64url.js.map +1 -0
- package/dist/oauth/crypto/clientauth.d.ts +13 -0
- package/dist/oauth/crypto/clientauth.d.ts.map +1 -0
- package/dist/oauth/crypto/clientauth.js +195 -0
- package/dist/oauth/crypto/clientauth.js.map +1 -0
- package/dist/oauth/crypto/codes.d.ts +65 -0
- package/dist/oauth/crypto/codes.d.ts.map +1 -0
- package/dist/oauth/crypto/codes.js +121 -0
- package/dist/oauth/crypto/codes.js.map +1 -0
- package/dist/oauth/crypto/hkdf.d.ts +13 -0
- package/dist/oauth/crypto/hkdf.d.ts.map +1 -0
- package/dist/oauth/crypto/hkdf.js +30 -0
- package/dist/oauth/crypto/hkdf.js.map +1 -0
- package/dist/oauth/crypto/keycache.d.ts +19 -0
- package/dist/oauth/crypto/keycache.d.ts.map +1 -0
- package/dist/oauth/crypto/keycache.js +44 -0
- package/dist/oauth/crypto/keycache.js.map +1 -0
- package/dist/oauth/crypto/pkce.d.ts.map +1 -0
- package/dist/oauth/{pkce.js → crypto/pkce.js} +1 -1
- package/dist/oauth/crypto/pkce.js.map +1 -0
- package/dist/oauth/crypto/sealed.d.ts +26 -0
- package/dist/oauth/crypto/sealed.d.ts.map +1 -0
- package/dist/oauth/crypto/sealed.js +65 -0
- package/dist/oauth/crypto/sealed.js.map +1 -0
- package/dist/oauth/crypto/secrethash.d.ts +58 -0
- package/dist/oauth/crypto/secrethash.d.ts.map +1 -0
- package/dist/oauth/crypto/secrethash.js +82 -0
- package/dist/oauth/crypto/secrethash.js.map +1 -0
- package/dist/oauth/deprecated.d.ts +52 -0
- package/dist/oauth/deprecated.d.ts.map +1 -0
- package/dist/oauth/deprecated.js +49 -0
- package/dist/oauth/deprecated.js.map +1 -0
- package/dist/oauth/endpoints/authorize.d.ts +3 -0
- package/dist/oauth/endpoints/authorize.d.ts.map +1 -0
- package/dist/oauth/endpoints/authorize.js +209 -0
- package/dist/oauth/endpoints/authorize.js.map +1 -0
- package/dist/oauth/endpoints/callback.d.ts +20 -0
- package/dist/oauth/endpoints/callback.d.ts.map +1 -0
- package/dist/oauth/endpoints/callback.js +31 -0
- package/dist/oauth/endpoints/callback.js.map +1 -0
- package/dist/oauth/endpoints/consent-page.d.ts +5 -0
- package/dist/oauth/endpoints/consent-page.d.ts.map +1 -0
- package/dist/oauth/endpoints/consent-page.js +63 -0
- package/dist/oauth/endpoints/consent-page.js.map +1 -0
- package/dist/oauth/endpoints/consent.d.ts +29 -0
- package/dist/oauth/endpoints/consent.d.ts.map +1 -0
- package/dist/oauth/endpoints/consent.js +98 -0
- package/dist/oauth/endpoints/consent.js.map +1 -0
- package/dist/oauth/{metadata.d.ts → endpoints/metadata.d.ts} +14 -8
- package/dist/oauth/endpoints/metadata.d.ts.map +1 -0
- package/dist/oauth/{metadata.js → endpoints/metadata.js} +10 -4
- package/dist/oauth/endpoints/metadata.js.map +1 -0
- package/dist/oauth/endpoints/register.d.ts +10 -0
- package/dist/oauth/endpoints/register.d.ts.map +1 -0
- package/dist/oauth/endpoints/register.js +96 -0
- package/dist/oauth/endpoints/register.js.map +1 -0
- package/dist/oauth/{revoke.d.ts → endpoints/revoke.d.ts} +1 -1
- package/dist/oauth/endpoints/revoke.d.ts.map +1 -0
- package/dist/oauth/{revoke.js → endpoints/revoke.js} +11 -9
- package/dist/oauth/endpoints/revoke.js.map +1 -0
- package/dist/oauth/endpoints/token.d.ts +3 -0
- package/dist/oauth/endpoints/token.d.ts.map +1 -0
- package/dist/oauth/{token.js → endpoints/token.js} +33 -47
- package/dist/oauth/endpoints/token.js.map +1 -0
- package/dist/oauth/{wellknown.d.ts → endpoints/wellknown.d.ts} +1 -1
- package/dist/oauth/endpoints/wellknown.d.ts.map +1 -0
- package/dist/oauth/{wellknown.js → endpoints/wellknown.js} +7 -7
- package/dist/oauth/endpoints/wellknown.js.map +1 -0
- package/dist/oauth/{redirect.d.ts → policy/redirect.d.ts} +6 -1
- package/dist/oauth/policy/redirect.d.ts.map +1 -0
- package/dist/oauth/policy/redirect.js +39 -0
- package/dist/oauth/policy/redirect.js.map +1 -0
- package/dist/oauth/policy/reqbody.d.ts +18 -0
- package/dist/oauth/policy/reqbody.d.ts.map +1 -0
- package/dist/oauth/policy/reqbody.js +68 -0
- package/dist/oauth/policy/reqbody.js.map +1 -0
- package/dist/oauth/policy/resource.d.ts +37 -0
- package/dist/oauth/policy/resource.d.ts.map +1 -0
- package/dist/oauth/policy/resource.js +74 -0
- package/dist/oauth/policy/resource.js.map +1 -0
- package/dist/oauth/policy/scope.d.ts +20 -0
- package/dist/oauth/policy/scope.d.ts.map +1 -0
- package/dist/oauth/policy/scope.js +35 -0
- package/dist/oauth/policy/scope.js.map +1 -0
- package/dist/oauth/router.d.ts +12 -12
- package/dist/oauth/router.d.ts.map +1 -1
- package/dist/oauth/router.js +105 -87
- package/dist/oauth/router.js.map +1 -1
- package/dist/oauth/types.d.ts +177 -26
- package/dist/oauth/types.d.ts.map +1 -1
- package/dist/oauth/types.js +1 -27
- package/dist/oauth/types.js.map +1 -1
- package/dist/server.d.ts +14 -13
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +10 -9
- package/dist/server.js.map +1 -1
- package/dist/util/defined.d.ts +11 -0
- package/dist/util/defined.d.ts.map +1 -0
- package/dist/util/defined.js +15 -0
- package/dist/util/defined.js.map +1 -0
- package/dist/util/json.d.ts +3 -0
- package/dist/util/json.d.ts.map +1 -0
- package/dist/util/json.js +3 -0
- package/dist/util/json.js.map +1 -0
- package/docs/ROADMAP.md +283 -0
- package/docs/compatibility.md +84 -0
- package/docs/diagrams/mcp-trellis-architecture.html +305 -0
- package/docs/guide.md +441 -0
- package/docs/reference.md +210 -0
- package/docs/security.md +112 -0
- package/docs/troubleshooting.md +19 -0
- package/package.json +24 -8
- package/dist/adapters/origins.d.ts +0 -19
- package/dist/adapters/origins.d.ts.map +0 -1
- package/dist/adapters/origins.js +0 -55
- package/dist/adapters/origins.js.map +0 -1
- package/dist/dispatch.d.ts +0 -7
- package/dist/dispatch.d.ts.map +0 -1
- package/dist/dispatch.js +0 -79
- package/dist/dispatch.js.map +0 -1
- package/dist/http.d.ts +0 -17
- package/dist/http.d.ts.map +0 -1
- package/dist/http.js +0 -38
- package/dist/http.js.map +0 -1
- package/dist/jsonrpc.d.ts.map +0 -1
- package/dist/jsonrpc.js +0 -17
- package/dist/jsonrpc.js.map +0 -1
- package/dist/methods.d.ts +0 -61
- package/dist/methods.d.ts.map +0 -1
- package/dist/methods.js +0 -114
- package/dist/methods.js.map +0 -1
- package/dist/oauth/authorize.d.ts +0 -3
- package/dist/oauth/authorize.d.ts.map +0 -1
- package/dist/oauth/authorize.js +0 -109
- package/dist/oauth/authorize.js.map +0 -1
- package/dist/oauth/base64url.d.ts.map +0 -1
- package/dist/oauth/base64url.js.map +0 -1
- package/dist/oauth/clientauth.d.ts +0 -19
- package/dist/oauth/clientauth.d.ts.map +0 -1
- package/dist/oauth/clientauth.js +0 -76
- package/dist/oauth/clientauth.js.map +0 -1
- package/dist/oauth/codes.d.ts +0 -30
- package/dist/oauth/codes.d.ts.map +0 -1
- package/dist/oauth/codes.js +0 -93
- package/dist/oauth/codes.js.map +0 -1
- package/dist/oauth/metadata.d.ts.map +0 -1
- package/dist/oauth/metadata.js.map +0 -1
- package/dist/oauth/pkce.d.ts.map +0 -1
- package/dist/oauth/pkce.js.map +0 -1
- package/dist/oauth/redirect.d.ts.map +0 -1
- package/dist/oauth/redirect.js +0 -26
- package/dist/oauth/redirect.js.map +0 -1
- package/dist/oauth/resource.d.ts +0 -7
- package/dist/oauth/resource.d.ts.map +0 -1
- package/dist/oauth/resource.js +0 -44
- package/dist/oauth/resource.js.map +0 -1
- package/dist/oauth/revoke.d.ts.map +0 -1
- package/dist/oauth/revoke.js.map +0 -1
- package/dist/oauth/scope.d.ts +0 -11
- package/dist/oauth/scope.d.ts.map +0 -1
- package/dist/oauth/scope.js +0 -24
- package/dist/oauth/scope.js.map +0 -1
- package/dist/oauth/token.d.ts +0 -3
- package/dist/oauth/token.d.ts.map +0 -1
- package/dist/oauth/token.js.map +0 -1
- package/dist/oauth/wellknown.d.ts.map +0 -1
- package/dist/oauth/wellknown.js.map +0 -1
- package/dist/protocol.d.ts +0 -4
- package/dist/protocol.d.ts.map +0 -1
- package/dist/protocol.js +0 -14
- package/dist/protocol.js.map +0 -1
- package/dist/registry.d.ts +0 -37
- package/dist/registry.d.ts.map +0 -1
- package/dist/registry.js +0 -69
- package/dist/registry.js.map +0 -1
- package/dist/tools.d.ts.map +0 -1
- package/dist/tools.js +0 -97
- package/dist/tools.js.map +0 -1
- package/dist/validate.d.ts.map +0 -1
- package/dist/validate.js +0 -130
- package/dist/validate.js.map +0 -1
- /package/dist/oauth/{pkce.d.ts → crypto/pkce.d.ts} +0 -0
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
export const JSON_SCHEMA_TYPES = {
|
|
2
|
+
string: "string",
|
|
3
|
+
number: "number",
|
|
4
|
+
integer: "integer",
|
|
5
|
+
boolean: "boolean",
|
|
6
|
+
object: "object",
|
|
7
|
+
array: "array",
|
|
8
|
+
null: "null",
|
|
9
|
+
};
|
|
10
|
+
/** `schema.type` as an array either way — a single type is a union of one. */
|
|
11
|
+
const declaredTypesOf = (schema) => schema.type ? (Array.isArray(schema.type) ? schema.type : [schema.type]) : [];
|
|
12
|
+
/**
|
|
13
|
+
* Direct subschemas the walkers below descend into (`properties.*`, `items`),
|
|
14
|
+
* each with the path it is reported under.
|
|
15
|
+
*/
|
|
16
|
+
const childSchemas = (schema, path) => [
|
|
17
|
+
...Object.entries(schema.properties ?? {}).map(([key, child]) => [
|
|
18
|
+
child,
|
|
19
|
+
`${path}.properties.${key}`,
|
|
20
|
+
]),
|
|
21
|
+
...(schema.items ? [[schema.items, `${path}.items`]] : []),
|
|
22
|
+
];
|
|
23
|
+
/** Keywords `validateAgainstSchema` actually evaluates. */
|
|
24
|
+
export const SUPPORTED_SCHEMA_KEYWORDS = new Set([
|
|
25
|
+
"type",
|
|
26
|
+
"enum",
|
|
27
|
+
"required",
|
|
28
|
+
"properties",
|
|
29
|
+
"items",
|
|
30
|
+
"minimum",
|
|
31
|
+
"maximum",
|
|
32
|
+
"additionalProperties",
|
|
33
|
+
]);
|
|
34
|
+
/** Supported only in these value shapes; any other value is reported as unsupported. */
|
|
35
|
+
const SUPPORTED_KEYWORD_VALUES = {
|
|
36
|
+
additionalProperties: (value) => typeof value === "boolean",
|
|
37
|
+
};
|
|
38
|
+
const isSupportedKeyword = (key, value) => SUPPORTED_SCHEMA_KEYWORDS.has(key) && (SUPPORTED_KEYWORD_VALUES[key]?.(value) ?? true);
|
|
39
|
+
/**
|
|
40
|
+
* Allowed on inputSchema but never evaluated (tools/list / generator metadata).
|
|
41
|
+
* Semantically-enforcing keywords (`pattern`, `oneOf`, …) are not ignored —
|
|
42
|
+
* with `validateArgs: true` they throw at construction.
|
|
43
|
+
*/
|
|
44
|
+
export const IGNORED_SCHEMA_KEYWORDS = new Set([
|
|
45
|
+
"description",
|
|
46
|
+
"title",
|
|
47
|
+
"$schema",
|
|
48
|
+
"$id",
|
|
49
|
+
"$comment",
|
|
50
|
+
"default",
|
|
51
|
+
"examples",
|
|
52
|
+
"deprecated",
|
|
53
|
+
"readOnly",
|
|
54
|
+
"writeOnly",
|
|
55
|
+
"format",
|
|
56
|
+
]);
|
|
57
|
+
/**
|
|
58
|
+
* Paths of keywords outside the evaluated subset, e.g. `args.properties.code.pattern`.
|
|
59
|
+
* Walks nested `properties` / `items`.
|
|
60
|
+
*/
|
|
61
|
+
export const unsupportedKeywords = (schema, path = "args") => {
|
|
62
|
+
const local = Object.entries(schema).flatMap(([key, value]) => isSupportedKeyword(key, value) || IGNORED_SCHEMA_KEYWORDS.has(key) ? [] : [`${path}.${key}`]);
|
|
63
|
+
return [
|
|
64
|
+
...local,
|
|
65
|
+
...childSchemas(schema, path).flatMap(([child, childPath]) => unsupportedKeywords(child, childPath)),
|
|
66
|
+
];
|
|
67
|
+
};
|
|
68
|
+
/**
|
|
69
|
+
* Paths where `properties`/`required` implies an object shape but `type`
|
|
70
|
+
* doesn't say so — absent, or a type/union without `"object"`. Such a
|
|
71
|
+
* schema would otherwise accept any input, `{}` included. Under
|
|
72
|
+
* `validateArgs: true` this throws at construction; `validateNode`'s
|
|
73
|
+
* dispatch on the value's runtime type is the backstop for schemas that
|
|
74
|
+
* reach validation unchecked.
|
|
75
|
+
*/
|
|
76
|
+
export const missingObjectType = (schema, path = "args") => {
|
|
77
|
+
const impliesObject = schema.properties !== undefined || schema.required !== undefined;
|
|
78
|
+
const declaresObject = declaredTypesOf(schema).includes(JSON_SCHEMA_TYPES.object);
|
|
79
|
+
return [
|
|
80
|
+
...(impliesObject && !declaresObject ? [path] : []),
|
|
81
|
+
...childSchemas(schema, path).flatMap(([child, childPath]) => missingObjectType(child, childPath)),
|
|
82
|
+
];
|
|
83
|
+
};
|
|
84
|
+
const runtimeTypeOf = (value) => {
|
|
85
|
+
if (value === null)
|
|
86
|
+
return JSON_SCHEMA_TYPES.null;
|
|
87
|
+
if (Array.isArray(value))
|
|
88
|
+
return JSON_SCHEMA_TYPES.array;
|
|
89
|
+
return typeof value;
|
|
90
|
+
};
|
|
91
|
+
const TYPE_MATCHERS = {
|
|
92
|
+
[JSON_SCHEMA_TYPES.integer]: (actual, value) => actual === JSON_SCHEMA_TYPES.number && Number.isInteger(value),
|
|
93
|
+
};
|
|
94
|
+
const matchesDeclaredType = (actual, value, declared) => TYPE_MATCHERS[declared]?.(actual, value) ?? actual === declared;
|
|
95
|
+
const typeMismatch = (value, schema, path) => {
|
|
96
|
+
const declared = declaredTypesOf(schema);
|
|
97
|
+
if (declared.length === 0)
|
|
98
|
+
return [];
|
|
99
|
+
const actual = runtimeTypeOf(value);
|
|
100
|
+
const matches = declared.some((type) => matchesDeclaredType(actual, value, type));
|
|
101
|
+
return matches ? [] : [`${path}: expected ${declared.join(" | ")}, got ${actual}`];
|
|
102
|
+
};
|
|
103
|
+
const enumErrors = (value, schema, path) => {
|
|
104
|
+
if (!schema.enum || schema.enum.includes(value))
|
|
105
|
+
return [];
|
|
106
|
+
return [`${path}: value not in enum`];
|
|
107
|
+
};
|
|
108
|
+
const RANGE_BOUNDS = [
|
|
109
|
+
{
|
|
110
|
+
key: "minimum",
|
|
111
|
+
fails: (value, bound) => value < bound,
|
|
112
|
+
message: (path, bound) => `${path}: below minimum ${bound}`,
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
key: "maximum",
|
|
116
|
+
fails: (value, bound) => value > bound,
|
|
117
|
+
message: (path, bound) => `${path}: above maximum ${bound}`,
|
|
118
|
+
},
|
|
119
|
+
];
|
|
120
|
+
const rangeErrors = (value, schema, path) => {
|
|
121
|
+
if (typeof value !== "number")
|
|
122
|
+
return [];
|
|
123
|
+
return RANGE_BOUNDS.flatMap(({ key, fails, message }) => {
|
|
124
|
+
const bound = schema[key];
|
|
125
|
+
if (bound === undefined || !fails(value, bound))
|
|
126
|
+
return [];
|
|
127
|
+
return [message(path, bound)];
|
|
128
|
+
});
|
|
129
|
+
};
|
|
130
|
+
const objectErrors = (value, schema, path) => {
|
|
131
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
132
|
+
return [];
|
|
133
|
+
const record = value;
|
|
134
|
+
// `Object.hasOwn`, not `key in record` — `in` also matches inherited
|
|
135
|
+
// Object.prototype members (`toString`, `constructor`, `hasOwnProperty`,
|
|
136
|
+
// …), so `required: ["toString"]` would pass against `{}` and a
|
|
137
|
+
// `properties: { constructor: {...} }` entry would validate against the
|
|
138
|
+
// prototype's own `constructor`, neither of which is an actual argument
|
|
139
|
+
// the caller supplied.
|
|
140
|
+
const requiredErrors = (schema.required ?? [])
|
|
141
|
+
.filter((key) => !Object.hasOwn(record, key))
|
|
142
|
+
.map((key) => `${path}.${key}: required`);
|
|
143
|
+
const propertyErrors = Object.entries(schema.properties ?? {})
|
|
144
|
+
.filter(([key]) => Object.hasOwn(record, key))
|
|
145
|
+
.flatMap(([key, propSchema]) => validateNode(record[key], propSchema, `${path}.${key}`));
|
|
146
|
+
const extraErrors = schema.additionalProperties === false
|
|
147
|
+
? Object.keys(record)
|
|
148
|
+
.filter((key) => !Object.hasOwn(schema.properties ?? {}, key))
|
|
149
|
+
.map((key) => `${path}.${key}: not allowed`)
|
|
150
|
+
: [];
|
|
151
|
+
return [...requiredErrors, ...propertyErrors, ...extraErrors];
|
|
152
|
+
};
|
|
153
|
+
const arrayErrors = (value, schema, path) => {
|
|
154
|
+
const items = schema.items;
|
|
155
|
+
if (!Array.isArray(value) || !items)
|
|
156
|
+
return [];
|
|
157
|
+
return value.flatMap((entry, index) => validateNode(entry, items, `${path}[${index}]`));
|
|
158
|
+
};
|
|
159
|
+
const TYPE_CHECKERS = {
|
|
160
|
+
[JSON_SCHEMA_TYPES.object]: objectErrors,
|
|
161
|
+
[JSON_SCHEMA_TYPES.array]: arrayErrors,
|
|
162
|
+
[JSON_SCHEMA_TYPES.number]: rangeErrors,
|
|
163
|
+
[JSON_SCHEMA_TYPES.integer]: rangeErrors,
|
|
164
|
+
};
|
|
165
|
+
const validateNode = (value, schema, path) => {
|
|
166
|
+
const typeErrors = typeMismatch(value, schema, path);
|
|
167
|
+
if (typeErrors.length > 0)
|
|
168
|
+
return typeErrors;
|
|
169
|
+
// Dispatch on the value's own runtime shape, not the declared type
|
|
170
|
+
// string(s). By this point `typeMismatch` has already confirmed `value`
|
|
171
|
+
// is consistent with whatever was declared (or nothing was declared at
|
|
172
|
+
// all) — so this is always safe, handles a union type correctly with no
|
|
173
|
+
// extra cases, and is what makes `objectErrors` run for a type-less
|
|
174
|
+
// schema whose `properties`/`required` clearly mean "object" even
|
|
175
|
+
// though `type` doesn't say so (the construction-time
|
|
176
|
+
// `missingObjectType` check is the primary guard; this is the runtime
|
|
177
|
+
// backstop for a schema that reaches validation unchecked).
|
|
178
|
+
const actual = runtimeTypeOf(value);
|
|
179
|
+
return [
|
|
180
|
+
...enumErrors(value, schema, path),
|
|
181
|
+
...(TYPE_CHECKERS[actual]?.(value, schema, path) ?? []),
|
|
182
|
+
];
|
|
183
|
+
};
|
|
184
|
+
/** Minimal JSON Schema subset used by MCP tool inputSchema. Empty = valid. */
|
|
185
|
+
export const validateAgainstSchema = (value, schema) => validateNode(value, schema, "args");
|
|
186
|
+
//# sourceMappingURL=validate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validate.js","sourceRoot":"","sources":["../../src/mcp/validate.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Invoke `ports.audit`, swallowing any failure and racing it against
|
|
3
|
+
* `auditTimeoutMs` — the OAuth-side twin of `methods.ts`'s `safeAudit`.
|
|
4
|
+
* A hanging sink must not stall `/token` or `/authorize` error responses.
|
|
5
|
+
*/
|
|
6
|
+
import type { OAuthAuditEntry, OAuthRouterOptions } from "./types.js";
|
|
7
|
+
export declare const safeOAuthAudit: (options: OAuthRouterOptions, entry: OAuthAuditEntry) => Promise<void>;
|
|
8
|
+
/**
|
|
9
|
+
* Fires `legacy_code_secret_used` when a value was recovered with a
|
|
10
|
+
* rotation-array entry other than the primary (`keyIndex > 0`) — the signal
|
|
11
|
+
* every codeSecret rotation site (`/authorize`, `/consent`, `/token`,
|
|
12
|
+
* `firstClientAuthError`) uses to tell an operator which old keys are still
|
|
13
|
+
* live. A no-op for the common case (`keyIndex === 0`, or none at all).
|
|
14
|
+
*/
|
|
15
|
+
export declare const auditLegacyKeyUsage: (options: OAuthRouterOptions, usage: {
|
|
16
|
+
clientId: string;
|
|
17
|
+
keyIndex: number | null | undefined;
|
|
18
|
+
source: string;
|
|
19
|
+
}) => Promise<void>;
|
|
20
|
+
//# sourceMappingURL=audit.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"audit.d.ts","sourceRoot":"","sources":["../../src/oauth/audit.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,KAAK,EAAE,eAAe,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAEtE,eAAO,MAAM,cAAc,GACzB,SAAS,kBAAkB,EAC3B,OAAO,eAAe,KACrB,OAAO,CAAC,IAAI,CAKd,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,mBAAmB,GAC9B,SAAS,kBAAkB,EAC3B,OAAO;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,KAC/E,OAAO,CAAC,IAAI,CAOd,CAAC"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Invoke `ports.audit`, swallowing any failure and racing it against
|
|
3
|
+
* `auditTimeoutMs` — the OAuth-side twin of `methods.ts`'s `safeAudit`.
|
|
4
|
+
* A hanging sink must not stall `/token` or `/authorize` error responses.
|
|
5
|
+
*/
|
|
6
|
+
import { DEFAULT_AUDIT_TIMEOUT_MS, runAuditBounded } from "../http/http.js";
|
|
7
|
+
export const safeOAuthAudit = async (options, entry) => {
|
|
8
|
+
const audit = options.ports.audit;
|
|
9
|
+
if (!audit)
|
|
10
|
+
return;
|
|
11
|
+
await runAuditBounded(() => audit(entry), options.auditTimeoutMs ?? DEFAULT_AUDIT_TIMEOUT_MS);
|
|
12
|
+
};
|
|
13
|
+
/**
|
|
14
|
+
* Fires `legacy_code_secret_used` when a value was recovered with a
|
|
15
|
+
* rotation-array entry other than the primary (`keyIndex > 0`) — the signal
|
|
16
|
+
* every codeSecret rotation site (`/authorize`, `/consent`, `/token`,
|
|
17
|
+
* `firstClientAuthError`) uses to tell an operator which old keys are still
|
|
18
|
+
* live. A no-op for the common case (`keyIndex === 0`, or none at all).
|
|
19
|
+
*/
|
|
20
|
+
export const auditLegacyKeyUsage = async (options, usage) => {
|
|
21
|
+
if (!usage.keyIndex)
|
|
22
|
+
return;
|
|
23
|
+
await safeOAuthAudit(options, {
|
|
24
|
+
event: "legacy_code_secret_used",
|
|
25
|
+
clientId: usage.clientId,
|
|
26
|
+
reason: `${usage.source} with codeSecret[${usage.keyIndex}]`,
|
|
27
|
+
});
|
|
28
|
+
};
|
|
29
|
+
//# sourceMappingURL=audit.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"audit.js","sourceRoot":"","sources":["../../src/oauth/audit.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,wBAAwB,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAG5E,MAAM,CAAC,MAAM,cAAc,GAAG,KAAK,EACjC,OAA2B,EAC3B,KAAsB,EACP,EAAE;IACjB,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC;IAClC,IAAI,CAAC,KAAK;QAAE,OAAO;IAEnB,MAAM,eAAe,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC,cAAc,IAAI,wBAAwB,CAAC,CAAC;AAChG,CAAC,CAAC;AAEF;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG,KAAK,EACtC,OAA2B,EAC3B,KAAgF,EACjE,EAAE;IACjB,IAAI,CAAC,KAAK,CAAC,QAAQ;QAAE,OAAO;IAC5B,MAAM,cAAc,CAAC,OAAO,EAAE;QAC5B,KAAK,EAAE,yBAAyB;QAChC,QAAQ,EAAE,KAAK,CAAC,QAAQ;QACxB,MAAM,EAAE,GAAG,KAAK,CAAC,MAAM,oBAAoB,KAAK,CAAC,QAAQ,GAAG;KAC7D,CAAC,CAAC;AACL,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cimd-parse.d.ts","sourceRoot":"","sources":["../../../src/oauth/cimd/cimd-parse.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAK/C,eAAO,MAAM,gBAAgB,GAAI,QAAQ,MAAM,GAAG,IAAI,KAAG,MAGxD,CAAC;AAoCF,eAAO,MAAM,aAAa,GAAI,UAAU,MAAM,EAAE,MAAM,MAAM,KAAG,YAsB9D,CAAC"}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { isJsonObject } from "../../util/json.js";
|
|
2
|
+
import { MAX_CLIENT_REDIRECT_URIS } from "../constants.js";
|
|
3
|
+
import { isLoopbackHostname } from "./cimd-ssrf.js";
|
|
4
|
+
const DEFAULT_CACHE_SECONDS = 300;
|
|
5
|
+
const MAX_CACHE_SECONDS = 3600;
|
|
6
|
+
export const parseCacheMaxAge = (header) => {
|
|
7
|
+
const maxAge = header ? /(?:^|,)\s*max-age=(\d+)/i.exec(header)?.[1] : undefined;
|
|
8
|
+
return maxAge === undefined ? DEFAULT_CACHE_SECONDS : Math.min(Number(maxAge), MAX_CACHE_SECONDS);
|
|
9
|
+
};
|
|
10
|
+
const assertHttpRedirectUri = (uri) => {
|
|
11
|
+
let parsed;
|
|
12
|
+
try {
|
|
13
|
+
parsed = new URL(uri);
|
|
14
|
+
}
|
|
15
|
+
catch {
|
|
16
|
+
throw new Error("CIMD redirect_uris entries must be absolute URLs");
|
|
17
|
+
}
|
|
18
|
+
if (parsed.username || parsed.password || parsed.hash) {
|
|
19
|
+
throw new Error("CIMD redirect_uris must not carry credentials or a fragment");
|
|
20
|
+
}
|
|
21
|
+
if (parsed.protocol === "https:")
|
|
22
|
+
return;
|
|
23
|
+
if (parsed.protocol === "http:") {
|
|
24
|
+
if (!isLoopbackHostname(parsed.hostname)) {
|
|
25
|
+
throw new Error("CIMD http redirect_uris must be loopback (127.0.0.1 / ::1)");
|
|
26
|
+
}
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
throw new Error("CIMD redirect_uris must be http(s)");
|
|
30
|
+
};
|
|
31
|
+
const parseRedirectUris = (rawUris) => {
|
|
32
|
+
if (!Array.isArray(rawUris) || rawUris.length === 0) {
|
|
33
|
+
throw new Error("CIMD document must include a non-empty redirect_uris array");
|
|
34
|
+
}
|
|
35
|
+
if (rawUris.length > MAX_CLIENT_REDIRECT_URIS) {
|
|
36
|
+
throw new Error(`CIMD redirect_uris must have at most ${MAX_CLIENT_REDIRECT_URIS} entries`);
|
|
37
|
+
}
|
|
38
|
+
return rawUris.map((u) => {
|
|
39
|
+
if (typeof u !== "string" || !u)
|
|
40
|
+
throw new Error("CIMD redirect_uris entries must be strings");
|
|
41
|
+
assertHttpRedirectUri(u);
|
|
42
|
+
return u;
|
|
43
|
+
});
|
|
44
|
+
};
|
|
45
|
+
export const parseDocument = (clientId, text) => {
|
|
46
|
+
let parsed;
|
|
47
|
+
try {
|
|
48
|
+
parsed = JSON.parse(text);
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
throw new Error("CIMD document is not JSON");
|
|
52
|
+
}
|
|
53
|
+
if (!isJsonObject(parsed))
|
|
54
|
+
throw new Error("CIMD document must be a JSON object");
|
|
55
|
+
const record = parsed;
|
|
56
|
+
if (record.client_id !== clientId) {
|
|
57
|
+
throw new Error("CIMD document client_id must equal the request URL exactly");
|
|
58
|
+
}
|
|
59
|
+
const authMethod = record.token_endpoint_auth_method;
|
|
60
|
+
if (authMethod !== undefined && authMethod !== "none") {
|
|
61
|
+
throw new Error("CIMD clients must be public (token_endpoint_auth_method none)");
|
|
62
|
+
}
|
|
63
|
+
return {
|
|
64
|
+
client_id: clientId,
|
|
65
|
+
redirect_uris: parseRedirectUris(record.redirect_uris),
|
|
66
|
+
...(typeof record.client_name === "string" ? { client_name: record.client_name } : {}),
|
|
67
|
+
token_endpoint_auth_method: "none",
|
|
68
|
+
};
|
|
69
|
+
};
|
|
70
|
+
//# sourceMappingURL=cimd-parse.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cimd-parse.js","sourceRoot":"","sources":["../../../src/oauth/cimd/cimd-parse.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SSRF guards for CIMD URL fetch: hostname normalization, private/NAT64
|
|
3
|
+
* IP blocks, and DNS lookup before connect.
|
|
4
|
+
*/
|
|
5
|
+
export declare const isLoopbackHostname: (hostname: string) => boolean;
|
|
6
|
+
export declare const defaultLookup: (hostname: string) => Promise<string[]>;
|
|
7
|
+
export declare const assertSafeUrl: (url: URL, lookup: (hostname: string) => Promise<string[]>) => Promise<void>;
|
|
8
|
+
//# sourceMappingURL=cimd-ssrf.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cimd-ssrf.d.ts","sourceRoot":"","sources":["../../../src/oauth/cimd/cimd-ssrf.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAoLH,eAAO,MAAM,kBAAkB,GAAI,UAAU,MAAM,KAAG,OAEL,CAAC;AAElD,eAAO,MAAM,aAAa,GAAU,UAAU,MAAM,KAAG,OAAO,CAAC,MAAM,EAAE,CAQtE,CAAC;AAiBF,eAAO,MAAM,aAAa,GACxB,KAAK,GAAG,EACR,QAAQ,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,EAAE,CAAC,KAC9C,OAAO,CAAC,IAAI,CASd,CAAC"}
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SSRF guards for CIMD URL fetch: hostname normalization, private/NAT64
|
|
3
|
+
* IP blocks, and DNS lookup before connect.
|
|
4
|
+
*/
|
|
5
|
+
/** Strip WHATWG brackets from IPv6 hostnames (`[::1]` → `::1`). */
|
|
6
|
+
const normalizeHostname = (hostname) => {
|
|
7
|
+
const host = hostname.toLowerCase().replace(/\.$/, "");
|
|
8
|
+
return host.startsWith("[") && host.endsWith("]") ? host.slice(1, -1) : host;
|
|
9
|
+
};
|
|
10
|
+
const DOTTED_QUAD = /^\d{1,3}(?:\.\d{1,3}){3}$/;
|
|
11
|
+
/** Digits-and-dots only — covers shorthand (`127.1`) and decimal (`2130706433`). */
|
|
12
|
+
const IPV4_CANDIDATE = /^\d+(?:\.\d+)*$/;
|
|
13
|
+
const IPV4_PRIVATE = [
|
|
14
|
+
/^0\./,
|
|
15
|
+
/^10\./,
|
|
16
|
+
/^127\./,
|
|
17
|
+
/^169\.254\./,
|
|
18
|
+
/^172\.(1[6-9]|2\d|3[0-1])\./,
|
|
19
|
+
/^192\.168\./,
|
|
20
|
+
/^100\.(6[4-9]|[7-9]\d|1[0-1]\d|12[0-7])\./,
|
|
21
|
+
// IETF protocol assignments 192.0.0/24, benchmarking 198.18/15
|
|
22
|
+
/^192\.0\.0\./,
|
|
23
|
+
/^198\.1[89]\./,
|
|
24
|
+
// Multicast 224/4, reserved 240/4 (incl. 255.255.255.255)
|
|
25
|
+
/^(22[4-9]|23\d|24\d|25[0-5])\./,
|
|
26
|
+
];
|
|
27
|
+
const isPrivateV4 = (v4) => IPV4_PRIVATE.some((re) => re.test(v4));
|
|
28
|
+
/** IPv4-mapped (`::ffff:a.b.c.d`) and deprecated IPv4-compatible (`::a.b.c.d`) forms. */
|
|
29
|
+
const expandIpv4Mapped = (ip) => {
|
|
30
|
+
const mapped = /^::(?:ffff:)?(\d{1,3}(?:\.\d{1,3}){3})$/i.exec(ip);
|
|
31
|
+
if (mapped)
|
|
32
|
+
return mapped[1] ?? null;
|
|
33
|
+
const hexMapped = /^::(?:ffff:)?([0-9a-f]{1,4}):([0-9a-f]{1,4})$/i.exec(ip);
|
|
34
|
+
if (!hexMapped)
|
|
35
|
+
return null;
|
|
36
|
+
const hi = Number.parseInt(hexMapped[1] ?? "", 16);
|
|
37
|
+
const lo = Number.parseInt(hexMapped[2] ?? "", 16);
|
|
38
|
+
return `${(hi >> 8) & 255}.${hi & 255}.${(lo >> 8) & 255}.${lo & 255}`;
|
|
39
|
+
};
|
|
40
|
+
const HEXTET = /^[0-9a-f]{1,4}$/i;
|
|
41
|
+
const parseHextet = (part) => HEXTET.test(part) ? Number.parseInt(part, 16) : null;
|
|
42
|
+
const hextetPairToIpv4 = (hi, lo) => {
|
|
43
|
+
const high = parseHextet(hi);
|
|
44
|
+
const low = parseHextet(lo);
|
|
45
|
+
if (high === null || low === null)
|
|
46
|
+
return null;
|
|
47
|
+
return `${(high >> 8) & 255}.${high & 255}.${(low >> 8) & 255}.${low & 255}`;
|
|
48
|
+
};
|
|
49
|
+
const splitHextetParts = (segment) => segment.length > 0 ? segment.split(":") : [];
|
|
50
|
+
const dottedQuadToHextetPair = (quad) => {
|
|
51
|
+
const octets = quad.split(".").map(Number);
|
|
52
|
+
if (octets.length !== 4 || octets.some((n) => !Number.isFinite(n) || n < 0 || n > 255)) {
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
const [a, b, c, d] = octets;
|
|
56
|
+
return [((a << 8) | b).toString(16), ((c << 8) | d).toString(16)];
|
|
57
|
+
};
|
|
58
|
+
const isValidHextetList = (hextets) => hextets.length === 8 && hextets.every((h) => parseHextet(h) !== null);
|
|
59
|
+
/**
|
|
60
|
+
* Expand an IPv6 literal into 8 hextets. Trailing dotted-quad (mapped /
|
|
61
|
+
* compatible forms) consumes the last two slots. Returns null when the
|
|
62
|
+
* string is not a well-formed IPv6 address — callers fail closed.
|
|
63
|
+
*/
|
|
64
|
+
const expandIpv6Hextets = (ip) => {
|
|
65
|
+
if (!ip.includes(":"))
|
|
66
|
+
return null;
|
|
67
|
+
let ipv4Tail = null;
|
|
68
|
+
let core = ip;
|
|
69
|
+
const lastColon = ip.lastIndexOf(":");
|
|
70
|
+
const after = ip.slice(lastColon + 1);
|
|
71
|
+
if (DOTTED_QUAD.test(after)) {
|
|
72
|
+
ipv4Tail = dottedQuadToHextetPair(after);
|
|
73
|
+
if (!ipv4Tail)
|
|
74
|
+
return null;
|
|
75
|
+
core = ip.slice(0, lastColon);
|
|
76
|
+
}
|
|
77
|
+
const slots = 8 - (ipv4Tail ? 2 : 0);
|
|
78
|
+
if (core.includes("::")) {
|
|
79
|
+
const pieces = core.split("::");
|
|
80
|
+
if (pieces.length !== 2)
|
|
81
|
+
return null;
|
|
82
|
+
const headParts = splitHextetParts(pieces[0] ?? "");
|
|
83
|
+
const tailParts = splitHextetParts(pieces[1] ?? "");
|
|
84
|
+
if (headParts.length + tailParts.length > slots)
|
|
85
|
+
return null;
|
|
86
|
+
const missing = slots - headParts.length - tailParts.length;
|
|
87
|
+
const hextets = [
|
|
88
|
+
...headParts,
|
|
89
|
+
...Array.from({ length: missing }, () => "0"),
|
|
90
|
+
...tailParts,
|
|
91
|
+
...(ipv4Tail ?? []),
|
|
92
|
+
];
|
|
93
|
+
return isValidHextetList(hextets) ? hextets : null;
|
|
94
|
+
}
|
|
95
|
+
const parts = splitHextetParts(core);
|
|
96
|
+
if (parts.length !== slots)
|
|
97
|
+
return null;
|
|
98
|
+
const hextets = [...parts, ...(ipv4Tail ?? [])];
|
|
99
|
+
return isValidHextetList(hextets) ? hextets : null;
|
|
100
|
+
};
|
|
101
|
+
// --- block rules (ordered; first match blocks) ---
|
|
102
|
+
/** Non-canonical IPv4 from a sloppy `cimdLookup` — fail closed, not a public hostname. */
|
|
103
|
+
const isNonCanonicalIpv4 = (v4) => IPV4_CANDIDATE.test(v4) && !DOTTED_QUAD.test(v4);
|
|
104
|
+
const isPrivateDottedQuad = (v4) => DOTTED_QUAD.test(v4) && isPrivateV4(v4);
|
|
105
|
+
const IPV6_BLOCKED_EXACT = new Set(["::1", "::"]);
|
|
106
|
+
const IPV6_BLOCKED_PREFIXES = ["fe80:", "::ffff:"];
|
|
107
|
+
/** ULA fc00::/7 and multicast ff00::/8. */
|
|
108
|
+
const IPV6_BLOCKED_PREFIX_RES = [/^f[cd][0-9a-f]{2}:/i, /^ff[0-9a-f]{0,2}:/];
|
|
109
|
+
const isBlockedIpv6Literal = (ip) => IPV6_BLOCKED_EXACT.has(ip) ||
|
|
110
|
+
IPV6_BLOCKED_PREFIXES.some((prefix) => ip.startsWith(prefix)) ||
|
|
111
|
+
IPV6_BLOCKED_PREFIX_RES.some((re) => re.test(ip));
|
|
112
|
+
const leadingFiveZero = (n) => n[0] === 0 && n[1] === 0 && n[2] === 0 && n[3] === 0 && n[4] === 0;
|
|
113
|
+
/** Full-form IPv4-mapped (`0:0:0:0:0:ffff:…`) — same block as `::ffff:` prefix. */
|
|
114
|
+
const isIpv4MappedHextets = (n) => leadingFiveZero(n) && n[5] === 0xffff;
|
|
115
|
+
/** Deprecated IPv4-compatible — private/reserved embeds only (public stays). */
|
|
116
|
+
const isBlockedIpv4Compatible = (n, raw) => {
|
|
117
|
+
if (!leadingFiveZero(n) || n[5] !== 0)
|
|
118
|
+
return false;
|
|
119
|
+
if (n[6] === 0 && (n[7] === 0 || n[7] === 1))
|
|
120
|
+
return true;
|
|
121
|
+
const embedded = hextetPairToIpv4(raw[6] ?? "", raw[7] ?? "");
|
|
122
|
+
return !embedded || isPrivateV4(embedded);
|
|
123
|
+
};
|
|
124
|
+
const HEXTET_BLOCK_RULES = [
|
|
125
|
+
isIpv4MappedHextets,
|
|
126
|
+
isBlockedIpv4Compatible,
|
|
127
|
+
(n) => n[0] === 0x2002, // 6to4 2002::/16
|
|
128
|
+
(n) => n[0] === 0x64 && n[1] === 0xff9b, // NAT64 64:ff9b::/96
|
|
129
|
+
];
|
|
130
|
+
const isBlockedIp = (ip) => {
|
|
131
|
+
const normalized = normalizeHostname(ip);
|
|
132
|
+
const v4 = expandIpv4Mapped(normalized) ?? normalized;
|
|
133
|
+
if (isNonCanonicalIpv4(v4) || isPrivateDottedQuad(v4))
|
|
134
|
+
return true;
|
|
135
|
+
if (!normalized.includes(":"))
|
|
136
|
+
return false;
|
|
137
|
+
if (isBlockedIpv6Literal(normalized))
|
|
138
|
+
return true;
|
|
139
|
+
const raw = expandIpv6Hextets(normalized);
|
|
140
|
+
if (!raw)
|
|
141
|
+
return true; // fail closed on unparseable IPv6
|
|
142
|
+
const n = raw.map((h) => Number.parseInt(h, 16));
|
|
143
|
+
return HEXTET_BLOCK_RULES.some((rule) => rule(n, raw));
|
|
144
|
+
};
|
|
145
|
+
const BLOCKED_HOST_EXACT = new Set(["localhost"]);
|
|
146
|
+
const BLOCKED_HOST_SUFFIXES = [".localhost", ".local"];
|
|
147
|
+
const isBlockedHostname = (hostname) => {
|
|
148
|
+
const host = normalizeHostname(hostname);
|
|
149
|
+
if (BLOCKED_HOST_EXACT.has(host))
|
|
150
|
+
return true;
|
|
151
|
+
if (BLOCKED_HOST_SUFFIXES.some((suffix) => host.endsWith(suffix)))
|
|
152
|
+
return true;
|
|
153
|
+
return isBlockedIp(host);
|
|
154
|
+
};
|
|
155
|
+
const LOOPBACK_HOSTS = new Set(["127.0.0.1", "::1"]);
|
|
156
|
+
export const isLoopbackHostname = (hostname) =>
|
|
157
|
+
// `localhost` deliberately excluded — RFC 8252 §8.3, same as policy/redirect.ts.
|
|
158
|
+
LOOPBACK_HOSTS.has(normalizeHostname(hostname));
|
|
159
|
+
export const defaultLookup = async (hostname) => {
|
|
160
|
+
try {
|
|
161
|
+
const dns = await import("node:dns/promises");
|
|
162
|
+
const results = await dns.lookup(hostname, { all: true, verbatim: true });
|
|
163
|
+
return results.map((r) => r.address);
|
|
164
|
+
}
|
|
165
|
+
catch {
|
|
166
|
+
throw new Error("CIMD DNS lookup unavailable");
|
|
167
|
+
}
|
|
168
|
+
};
|
|
169
|
+
const URL_SAFETY = {
|
|
170
|
+
https: {
|
|
171
|
+
ok: (url) => url.protocol === "https:",
|
|
172
|
+
error: "CIMD client_id must be https",
|
|
173
|
+
},
|
|
174
|
+
noCredentials: {
|
|
175
|
+
ok: (url) => !url.username && !url.password,
|
|
176
|
+
error: "CIMD URL must not carry credentials",
|
|
177
|
+
},
|
|
178
|
+
publicHostname: {
|
|
179
|
+
ok: (url) => !isBlockedHostname(url.hostname),
|
|
180
|
+
error: "CIMD hostname is not publicly routable",
|
|
181
|
+
},
|
|
182
|
+
};
|
|
183
|
+
export const assertSafeUrl = async (url, lookup) => {
|
|
184
|
+
for (const rule of Object.values(URL_SAFETY)) {
|
|
185
|
+
if (!rule.ok(url))
|
|
186
|
+
throw new Error(rule.error);
|
|
187
|
+
}
|
|
188
|
+
const addrs = await lookup(normalizeHostname(url.hostname));
|
|
189
|
+
if (addrs.length === 0 || addrs.some(isBlockedIp)) {
|
|
190
|
+
throw new Error("CIMD resolved to a non-public address");
|
|
191
|
+
}
|
|
192
|
+
};
|
|
193
|
+
//# sourceMappingURL=cimd-ssrf.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cimd-ssrf.js","sourceRoot":"","sources":["../../../src/oauth/cimd/cimd-ssrf.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,mEAAmE;AACnE,MAAM,iBAAiB,GAAG,CAAC,QAAgB,EAAU,EAAE;IACrD,MAAM,IAAI,GAAG,QAAQ,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;IACvD,OAAO,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;AAC/E,CAAC,CAAC;AAEF,MAAM,WAAW,GAAG,2BAA2B,CAAC;AAEhD,oFAAoF;AACpF,MAAM,cAAc,GAAG,iBAAiB,CAAC;AAEzC,MAAM,YAAY,GAAG;IACnB,MAAM;IACN,OAAO;IACP,QAAQ;IACR,aAAa;IACb,6BAA6B;IAC7B,aAAa;IACb,2CAA2C;IAC3C,+DAA+D;IAC/D,cAAc;IACd,eAAe;IACf,0DAA0D;IAC1D,gCAAgC;CACjC,CAAC;AAEF,MAAM,WAAW,GAAG,CAAC,EAAU,EAAW,EAAE,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC;AAEpF,yFAAyF;AACzF,MAAM,gBAAgB,GAAG,CAAC,EAAU,EAAiB,EAAE;IACrD,MAAM,MAAM,GAAG,0CAA0C,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACnE,IAAI,MAAM;QAAE,OAAO,MAAM,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC;IAErC,MAAM,SAAS,GAAG,gDAAgD,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAC5E,IAAI,CAAC,SAAS;QAAE,OAAO,IAAI,CAAC;IAE5B,MAAM,EAAE,GAAG,MAAM,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,EAAE,EAAE,EAAE,CAAC,CAAC;IACnD,MAAM,EAAE,GAAG,MAAM,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,EAAE,EAAE,EAAE,CAAC,CAAC;IACnD,OAAO,GAAG,CAAC,EAAE,IAAI,CAAC,CAAC,GAAG,GAAG,IAAI,EAAE,GAAG,GAAG,IAAI,CAAC,EAAE,IAAI,CAAC,CAAC,GAAG,GAAG,IAAI,EAAE,GAAG,GAAG,EAAE,CAAC;AACzE,CAAC,CAAC;AAEF,MAAM,MAAM,GAAG,kBAAkB,CAAC;AAElC,MAAM,WAAW,GAAG,CAAC,IAAY,EAAiB,EAAE,CAClD,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;AAEvD,MAAM,gBAAgB,GAAG,CAAC,EAAU,EAAE,EAAU,EAAiB,EAAE;IACjE,MAAM,IAAI,GAAG,WAAW,CAAC,EAAE,CAAC,CAAC;IAC7B,MAAM,GAAG,GAAG,WAAW,CAAC,EAAE,CAAC,CAAC;IAC5B,IAAI,IAAI,KAAK,IAAI,IAAI,GAAG,KAAK,IAAI;QAAE,OAAO,IAAI,CAAC;IAC/C,OAAO,GAAG,CAAC,IAAI,IAAI,CAAC,CAAC,GAAG,GAAG,IAAI,IAAI,GAAG,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC,CAAC,GAAG,GAAG,IAAI,GAAG,GAAG,GAAG,EAAE,CAAC;AAC/E,CAAC,CAAC;AAEF,MAAM,gBAAgB,GAAG,CAAC,OAAe,EAAY,EAAE,CACrD,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;AAE/C,MAAM,sBAAsB,GAAG,CAAC,IAAY,EAA2B,EAAE;IACvE,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IAC3C,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,IAAI,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,GAAG,CAAC,EAAE,CAAC;QACvF,OAAO,IAAI,CAAC;IACd,CAAC;IACD,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,GAAG,MAA0C,CAAC;IAChE,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC;AACpE,CAAC,CAAC;AAEF,MAAM,iBAAiB,GAAG,CACxB,OAAiB,EAC4D,EAAE,CAC/E,OAAO,CAAC,MAAM,KAAK,CAAC,IAAI,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,WAAW,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC;AAExE;;;;GAIG;AACH,MAAM,iBAAiB,GAAG,CAAC,EAAU,EAAmB,EAAE;IACxD,IAAI,CAAC,EAAE,CAAC,QAAQ,CAAC,GAAG,CAAC;QAAE,OAAO,IAAI,CAAC;IAEnC,IAAI,QAAQ,GAA4B,IAAI,CAAC;IAC7C,IAAI,IAAI,GAAG,EAAE,CAAC;IACd,MAAM,SAAS,GAAG,EAAE,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC;IACtC,MAAM,KAAK,GAAG,EAAE,CAAC,KAAK,CAAC,SAAS,GAAG,CAAC,CAAC,CAAC;IACtC,IAAI,WAAW,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;QAC5B,QAAQ,GAAG,sBAAsB,CAAC,KAAK,CAAC,CAAC;QACzC,IAAI,CAAC,QAAQ;YAAE,OAAO,IAAI,CAAC;QAC3B,IAAI,GAAG,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,SAAS,CAAC,CAAC;IAChC,CAAC;IAED,MAAM,KAAK,GAAG,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAErC,IAAI,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;QACxB,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAChC,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,IAAI,CAAC;QACrC,MAAM,SAAS,GAAG,gBAAgB,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;QACpD,MAAM,SAAS,GAAG,gBAAgB,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;QACpD,IAAI,SAAS,CAAC,MAAM,GAAG,SAAS,CAAC,MAAM,GAAG,KAAK;YAAE,OAAO,IAAI,CAAC;QAC7D,MAAM,OAAO,GAAG,KAAK,GAAG,SAAS,CAAC,MAAM,GAAG,SAAS,CAAC,MAAM,CAAC;QAC5D,MAAM,OAAO,GAAG;YACd,GAAG,SAAS;YACZ,GAAG,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC;YAC7C,GAAG,SAAS;YACZ,GAAG,CAAC,QAAQ,IAAI,EAAE,CAAC;SACpB,CAAC;QACF,OAAO,iBAAiB,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC;IACrD,CAAC;IAED,MAAM,KAAK,GAAG,gBAAgB,CAAC,IAAI,CAAC,CAAC;IACrC,IAAI,KAAK,CAAC,MAAM,KAAK,KAAK;QAAE,OAAO,IAAI,CAAC;IACxC,MAAM,OAAO,GAAG,CAAC,GAAG,KAAK,EAAE,GAAG,CAAC,QAAQ,IAAI,EAAE,CAAC,CAAC,CAAC;IAChD,OAAO,iBAAiB,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC;AACrD,CAAC,CAAC;AAEF,oDAAoD;AAEpD,0FAA0F;AAC1F,MAAM,kBAAkB,GAAG,CAAC,EAAU,EAAW,EAAE,CACjD,cAAc,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;AAEnD,MAAM,mBAAmB,GAAG,CAAC,EAAU,EAAW,EAAE,CAAC,WAAW,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,WAAW,CAAC,EAAE,CAAC,CAAC;AAE7F,MAAM,kBAAkB,GAAG,IAAI,GAAG,CAAC,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC,CAAC;AAClD,MAAM,qBAAqB,GAAG,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC;AACnD,2CAA2C;AAC3C,MAAM,uBAAuB,GAAG,CAAC,qBAAqB,EAAE,mBAAmB,CAAC,CAAC;AAE7E,MAAM,oBAAoB,GAAG,CAAC,EAAU,EAAW,EAAE,CACnD,kBAAkB,CAAC,GAAG,CAAC,EAAE,CAAC;IAC1B,qBAAqB,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,EAAE,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC;IAC7D,uBAAuB,CAAC,IAAI,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC;AAEpD,MAAM,eAAe,GAAG,CAAC,CAAW,EAAW,EAAE,CAC/C,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;AAErE,mFAAmF;AACnF,MAAM,mBAAmB,GAAG,CAAC,CAAW,EAAW,EAAE,CAAC,eAAe,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,KAAK,MAAM,CAAC;AAE5F,gFAAgF;AAChF,MAAM,uBAAuB,GAAG,CAAC,CAAW,EAAE,GAAa,EAAW,EAAE;IACtE,IAAI,CAAC,eAAe,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC;IACpD,IAAI,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;QAAE,OAAO,IAAI,CAAC;IAC1D,MAAM,QAAQ,GAAG,gBAAgB,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,EAAE,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;IAC9D,OAAO,CAAC,QAAQ,IAAI,WAAW,CAAC,QAAQ,CAAC,CAAC;AAC5C,CAAC,CAAC;AAEF,MAAM,kBAAkB,GAAmD;IACzE,mBAAmB;IACnB,uBAAuB;IACvB,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,MAAM,EAAE,iBAAiB;IACzC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC,KAAK,MAAM,EAAE,qBAAqB;CAC/D,CAAC;AAEF,MAAM,WAAW,GAAG,CAAC,EAAU,EAAW,EAAE;IAC1C,MAAM,UAAU,GAAG,iBAAiB,CAAC,EAAE,CAAC,CAAC;IACzC,MAAM,EAAE,GAAG,gBAAgB,CAAC,UAAU,CAAC,IAAI,UAAU,CAAC;IAEtD,IAAI,kBAAkB,CAAC,EAAE,CAAC,IAAI,mBAAmB,CAAC,EAAE,CAAC;QAAE,OAAO,IAAI,CAAC;IACnE,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,GAAG,CAAC;QAAE,OAAO,KAAK,CAAC;IAC5C,IAAI,oBAAoB,CAAC,UAAU,CAAC;QAAE,OAAO,IAAI,CAAC;IAElD,MAAM,GAAG,GAAG,iBAAiB,CAAC,UAAU,CAAC,CAAC;IAC1C,IAAI,CAAC,GAAG;QAAE,OAAO,IAAI,CAAC,CAAC,kCAAkC;IAEzD,MAAM,CAAC,GAAG,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC;IACjD,OAAO,kBAAkB,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC;AACzD,CAAC,CAAC;AAEF,MAAM,kBAAkB,GAAG,IAAI,GAAG,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC;AAClD,MAAM,qBAAqB,GAAG,CAAC,YAAY,EAAE,QAAQ,CAAC,CAAC;AAEvD,MAAM,iBAAiB,GAAG,CAAC,QAAgB,EAAW,EAAE;IACtD,MAAM,IAAI,GAAG,iBAAiB,CAAC,QAAQ,CAAC,CAAC;IACzC,IAAI,kBAAkB,CAAC,GAAG,CAAC,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC;IAC9C,IAAI,qBAAqB,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;QAAE,OAAO,IAAI,CAAC;IAC/E,OAAO,WAAW,CAAC,IAAI,CAAC,CAAC;AAC3B,CAAC,CAAC;AAEF,MAAM,cAAc,GAAG,IAAI,GAAG,CAAC,CAAC,WAAW,EAAE,KAAK,CAAC,CAAC,CAAC;AAErD,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,QAAgB,EAAW,EAAE;AAC9D,iFAAiF;AACjF,cAAc,CAAC,GAAG,CAAC,iBAAiB,CAAC,QAAQ,CAAC,CAAC,CAAC;AAElD,MAAM,CAAC,MAAM,aAAa,GAAG,KAAK,EAAE,QAAgB,EAAqB,EAAE;IACzE,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,mBAAmB,CAAC,CAAC;QAC9C,MAAM,OAAO,GAAG,MAAM,GAAG,CAAC,MAAM,CAAC,QAAQ,EAAE,EAAE,GAAG,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC;QAC1E,OAAO,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC;IACvC,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,IAAI,KAAK,CAAC,6BAA6B,CAAC,CAAC;IACjD,CAAC;AACH,CAAC,CAAC;AAEF,MAAM,UAAU,GAAiE;IAC/E,KAAK,EAAE;QACL,EAAE,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,QAAQ,KAAK,QAAQ;QACtC,KAAK,EAAE,8BAA8B;KACtC;IACD,aAAa,EAAE;QACb,EAAE,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,QAAQ,IAAI,CAAC,GAAG,CAAC,QAAQ;QAC3C,KAAK,EAAE,qCAAqC;KAC7C;IACD,cAAc,EAAE;QACd,EAAE,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,iBAAiB,CAAC,GAAG,CAAC,QAAQ,CAAC;QAC7C,KAAK,EAAE,wCAAwC;KAChD;CACF,CAAC;AAEF,MAAM,CAAC,MAAM,aAAa,GAAG,KAAK,EAChC,GAAQ,EACR,MAA+C,EAChC,EAAE;IACjB,KAAK,MAAM,IAAI,IAAI,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,EAAE,CAAC;QAC7C,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,GAAG,CAAC;YAAE,MAAM,IAAI,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACjD,CAAC;IAED,MAAM,KAAK,GAAG,MAAM,MAAM,CAAC,iBAAiB,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC;IAC5D,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,IAAI,KAAK,CAAC,IAAI,CAAC,WAAW,CAAC,EAAE,CAAC;QAClD,MAAM,IAAI,KAAK,CAAC,uCAAuC,CAAC,CAAC;IAC3D,CAAC;AACH,CAAC,CAAC"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Client ID Metadata Documents (CIMD) — HTTPS URL `client_id` metadata.
|
|
3
|
+
* Off by default (`cimd: true`). SSRF guards live in `cimd-ssrf.ts`.
|
|
4
|
+
*
|
|
5
|
+
* ponytail: DNS checked before/after fetch; TCP connect is not pinned (Web
|
|
6
|
+
* fetch). Upgrade: custom dispatcher dialing the resolved IP + Host header.
|
|
7
|
+
*/
|
|
8
|
+
import type { CimdCache, CimdDocument } from "./types.js";
|
|
9
|
+
export type { CimdCache, CimdDocument } from "./types.js";
|
|
10
|
+
export type CimdFetchOptions = {
|
|
11
|
+
/** Override fetch (tests). Defaults to globalThis.fetch. */
|
|
12
|
+
fetch?: typeof fetch;
|
|
13
|
+
cache?: CimdCache;
|
|
14
|
+
/**
|
|
15
|
+
* DNS lookup for rebinding checks. When omitted, the library tries
|
|
16
|
+
* `node:dns/promises.lookup` if available; if that is also unavailable
|
|
17
|
+
* (e.g. some Workers), resolution is refused rather than fetched blind.
|
|
18
|
+
*/
|
|
19
|
+
lookup?: (hostname: string) => Promise<string[]>;
|
|
20
|
+
/** Per-hop timeout covering connect **and** body read. Tests only — default 10 s. */
|
|
21
|
+
timeoutMs?: number;
|
|
22
|
+
};
|
|
23
|
+
/** Build fetch options from router/app CIMD ports. */
|
|
24
|
+
export declare const cimdFetchOpts: (ports: {
|
|
25
|
+
cimdCache?: CimdCache;
|
|
26
|
+
cimdLookup?: (hostname: string) => Promise<string[]>;
|
|
27
|
+
}) => Pick<CimdFetchOptions, "cache" | "lookup">;
|
|
28
|
+
/** True when `client_id` is an HTTPS URL eligible for CIMD resolution. */
|
|
29
|
+
export declare const isCimdClientId: (clientId: string) => boolean;
|
|
30
|
+
/**
|
|
31
|
+
* Fetch and validate a CIMD document for `clientId` (the HTTPS URL itself).
|
|
32
|
+
* Returns null on any validation / network failure — callers map that to
|
|
33
|
+
* `unauthorized_client` without leaking fetch details to the connector.
|
|
34
|
+
*/
|
|
35
|
+
export declare const resolveCimdClient: (clientId: string, options?: CimdFetchOptions) => Promise<CimdDocument | null>;
|
|
36
|
+
//# sourceMappingURL=cimd.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cimd.d.ts","sourceRoot":"","sources":["../../../src/oauth/cimd/cimd.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAKH,OAAO,KAAK,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE1D,YAAY,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAM1D,MAAM,MAAM,gBAAgB,GAAG;IAC7B,4DAA4D;IAC5D,KAAK,CAAC,EAAE,OAAO,KAAK,CAAC;IACrB,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB;;;;OAIG;IACH,MAAM,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IACjD,qFAAqF;IACrF,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,sDAAsD;AACtD,eAAO,MAAM,aAAa,GAAI,OAAO;IACnC,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB,UAAU,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;CACtD,KAAG,IAAI,CAAC,gBAAgB,EAAE,OAAO,GAAG,QAAQ,CAG3C,CAAC;AAEH,0EAA0E;AAC1E,eAAO,MAAM,cAAc,GAAI,UAAU,MAAM,KAAG,OAajD,CAAC;AAwGF;;;;GAIG;AACH,eAAO,MAAM,iBAAiB,GAC5B,UAAU,MAAM,EAChB,UAAS,gBAAqB,KAC7B,OAAO,CAAC,YAAY,GAAG,IAAI,CAkB7B,CAAC"}
|