@adcp/sdk 13.0.0-rc.8 → 13.0.0-rc.9
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/dist/lib/adapters/roster-account-store.d.mts +9 -8
- package/dist/lib/adapters/roster-account-store.d.ts +9 -8
- package/dist/lib/adapters/roster-account-store.d.ts.map +1 -1
- package/dist/lib/adapters/roster-account-store.js +2 -1
- package/dist/lib/adapters/roster-account-store.js.map +1 -1
- package/dist/lib/adapters/roster-account-store.mjs +2 -1
- package/dist/lib/adapters/roster-account-store.mjs.map +1 -1
- package/dist/lib/net/address-guards.d.ts.map +1 -1
- package/dist/lib/net/address-guards.js +6 -0
- package/dist/lib/net/address-guards.js.map +1 -1
- package/dist/lib/net/address-guards.mjs +6 -0
- package/dist/lib/net/address-guards.mjs.map +1 -1
- package/dist/lib/net/ssrf-fetch.d.ts.map +1 -1
- package/dist/lib/net/ssrf-fetch.js +66 -53
- package/dist/lib/net/ssrf-fetch.js.map +1 -1
- package/dist/lib/net/ssrf-fetch.mjs +66 -53
- package/dist/lib/net/ssrf-fetch.mjs.map +1 -1
- package/dist/lib/schemas-data/v2.5/_provenance.json +1 -1
- package/dist/lib/server/account-mode.d.mts +2 -1
- package/dist/lib/server/account-mode.d.ts +2 -1
- package/dist/lib/server/account-mode.d.ts.map +1 -1
- package/dist/lib/server/account-mode.js +1 -0
- package/dist/lib/server/account-mode.js.map +1 -1
- package/dist/lib/server/account-mode.mjs +1 -0
- package/dist/lib/server/account-mode.mjs.map +1 -1
- package/dist/lib/server/create-adcp-server.d.mts +10 -6
- package/dist/lib/server/create-adcp-server.d.ts +10 -6
- package/dist/lib/server/create-adcp-server.d.ts.map +1 -1
- package/dist/lib/server/create-adcp-server.js +2 -1
- package/dist/lib/server/create-adcp-server.js.map +1 -1
- package/dist/lib/server/create-adcp-server.mjs +2 -1
- package/dist/lib/server/create-adcp-server.mjs.map +1 -1
- package/dist/lib/server/decisioning/account.d.mts +16 -11
- package/dist/lib/server/decisioning/account.d.ts +16 -11
- package/dist/lib/server/decisioning/account.d.ts.map +1 -1
- package/dist/lib/server/decisioning/account.js.map +1 -1
- package/dist/lib/server/decisioning/account.mjs.map +1 -1
- package/dist/lib/server/decisioning/pagination.d.mts +2 -0
- package/dist/lib/server/decisioning/pagination.d.ts +2 -0
- package/dist/lib/server/decisioning/pagination.d.ts.map +1 -1
- package/dist/lib/server/decisioning/pagination.js.map +1 -1
- package/dist/lib/server/decisioning/runtime/from-platform.d.mts +20 -14
- package/dist/lib/server/decisioning/runtime/from-platform.d.ts +20 -14
- package/dist/lib/server/decisioning/runtime/from-platform.d.ts.map +1 -1
- package/dist/lib/server/decisioning/runtime/from-platform.js +32 -6
- package/dist/lib/server/decisioning/runtime/from-platform.js.map +1 -1
- package/dist/lib/server/decisioning/runtime/from-platform.mjs +32 -6
- package/dist/lib/server/decisioning/runtime/from-platform.mjs.map +1 -1
- package/dist/lib/server/decisioning/specialisms/sales.d.mts +16 -4
- package/dist/lib/server/decisioning/specialisms/sales.d.ts +16 -4
- package/dist/lib/server/decisioning/specialisms/sales.d.ts.map +1 -1
- package/dist/lib/server/decisioning/specialisms/sales.js.map +1 -1
- package/dist/lib/server/index.d.ts.map +1 -1
- package/dist/lib/server/index.js.map +1 -1
- package/dist/lib/server/index.mjs.map +1 -1
- package/dist/lib/server/legacy/v5/index.d.mts +2 -1
- package/dist/lib/server/legacy/v5/index.d.ts +2 -1
- package/dist/lib/server/legacy/v5/index.d.ts.map +1 -1
- package/dist/lib/server/legacy/v5/index.js.map +1 -1
- package/dist/lib/server/legacy/v5/index.mjs.map +1 -1
- package/dist/lib/testing/compliance/comply.d.mts +2 -0
- package/dist/lib/testing/compliance/comply.d.ts +2 -0
- package/dist/lib/testing/compliance/comply.d.ts.map +1 -1
- package/dist/lib/testing/compliance/comply.js +45 -14
- package/dist/lib/testing/compliance/comply.js.map +1 -1
- package/dist/lib/testing/compliance/comply.mjs +45 -14
- package/dist/lib/testing/compliance/comply.mjs.map +1 -1
- package/dist/lib/testing/compliance/index.d.mts +1 -1
- package/dist/lib/testing/compliance/index.d.ts +1 -1
- package/dist/lib/testing/compliance/index.d.ts.map +1 -1
- package/dist/lib/testing/compliance/index.js.map +1 -1
- package/dist/lib/testing/compliance/index.mjs.map +1 -1
- package/dist/lib/testing/compliance/storyboard-tracks.d.ts.map +1 -1
- package/dist/lib/testing/compliance/storyboard-tracks.js +9 -1
- package/dist/lib/testing/compliance/storyboard-tracks.js.map +1 -1
- package/dist/lib/testing/compliance/storyboard-tracks.mjs +9 -1
- package/dist/lib/testing/compliance/storyboard-tracks.mjs.map +1 -1
- package/dist/lib/testing/compliance/types.d.mts +26 -25
- package/dist/lib/testing/compliance/types.d.ts +26 -25
- package/dist/lib/testing/compliance/types.d.ts.map +1 -1
- package/dist/lib/testing/compliance/types.js.map +1 -1
- package/dist/lib/testing/index.d.mts +2 -2
- package/dist/lib/testing/index.d.ts +2 -2
- package/dist/lib/testing/index.d.ts.map +1 -1
- package/dist/lib/testing/index.js +16 -0
- package/dist/lib/testing/index.js.map +1 -1
- package/dist/lib/testing/index.mjs +16 -0
- package/dist/lib/testing/index.mjs.map +1 -1
- package/dist/lib/testing/scenarios/media-buy.d.ts.map +1 -1
- package/dist/lib/testing/scenarios/media-buy.js +20 -12
- package/dist/lib/testing/scenarios/media-buy.js.map +1 -1
- package/dist/lib/testing/scenarios/media-buy.mjs +20 -12
- package/dist/lib/testing/scenarios/media-buy.mjs.map +1 -1
- package/dist/lib/testing/storyboard/creative-assets.d.mts +23 -0
- package/dist/lib/testing/storyboard/creative-assets.d.ts +23 -0
- package/dist/lib/testing/storyboard/creative-assets.d.ts.map +1 -1
- package/dist/lib/testing/storyboard/creative-assets.js +322 -45
- package/dist/lib/testing/storyboard/creative-assets.js.map +1 -1
- package/dist/lib/testing/storyboard/creative-assets.mjs +321 -45
- package/dist/lib/testing/storyboard/creative-assets.mjs.map +1 -1
- package/dist/lib/testing/storyboard/index.d.mts +4 -1
- package/dist/lib/testing/storyboard/index.d.ts +4 -1
- package/dist/lib/testing/storyboard/index.d.ts.map +1 -1
- package/dist/lib/testing/storyboard/index.js +20 -0
- package/dist/lib/testing/storyboard/index.js.map +1 -1
- package/dist/lib/testing/storyboard/index.mjs +19 -0
- package/dist/lib/testing/storyboard/index.mjs.map +1 -1
- package/dist/lib/testing/storyboard/oauth-metadata-graph/grader.d.mts +21 -0
- package/dist/lib/testing/storyboard/oauth-metadata-graph/grader.d.ts +22 -0
- package/dist/lib/testing/storyboard/oauth-metadata-graph/grader.d.ts.map +1 -0
- package/dist/lib/testing/storyboard/oauth-metadata-graph/grader.js +668 -0
- package/dist/lib/testing/storyboard/oauth-metadata-graph/grader.js.map +1 -0
- package/dist/lib/testing/storyboard/oauth-metadata-graph/grader.mjs +638 -0
- package/dist/lib/testing/storyboard/oauth-metadata-graph/grader.mjs.map +1 -0
- package/dist/lib/testing/storyboard/oauth-metadata-graph/index.d.mts +5 -0
- package/dist/lib/testing/storyboard/oauth-metadata-graph/index.d.ts +6 -0
- package/dist/lib/testing/storyboard/oauth-metadata-graph/index.d.ts.map +1 -0
- package/dist/lib/testing/storyboard/oauth-metadata-graph/index.js +44 -0
- package/dist/lib/testing/storyboard/oauth-metadata-graph/index.js.map +1 -0
- package/dist/lib/testing/storyboard/oauth-metadata-graph/index.mjs +20 -0
- package/dist/lib/testing/storyboard/oauth-metadata-graph/index.mjs.map +1 -0
- package/dist/lib/testing/storyboard/oauth-metadata-graph/types.d.mts +38 -0
- package/dist/lib/testing/storyboard/oauth-metadata-graph/types.d.ts +39 -0
- package/dist/lib/testing/storyboard/oauth-metadata-graph/types.d.ts.map +1 -0
- package/dist/lib/testing/storyboard/oauth-metadata-graph/types.js +17 -0
- package/dist/lib/testing/storyboard/oauth-metadata-graph/types.js.map +1 -0
- package/dist/lib/testing/storyboard/oauth-metadata-graph/types.mjs +1 -0
- package/dist/lib/testing/storyboard/oauth-metadata-graph/types.mjs.map +1 -0
- package/dist/lib/testing/storyboard/oauth-metadata-graph/vector-loader.d.mts +32 -0
- package/dist/lib/testing/storyboard/oauth-metadata-graph/vector-loader.d.ts +33 -0
- package/dist/lib/testing/storyboard/oauth-metadata-graph/vector-loader.d.ts.map +1 -0
- package/dist/lib/testing/storyboard/oauth-metadata-graph/vector-loader.js +105 -0
- package/dist/lib/testing/storyboard/oauth-metadata-graph/vector-loader.js.map +1 -0
- package/dist/lib/testing/storyboard/oauth-metadata-graph/vector-loader.mjs +80 -0
- package/dist/lib/testing/storyboard/oauth-metadata-graph/vector-loader.mjs.map +1 -0
- package/dist/lib/testing/storyboard/probes.d.ts.map +1 -1
- package/dist/lib/testing/storyboard/probes.js +2 -1
- package/dist/lib/testing/storyboard/probes.js.map +1 -1
- package/dist/lib/testing/storyboard/probes.mjs +2 -1
- package/dist/lib/testing/storyboard/probes.mjs.map +1 -1
- package/dist/lib/testing/storyboard/runner.d.ts.map +1 -1
- package/dist/lib/testing/storyboard/runner.js +382 -69
- package/dist/lib/testing/storyboard/runner.js.map +1 -1
- package/dist/lib/testing/storyboard/runner.mjs +387 -70
- package/dist/lib/testing/storyboard/runner.mjs.map +1 -1
- package/dist/lib/testing/storyboard/seeding.js +3 -1
- package/dist/lib/testing/storyboard/seeding.js.map +1 -1
- package/dist/lib/testing/storyboard/seeding.mjs +3 -1
- package/dist/lib/testing/storyboard/seeding.mjs.map +1 -1
- package/dist/lib/testing/storyboard/task-map.d.ts.map +1 -1
- package/dist/lib/testing/storyboard/task-map.js +16 -2
- package/dist/lib/testing/storyboard/task-map.js.map +1 -1
- package/dist/lib/testing/storyboard/task-map.mjs +16 -2
- package/dist/lib/testing/storyboard/task-map.mjs.map +1 -1
- package/dist/lib/testing/storyboard/trusted-match-context-replay.d.mts +11 -0
- package/dist/lib/testing/storyboard/trusted-match-context-replay.d.ts +12 -0
- package/dist/lib/testing/storyboard/trusted-match-context-replay.d.ts.map +1 -0
- package/dist/lib/testing/storyboard/trusted-match-context-replay.js +340 -0
- package/dist/lib/testing/storyboard/trusted-match-context-replay.js.map +1 -0
- package/dist/lib/testing/storyboard/trusted-match-context-replay.mjs +316 -0
- package/dist/lib/testing/storyboard/trusted-match-context-replay.mjs.map +1 -0
- package/dist/lib/testing/storyboard/types.d.mts +84 -15
- package/dist/lib/testing/storyboard/types.d.ts +84 -15
- package/dist/lib/testing/storyboard/types.d.ts.map +1 -1
- package/dist/lib/testing/storyboard/types.js +2 -0
- package/dist/lib/testing/storyboard/types.js.map +1 -1
- package/dist/lib/testing/storyboard/types.mjs +2 -0
- package/dist/lib/testing/storyboard/types.mjs.map +1 -1
- package/dist/lib/testing/storyboard/validations.d.mts +3 -0
- package/dist/lib/testing/storyboard/validations.d.ts +3 -0
- package/dist/lib/testing/storyboard/validations.d.ts.map +1 -1
- package/dist/lib/testing/storyboard/validations.js +64 -24
- package/dist/lib/testing/storyboard/validations.js.map +1 -1
- package/dist/lib/testing/storyboard/validations.mjs +64 -24
- package/dist/lib/testing/storyboard/validations.mjs.map +1 -1
- package/dist/lib/types/server-payload-aliases.d.mts +8 -2
- package/dist/lib/types/server-payload-aliases.d.ts +8 -2
- package/dist/lib/types/server-payload-aliases.d.ts.map +1 -1
- package/dist/lib/types/server-payload-aliases.js.map +1 -1
- package/dist/lib/upstream-recorder/recorder.d.mts +7 -18
- package/dist/lib/upstream-recorder/recorder.d.ts +7 -18
- package/dist/lib/upstream-recorder/recorder.d.ts.map +1 -1
- package/dist/lib/upstream-recorder/recorder.js +17 -10
- package/dist/lib/upstream-recorder/recorder.js.map +1 -1
- package/dist/lib/upstream-recorder/recorder.mjs +12 -5
- package/dist/lib/upstream-recorder/recorder.mjs.map +1 -1
- package/dist/lib/utils/redact-secrets.d.ts.map +1 -1
- package/dist/lib/utils/redact-secrets.js +7 -1
- package/dist/lib/utils/redact-secrets.js.map +1 -1
- package/dist/lib/utils/redact-secrets.mjs +7 -1
- package/dist/lib/utils/redact-secrets.mjs.map +1 -1
- package/dist/lib/version.d.mts +3 -3
- package/dist/lib/version.d.ts +3 -3
- package/dist/lib/version.js +3 -3
- package/dist/lib/version.js.map +1 -1
- package/dist/lib/version.mjs +3 -3
- package/dist/lib/version.mjs.map +1 -1
- package/docs/guides/BUILD-AN-AGENT.md +4 -2
- package/docs/llms.txt +1 -1
- package/package.json +2 -2
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../src/lib/testing/storyboard/oauth-metadata-graph/grader.ts"],"sourcesContent":["import { isAlwaysBlocked, isPrivateIp } from '../../../net/address-guards';\nimport { ssrfSafeFetch, SsrfRefusedError } from '../../../net/ssrf-fetch';\nimport type { HttpProbeResult } from '../types';\nimport type {\n OAuthMetadataFetchResponse,\n OAuthMetadataFetchTransport,\n OAuthMetadataGraphErrorCode,\n OAuthMetadataGraphFinding,\n OAuthMetadataGraphGrade,\n OAuthMetadataGraphObservation,\n} from './types';\n\nconst LIMITS = Object.freeze({\n authorizationServers: 16,\n uniqueUrls: 64,\n totalRequests: 64,\n responseBytes: 2 * 1024 * 1024,\n responseBytesPerRequest: 256 * 1024,\n redirects: 3,\n wholeRunMs: 60_000,\n});\n\nconst DIAGNOSTIC_ORDER: readonly OAuthMetadataGraphErrorCode[] = Object.freeze([\n 'oauth_graph_limit_exceeded',\n 'oauth_fetch_blocked',\n 'oauth_protected_resource_metadata_unavailable',\n 'oauth_protected_resource_metadata_invalid',\n 'oauth_resource_mismatch',\n 'oauth_authorization_servers_empty',\n 'oauth_authorization_server_url_invalid',\n 'oauth_authorization_server_metadata_unavailable',\n 'oauth_authorization_server_metadata_invalid',\n 'oauth_issuer_mismatch',\n 'oauth_endpoint_url_invalid',\n 'oauth_endpoint_unreachable',\n 'oauth_jwks_unavailable',\n]);\n\nexport interface GradeOAuthMetadataGraphOptions {\n /** Explicit local-development opt-in. Production grading remains HTTPS-only. */\n allowHttp?: boolean;\n signal?: AbortSignal;\n /** Trusted scoped fetch supplied by a hosted runner; it must enforce peer-address binding. */\n trustedFetchFn?: typeof fetch;\n}\n\ninterface InternalGradeOAuthMetadataGraphOptions extends GradeOAuthMetadataGraphOptions {\n /** Offline vector transport; deliberately excluded from the public grader options. */\n transport?: OAuthMetadataFetchTransport;\n trustedDevelopmentOrigin?: string;\n}\n\ninterface FetchedDocument {\n result: HttpProbeResult;\n bytes: number;\n blocked?: boolean;\n limitExceeded?: boolean;\n}\n\ninterface FetchState {\n totalRequests: number;\n totalResponseBytes: number;\n uniqueUrls: Set<string>;\n cache: Map<string, FetchedDocument>;\n responses: Map<string, OAuthMetadataFetchResponse>;\n observations: OAuthMetadataGraphObservation[];\n}\n\ninterface FetchDocumentOptions {\n followRedirects: boolean;\n kind: OAuthMetadataGraphObservation['kind'];\n authorizationServerIndex?: number;\n}\n\n/**\n * Shared RFC 9728 comparison semantics used by security.yaml and oauth_setup.\n * Only scheme and host are case-folded and default ports are elided. The\n * path, query, fragment, userinfo, and trailing slash remain significant.\n */\nexport function normalizeOAuthResourceForComparison(value: string): string {\n // Do not rebuild through URL: WHATWG parsing removes dot segments and turns\n // an empty path into `/`, but the shared storyboard rule says the remainder\n // of the URL is byte-for-byte significant after scheme/host/default-port.\n const match = /^([A-Za-z][A-Za-z\\d+.-]*):\\/\\/([^/?#]*)([\\s\\S]*)$/.exec(value);\n if (!match) return value;\n const scheme = match[1]!.toLowerCase();\n const authority = match[2]!;\n const remainder = match[3]!;\n const at = authority.lastIndexOf('@');\n const userinfo = at >= 0 ? authority.slice(0, at + 1) : '';\n const hostAndPort = at >= 0 ? authority.slice(at + 1) : authority;\n let host: string;\n let port = '';\n if (hostAndPort.startsWith('[')) {\n const bracket = hostAndPort.indexOf(']');\n if (bracket < 0) return value;\n host = hostAndPort.slice(0, bracket + 1).toLowerCase();\n const suffix = hostAndPort.slice(bracket + 1);\n if (suffix && !/^:\\d+$/.test(suffix)) return value;\n port = suffix.slice(1);\n } else {\n const colon = hostAndPort.lastIndexOf(':');\n if (colon >= 0) {\n const suffix = hostAndPort.slice(colon + 1);\n if (!/^\\d+$/.test(suffix)) return value;\n host = hostAndPort.slice(0, colon).toLowerCase();\n port = suffix;\n } else {\n host = hostAndPort.toLowerCase();\n }\n }\n if (!host) return value;\n const defaultPort = scheme === 'https' ? '443' : scheme === 'http' ? '80' : undefined;\n const renderedPort = port && port !== defaultPort ? `:${port}` : '';\n return `${scheme}://${userinfo}${host}${renderedPort}${remainder}`;\n}\n\n/** Redact sensitive URL components before including a URL in a report. */\nexport function redactOAuthUrlForOutput(value: string): string {\n try {\n const u = new URL(value);\n if (u.username || u.password) {\n u.username = 'REDACTED';\n u.password = 'REDACTED';\n }\n for (const key of [...u.searchParams.keys()]) u.searchParams.set(key, 'REDACTED');\n if (u.hash) u.hash = '#REDACTED';\n return u.href;\n } catch {\n return '[invalid URL]';\n }\n}\n\n/** Redact absolute URLs embedded in diagnostic text before report serialization. */\nexport function redactOAuthUrlsInText(value: string): string {\n return value.replace(/https?:\\/\\/[^\\s\"'<>]+/gi, url => redactOAuthUrlForOutput(url));\n}\n\nexport function buildProtectedResourceMetadataUrl(agentUrl: string): string {\n const u = new URL(agentUrl);\n return `${u.origin}/.well-known/oauth-protected-resource${u.pathname}`;\n}\n\nexport function buildAuthorizationServerMetadataUrl(issuer: string): string {\n const u = new URL(issuer);\n const issuerPath = u.pathname === '/' ? '' : u.pathname;\n return `${u.origin}/.well-known/oauth-authorization-server${issuerPath}`;\n}\n\nexport async function gradeOAuthMetadataGraph(\n agentUrl: string,\n options: GradeOAuthMetadataGraphOptions = {}\n): Promise<OAuthMetadataGraphGrade> {\n return gradeOAuthMetadataGraphWithTransport(agentUrl, options);\n}\n\n/** @internal Used only by the deterministic, no-network vector harness. */\nexport async function gradeOAuthMetadataGraphWithTransport(\n agentUrl: string,\n options: InternalGradeOAuthMetadataGraphOptions = {}\n): Promise<OAuthMetadataGraphGrade> {\n const timeoutSignal = AbortSignal.timeout(LIMITS.wholeRunMs);\n const signal = options.signal ? AbortSignal.any([options.signal, timeoutSignal]) : timeoutSignal;\n const trustedDevelopmentOrigin = options.allowHttp === true ? parseOrigin(agentUrl) : undefined;\n const graphOptions: InternalGradeOAuthMetadataGraphOptions = { ...options, trustedDevelopmentOrigin };\n const state: FetchState = {\n totalRequests: 0,\n totalResponseBytes: 0,\n uniqueUrls: new Set(),\n cache: new Map(),\n responses: new Map(),\n observations: [],\n };\n const findings: OAuthMetadataGraphFinding[] = [];\n let protectedResourceUrl: string;\n try {\n protectedResourceUrl = buildProtectedResourceMetadataUrl(agentUrl);\n } catch {\n protectedResourceUrl = redactOAuthUrlForOutput(agentUrl);\n findings.push(finding('oauth_protected_resource_metadata_invalid', 'Agent URL is not an absolute URL.'));\n return finish(protectedResourceUrl, emptyProbe(protectedResourceUrl), state, findings);\n }\n\n const protectedResource = await fetchDocument(protectedResourceUrl, graphOptions, signal, state, {\n followRedirects: true,\n kind: 'protected_resource_metadata',\n });\n if (protectedResource.blocked) {\n findings.push(fetchFailureFinding('oauth_fetch_blocked', protectedResource.result, undefined));\n return finish(protectedResourceUrl, protectedResource.result, state, findings);\n }\n if (protectedResource.limitExceeded) {\n findings.push(fetchFailureFinding('oauth_graph_limit_exceeded', protectedResource.result, undefined));\n return finish(protectedResourceUrl, protectedResource.result, state, findings);\n }\n if (protectedResource.result.error || protectedResource.result.status !== 200) {\n findings.push(\n fetchFailureFinding('oauth_protected_resource_metadata_unavailable', protectedResource.result, undefined)\n );\n return finish(protectedResourceUrl, protectedResource.result, state, findings);\n }\n const prm = asObject(protectedResource.result.body);\n if (!prm || !isJsonContentType(protectedResource.result.headers['content-type'])) {\n findings.push(\n finding(\n 'oauth_protected_resource_metadata_invalid',\n 'Protected-resource metadata must be an application/json object.',\n protectedResource.result.url\n )\n );\n return finish(protectedResourceUrl, protectedResource.result, state, findings);\n }\n\n const resource = prm.resource;\n if (typeof resource !== 'string' || !isAllowedAbsoluteUrl(resource, trustedDevelopmentOrigin, false)) {\n findings.push(\n finding(\n 'oauth_protected_resource_metadata_invalid',\n 'Protected-resource metadata resource must be an absolute HTTPS URI.',\n protectedResource.result.url,\n 'resource'\n )\n );\n } else if (normalizeOAuthResourceForComparison(resource) !== normalizeOAuthResourceForComparison(agentUrl)) {\n findings.push(\n finding(\n 'oauth_resource_mismatch',\n 'Protected-resource metadata resource does not match the agent endpoint.',\n protectedResource.result.url,\n 'resource'\n )\n );\n }\n\n const authorizationServers = prm.authorization_servers;\n if (!Array.isArray(authorizationServers) || authorizationServers.length === 0) {\n findings.push(\n finding(\n 'oauth_authorization_servers_empty',\n 'authorization_servers must be a non-empty array of issuer URI strings.',\n protectedResource.result.url,\n 'authorization_servers'\n )\n );\n return finish(protectedResourceUrl, protectedResource.result, state, findings);\n }\n if (authorizationServers.length > LIMITS.authorizationServers) {\n findings.push(\n finding(\n 'oauth_graph_limit_exceeded',\n `authorization_servers exceeds the graph limit of ${LIMITS.authorizationServers}.`,\n protectedResource.result.url,\n 'authorization_servers'\n )\n );\n return finish(protectedResourceUrl, protectedResource.result, state, findings);\n }\n\n // Deliberately sequential: the contract permits up to four concurrent\n // requests, while sequential traversal guarantees source-array ordering and\n // still validates every authorization server instead of returning early.\n for (let index = 0; index < authorizationServers.length; index++) {\n const issuer: unknown = authorizationServers[index];\n if (typeof issuer !== 'string' || !isIssuerUrl(issuer, trustedDevelopmentOrigin)) {\n findings.push(\n finding(\n 'oauth_authorization_server_url_invalid',\n 'Authorization-server identifier must be an absolute HTTPS issuer URI without userinfo, query, or fragment.',\n typeof issuer === 'string' ? issuer : protectedResource.result.url,\n `authorization_servers[${index}]`,\n index\n )\n );\n continue;\n }\n\n const metadataUrl = buildAuthorizationServerMetadataUrl(issuer);\n const metadata = await fetchDocument(metadataUrl, graphOptions, signal, state, {\n followRedirects: true,\n kind: 'authorization_server_metadata',\n authorizationServerIndex: index,\n });\n if (metadata.blocked) {\n findings.push(fetchFailureFinding('oauth_fetch_blocked', metadata.result, index));\n continue;\n }\n if (metadata.limitExceeded) {\n findings.push(fetchFailureFinding('oauth_graph_limit_exceeded', metadata.result, index));\n continue;\n }\n if (metadata.result.error || metadata.result.status !== 200) {\n findings.push(fetchFailureFinding('oauth_authorization_server_metadata_unavailable', metadata.result, index));\n continue;\n }\n const document = asObject(metadata.result.body);\n if (!document || !isJsonContentType(metadata.result.headers['content-type'])) {\n findings.push(\n finding(\n 'oauth_authorization_server_metadata_invalid',\n 'Authorization-server metadata must be an application/json object.',\n metadata.result.url,\n undefined,\n index\n )\n );\n continue;\n }\n\n const issuerField = document.issuer;\n if (typeof issuerField !== 'string' || !isIssuerUrl(issuerField, trustedDevelopmentOrigin)) {\n findings.push(\n finding(\n 'oauth_authorization_server_metadata_invalid',\n 'Authorization-server metadata issuer is missing or invalid.',\n metadata.result.url,\n 'issuer',\n index\n )\n );\n } else if (issuerField !== issuer) {\n // RFC 8414 section 3.3: exact decoded JSON string equality. Do not URL-\n // normalize case, ports, escapes, path, or a trailing slash here.\n findings.push(\n finding(\n 'oauth_issuer_mismatch',\n 'Authorization-server metadata issuer is not exactly equal to the advertised issuer.',\n metadata.result.url,\n 'issuer',\n index\n )\n );\n }\n\n const shape = validateAuthorizationServerShape(document);\n if (!shape.ok) {\n findings.push(\n finding('oauth_authorization_server_metadata_invalid', shape.message, metadata.result.url, shape.field, index)\n );\n continue;\n }\n\n const endpointEntries: Array<{\n field: 'authorization_endpoint' | 'token_endpoint' | 'jwks_uri' | 'registration_endpoint';\n kind: OAuthMetadataGraphObservation['kind'];\n probe: boolean;\n followRedirects: boolean;\n }> = [\n {\n field: 'authorization_endpoint',\n kind: 'authorization_endpoint',\n probe: shape.needsAuthorizationEndpoint,\n followRedirects: false,\n },\n { field: 'token_endpoint', kind: 'token_endpoint', probe: shape.needsTokenEndpoint, followRedirects: false },\n { field: 'jwks_uri', kind: 'jwks_uri', probe: document.jwks_uri !== undefined, followRedirects: true },\n { field: 'registration_endpoint', kind: 'authorization_endpoint', probe: false, followRedirects: false },\n ];\n\n let endpointShapeFailed = false;\n for (const endpoint of endpointEntries) {\n const raw = document[endpoint.field];\n if (raw === undefined) continue;\n if (typeof raw !== 'string' || !isAllowedAbsoluteUrl(raw, trustedDevelopmentOrigin, true)) {\n findings.push(\n finding(\n 'oauth_endpoint_url_invalid',\n `${endpoint.field} must be an absolute HTTPS URL without credentials or a fragment.`,\n metadata.result.url,\n endpoint.field,\n index\n )\n );\n endpointShapeFailed = true;\n }\n }\n if (endpointShapeFailed) continue;\n\n for (const endpoint of endpointEntries) {\n if (!endpoint.probe) continue;\n const url = document[endpoint.field] as string;\n const probed = await fetchDocument(url, graphOptions, signal, state, {\n followRedirects: endpoint.followRedirects,\n kind: endpoint.kind,\n authorizationServerIndex: index,\n });\n if (probed.blocked) {\n findings.push(fetchFailureFinding('oauth_fetch_blocked', probed.result, index));\n continue;\n }\n if (probed.limitExceeded) {\n findings.push(fetchFailureFinding('oauth_graph_limit_exceeded', probed.result, index));\n continue;\n }\n if (endpoint.field === 'jwks_uri') {\n const jwks = asObject(probed.result.body);\n if (\n probed.result.error ||\n probed.result.status !== 200 ||\n !isJsonContentType(probed.result.headers['content-type']) ||\n !jwks ||\n !Array.isArray(jwks.keys)\n ) {\n findings.push(fetchFailureFinding('oauth_jwks_unavailable', probed.result, index));\n }\n } else if (\n (endpoint.field === 'authorization_endpoint' || endpoint.field === 'token_endpoint') &&\n !endpointReachable(endpoint.field, probed.result)\n ) {\n findings.push(fetchFailureFinding('oauth_endpoint_unreachable', probed.result, index));\n }\n }\n }\n\n return finish(protectedResourceUrl, protectedResource.result, state, findings);\n}\n\nfunction validateAuthorizationServerShape(document: Record<string, unknown>):\n | {\n ok: true;\n needsAuthorizationEndpoint: boolean;\n needsTokenEndpoint: boolean;\n }\n | { ok: false; message: string; field: string } {\n const grants = document.grant_types_supported;\n if (grants !== undefined && !isNonEmptyStringArray(grants)) {\n return {\n ok: false,\n message: 'grant_types_supported must be a non-empty string array when present.',\n field: 'grant_types_supported',\n };\n }\n const effectiveGrants = grants === undefined ? ['authorization_code', 'implicit'] : (grants as string[]);\n const responseTypes = document.response_types_supported;\n if (responseTypes !== undefined && !isNonEmptyStringArray(responseTypes)) {\n return {\n ok: false,\n message: 'response_types_supported must be a non-empty string array when present.',\n field: 'response_types_supported',\n };\n }\n const responseTypeUsesAuthorization = Array.isArray(responseTypes)\n ? responseTypes.some(value => value.split(/\\s+/).some(part => part === 'code' || part === 'token'))\n : false;\n const needsAuthorizationEndpoint =\n effectiveGrants.some(grant => grant === 'authorization_code' || grant === 'implicit') ||\n responseTypeUsesAuthorization;\n const needsTokenEndpoint = !(effectiveGrants.length === 1 && effectiveGrants[0] === 'implicit');\n\n if (needsAuthorizationEndpoint && !isNonEmptyStringArray(responseTypes)) {\n return {\n ok: false,\n message: 'response_types_supported is required when the authorization endpoint is used.',\n field: 'response_types_supported',\n };\n }\n if (needsAuthorizationEndpoint && typeof document.authorization_endpoint !== 'string') {\n return {\n ok: false,\n message: 'authorization_endpoint is required for the effective grant and response-type set.',\n field: 'authorization_endpoint',\n };\n }\n if (needsTokenEndpoint && typeof document.token_endpoint !== 'string') {\n return {\n ok: false,\n message: 'token_endpoint is required unless the effective grant set is implicit-only.',\n field: 'token_endpoint',\n };\n }\n return { ok: true, needsAuthorizationEndpoint, needsTokenEndpoint };\n}\n\nfunction endpointReachable(field: 'authorization_endpoint' | 'token_endpoint', result: HttpProbeResult): boolean {\n if (result.error) return false;\n const status = result.status;\n if (field === 'token_endpoint') return status > 0 && status < 500 && status !== 410;\n if (status >= 200 && status < 400) return true;\n return status === 400 || status === 401 || status === 403 || status === 405 || status === 429;\n}\n\nasync function fetchDocument(\n initialUrl: string,\n options: InternalGradeOAuthMetadataGraphOptions,\n signal: AbortSignal,\n state: FetchState,\n fetchOptions: FetchDocumentOptions\n): Promise<FetchedDocument> {\n const cacheKey = `${fetchOptions.followRedirects ? 'follow' : 'manual'} ${initialUrl}`;\n const cached = state.cache.get(cacheKey);\n if (cached) return cached;\n\n let current = initialUrl;\n let redirects = 0;\n while (true) {\n throwIfCallerAborted(options.signal);\n if (signal.aborted) {\n const outcome = failedFetch(current, 'OAuth metadata graph exceeded its whole-run timeout.');\n outcome.limitExceeded = true;\n state.cache.set(cacheKey, outcome);\n return outcome;\n }\n let response = state.responses.get(current);\n if (!response) {\n if (!state.uniqueUrls.has(current) && state.uniqueUrls.size >= LIMITS.uniqueUrls) {\n const outcome = failedFetch(current, `OAuth metadata graph exceeded ${LIMITS.uniqueUrls} unique URLs.`);\n outcome.limitExceeded = true;\n state.cache.set(cacheKey, outcome);\n return outcome;\n }\n state.uniqueUrls.add(current);\n if (state.totalRequests >= LIMITS.totalRequests) {\n const outcome = failedFetch(current, `OAuth metadata graph exceeded ${LIMITS.totalRequests} requests.`);\n outcome.limitExceeded = true;\n state.cache.set(cacheKey, outcome);\n return outcome;\n }\n state.totalRequests++;\n\n try {\n response = options.transport\n ? await options.transport.fetch(current, { signal, maxBodyBytes: LIMITS.responseBytesPerRequest })\n : await defaultTransportFetch(current, options.trustedDevelopmentOrigin, signal, options.trustedFetchFn);\n if (\n response.connectedPeerAddress &&\n isBlockedAddress(\n response.connectedPeerAddress,\n isTrustedDevelopmentUrl(current, options.trustedDevelopmentOrigin)\n )\n ) {\n const outcome = failedFetch(current, 'Connected peer address was rejected by the SSRF policy.');\n outcome.blocked = true;\n state.cache.set(cacheKey, outcome);\n return outcome;\n }\n } catch (error) {\n throwIfCallerAborted(options.signal);\n const outcome = failedFetch(current, safeFetchError(error));\n if (signal.aborted || (error instanceof SsrfRefusedError && error.code === 'body_exceeds_limit')) {\n outcome.limitExceeded = true;\n } else {\n outcome.blocked =\n error instanceof SsrfRefusedError && !['dns_lookup_failed', 'dns_empty'].includes(error.code);\n }\n state.cache.set(cacheKey, outcome);\n return outcome;\n }\n\n state.totalResponseBytes += response.body.byteLength;\n if (state.totalResponseBytes > LIMITS.responseBytes) {\n const outcome = failedFetch(current, `OAuth metadata graph exceeded ${LIMITS.responseBytes} response bytes.`);\n outcome.limitExceeded = true;\n state.cache.set(cacheKey, outcome);\n return outcome;\n }\n state.responses.set(current, response);\n }\n\n const result: HttpProbeResult = {\n url: redactOAuthUrlForOutput(current),\n status: response.status,\n headers: response.headers,\n body: decodeBody(response.body, response.headers['content-type']),\n };\n state.observations.push({\n kind: fetchOptions.kind,\n url: result.url,\n status: result.status,\n ...(fetchOptions.authorizationServerIndex !== undefined && {\n authorization_server_index: fetchOptions.authorizationServerIndex,\n }),\n });\n\n if (!fetchOptions.followRedirects || !isRedirectStatus(response.status)) {\n const outcome = { result, bytes: response.body.byteLength };\n state.cache.set(cacheKey, outcome);\n return outcome;\n }\n const location = response.headers.location;\n if (!location) {\n const outcome = { result, bytes: response.body.byteLength };\n state.cache.set(cacheKey, outcome);\n return outcome;\n }\n if (redirects >= LIMITS.redirects) {\n const outcome = failedFetch(current, `OAuth metadata redirect count exceeded ${LIMITS.redirects}.`);\n outcome.limitExceeded = true;\n state.cache.set(cacheKey, outcome);\n return outcome;\n }\n let next: string;\n try {\n next = new URL(location, current).href;\n } catch {\n const outcome = failedFetch(current, 'OAuth metadata redirect Location was not a valid URL.');\n outcome.blocked = true;\n state.cache.set(cacheKey, outcome);\n return outcome;\n }\n if (!isAllowedAbsoluteUrl(next, options.trustedDevelopmentOrigin, true)) {\n const outcome = failedFetch(next, 'OAuth metadata redirect target violated the URL policy.');\n outcome.blocked = true;\n state.cache.set(cacheKey, outcome);\n return outcome;\n }\n redirects++;\n current = next;\n }\n}\n\nasync function defaultTransportFetch(\n url: string,\n trustedDevelopmentOrigin: string | undefined,\n signal: AbortSignal,\n trustedFetchFn?: typeof fetch\n): Promise<OAuthMetadataFetchResponse> {\n const result = await ssrfSafeFetch(url, {\n method: 'GET',\n headers: { accept: 'application/json', 'accept-encoding': 'identity' },\n allowPrivateIp: isTrustedDevelopmentUrl(url, trustedDevelopmentOrigin),\n signal,\n timeoutMs: 10_000,\n maxBodyBytes: LIMITS.responseBytesPerRequest,\n ...(trustedFetchFn && { trustedFetchFn }),\n });\n return {\n status: result.status,\n headers: result.headers,\n body: result.body,\n connectedPeerAddress: result.connectionPinned ? result.pinnedAddress : undefined,\n };\n}\n\nfunction finish(\n protectedResourceUrl: string,\n protectedResourceResult: HttpProbeResult,\n state: FetchState,\n findings: OAuthMetadataGraphFinding[]\n): OAuthMetadataGraphGrade {\n const sorted = findings.slice().sort((a, b) => {\n const codeOrder = DIAGNOSTIC_ORDER.indexOf(a.code) - DIAGNOSTIC_ORDER.indexOf(b.code);\n if (codeOrder !== 0) return codeOrder;\n return (a.authorization_server_index ?? -1) - (b.authorization_server_index ?? -1);\n });\n const first = sorted[0];\n return {\n success: first === undefined,\n protected_resource_url: redactOAuthUrlForOutput(protectedResourceUrl),\n protected_resource_result: sanitizeProtectedResourceResult(protectedResourceResult),\n observations: state.observations,\n findings: sorted,\n ...(first && { error_code: first.code, error: first.message }),\n total_requests: state.totalRequests,\n total_response_bytes: state.totalResponseBytes,\n };\n}\n\nfunction finding(\n code: OAuthMetadataGraphErrorCode,\n message: string,\n url?: string,\n field?: string,\n authorizationServerIndex?: number\n): OAuthMetadataGraphFinding {\n return {\n code,\n message,\n ...(url && { url: redactOAuthUrlForOutput(url) }),\n ...(field && { field }),\n ...(authorizationServerIndex !== undefined && { authorization_server_index: authorizationServerIndex }),\n };\n}\n\nfunction fetchFailureFinding(\n code: OAuthMetadataGraphErrorCode,\n result: HttpProbeResult,\n authorizationServerIndex: number | undefined\n): OAuthMetadataGraphFinding {\n return finding(code, result.error ?? `HTTP ${result.status}`, result.url, undefined, authorizationServerIndex);\n}\n\nfunction failedFetch(url: string, error: string): FetchedDocument {\n return { result: { ...emptyProbe(redactOAuthUrlForOutput(url)), error }, bytes: 0 };\n}\n\nfunction emptyProbe(url: string): HttpProbeResult {\n return { url, status: 0, headers: {}, body: null };\n}\n\nfunction safeFetchError(error: unknown): string {\n if (error instanceof SsrfRefusedError) return `SSRF fetch refused (${error.code}).`;\n if (error instanceof Error) {\n if (error.name === 'AbortError' || /timeout/i.test(error.message)) return 'OAuth metadata fetch timed out.';\n return 'OAuth metadata fetch failed.';\n }\n return 'OAuth metadata fetch failed.';\n}\n\nfunction throwIfCallerAborted(signal: AbortSignal | undefined): void {\n if (!signal?.aborted) return;\n if (signal.reason instanceof Error) throw signal.reason;\n const error = new Error(signal.reason == null ? 'The operation was aborted' : String(signal.reason));\n error.name = 'AbortError';\n throw error;\n}\n\nfunction isIssuerUrl(value: string, trustedDevelopmentOrigin: string | undefined): boolean {\n try {\n const u = new URL(value);\n return (\n (u.protocol === 'https:' || isTrustedDevelopmentUrl(value, trustedDevelopmentOrigin)) &&\n !u.username &&\n !u.password &&\n !u.search &&\n !u.hash\n );\n } catch {\n return false;\n }\n}\n\nfunction isAllowedAbsoluteUrl(\n value: string,\n trustedDevelopmentOrigin: string | undefined,\n rejectCredentialsAndFragment: boolean\n): boolean {\n try {\n const u = new URL(value);\n if (u.protocol !== 'https:' && !isTrustedDevelopmentUrl(value, trustedDevelopmentOrigin)) return false;\n if (rejectCredentialsAndFragment && (u.username || u.password || u.hash)) return false;\n return true;\n } catch {\n return false;\n }\n}\n\nfunction parseOrigin(value: string): string | undefined {\n try {\n return new URL(value).origin;\n } catch {\n return undefined;\n }\n}\n\nfunction isTrustedDevelopmentUrl(value: string, trustedDevelopmentOrigin: string | undefined): boolean {\n return trustedDevelopmentOrigin !== undefined && parseOrigin(value) === trustedDevelopmentOrigin;\n}\n\nfunction isRedirectStatus(status: number): boolean {\n return status === 301 || status === 302 || status === 303 || status === 307 || status === 308;\n}\n\nfunction sanitizeProtectedResourceResult(result: HttpProbeResult): HttpProbeResult {\n const body = asObject(result.body);\n let safeBody: unknown = result.body;\n if (body) {\n const safeRecord: Record<string, unknown> = { ...body };\n if (typeof body.resource === 'string') {\n safeRecord.resource = redactOAuthUrlForOutput(body.resource);\n }\n if (Array.isArray(body.authorization_servers)) {\n safeRecord.authorization_servers = body.authorization_servers.map(value =>\n typeof value === 'string' ? redactOAuthUrlForOutput(value) : value\n );\n }\n safeBody = safeRecord;\n }\n const allowedHeaders = new Set(['content-type', 'content-length', 'content-encoding', 'location']);\n const headers = Object.fromEntries(\n Object.entries(result.headers)\n .filter(([key]) => allowedHeaders.has(key.toLowerCase()))\n .map(([key, value]) => [\n key.toLowerCase(),\n key.toLowerCase() === 'location' ? redactOAuthUrlForOutput(value) : value,\n ])\n );\n return {\n ...result,\n url: redactOAuthUrlForOutput(result.url),\n headers,\n body: safeBody,\n };\n}\n\nfunction isBlockedAddress(address: string, allowPrivate: boolean): boolean {\n return isAlwaysBlocked(address) || (!allowPrivate && isPrivateIp(address));\n}\n\nfunction isNonEmptyStringArray(value: unknown): value is string[] {\n return Array.isArray(value) && value.length > 0 && value.every(item => typeof item === 'string' && item.length > 0);\n}\n\nfunction asObject(value: unknown): Record<string, unknown> | undefined {\n return value !== null && typeof value === 'object' && !Array.isArray(value)\n ? (value as Record<string, unknown>)\n : undefined;\n}\n\nfunction isJsonContentType(value: string | undefined): boolean {\n return typeof value === 'string' && /^(application\\/json|[^;]+\\+json)(?:;|$)/i.test(value.trim());\n}\n\nfunction decodeBody(body: Uint8Array, contentType: string | undefined): unknown {\n if (body.byteLength === 0) return null;\n const text = Buffer.from(body.buffer, body.byteOffset, body.byteLength).toString('utf8');\n if (isJsonContentType(contentType)) {\n try {\n return JSON.parse(text);\n } catch {\n return text;\n }\n }\n return text;\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { gradeOAuthMetadataGraph, buildAuthorizationServerMetadataUrl, buildProtectedResourceMetadataUrl, normalizeOAuthResourceForComparison, redactOAuthUrlForOutput, redactOAuthUrlsInText, } from './grader.mjs';
|
|
2
|
+
export type { GradeOAuthMetadataGraphOptions } from './grader.mjs';
|
|
3
|
+
export { gradeOAuthMetadataGraphVector, loadOAuthMetadataGraphVectors } from './vector-loader.mjs';
|
|
4
|
+
export type { OAuthMetadataGraphVector, OAuthMetadataGraphVectorCorpus } from './vector-loader.mjs';
|
|
5
|
+
export type { OAuthMetadataFetchResponse, OAuthMetadataFetchTransport, OAuthMetadataGraphErrorCode, OAuthMetadataGraphFinding, OAuthMetadataGraphGrade, OAuthMetadataGraphObservation, } from './types.mjs';
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { gradeOAuthMetadataGraph, buildAuthorizationServerMetadataUrl, buildProtectedResourceMetadataUrl, normalizeOAuthResourceForComparison, redactOAuthUrlForOutput, redactOAuthUrlsInText, } from './grader';
|
|
2
|
+
export type { GradeOAuthMetadataGraphOptions } from './grader';
|
|
3
|
+
export { gradeOAuthMetadataGraphVector, loadOAuthMetadataGraphVectors } from './vector-loader';
|
|
4
|
+
export type { OAuthMetadataGraphVector, OAuthMetadataGraphVectorCorpus } from './vector-loader';
|
|
5
|
+
export type { OAuthMetadataFetchResponse, OAuthMetadataFetchTransport, OAuthMetadataGraphErrorCode, OAuthMetadataGraphFinding, OAuthMetadataGraphGrade, OAuthMetadataGraphObservation, } from './types';
|
|
6
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/lib/testing/storyboard/oauth-metadata-graph/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,uBAAuB,EACvB,mCAAmC,EACnC,iCAAiC,EACjC,mCAAmC,EACnC,uBAAuB,EACvB,qBAAqB,GACtB,MAAM,UAAU,CAAC;AAClB,YAAY,EAAE,8BAA8B,EAAE,MAAM,UAAU,CAAC;AAC/D,OAAO,EAAE,6BAA6B,EAAE,6BAA6B,EAAE,MAAM,iBAAiB,CAAC;AAC/F,YAAY,EAAE,wBAAwB,EAAE,8BAA8B,EAAE,MAAM,iBAAiB,CAAC;AAChG,YAAY,EACV,0BAA0B,EAC1B,2BAA2B,EAC3B,2BAA2B,EAC3B,yBAAyB,EACzB,uBAAuB,EACvB,6BAA6B,GAC9B,MAAM,SAAS,CAAC"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
var oauth_metadata_graph_exports = {};
|
|
20
|
+
__export(oauth_metadata_graph_exports, {
|
|
21
|
+
buildAuthorizationServerMetadataUrl: () => import_grader.buildAuthorizationServerMetadataUrl,
|
|
22
|
+
buildProtectedResourceMetadataUrl: () => import_grader.buildProtectedResourceMetadataUrl,
|
|
23
|
+
gradeOAuthMetadataGraph: () => import_grader.gradeOAuthMetadataGraph,
|
|
24
|
+
gradeOAuthMetadataGraphVector: () => import_vector_loader.gradeOAuthMetadataGraphVector,
|
|
25
|
+
loadOAuthMetadataGraphVectors: () => import_vector_loader.loadOAuthMetadataGraphVectors,
|
|
26
|
+
normalizeOAuthResourceForComparison: () => import_grader.normalizeOAuthResourceForComparison,
|
|
27
|
+
redactOAuthUrlForOutput: () => import_grader.redactOAuthUrlForOutput,
|
|
28
|
+
redactOAuthUrlsInText: () => import_grader.redactOAuthUrlsInText
|
|
29
|
+
});
|
|
30
|
+
module.exports = __toCommonJS(oauth_metadata_graph_exports);
|
|
31
|
+
var import_grader = require('./grader.js');
|
|
32
|
+
var import_vector_loader = require('./vector-loader.js');
|
|
33
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
34
|
+
0 && (module.exports = {
|
|
35
|
+
buildAuthorizationServerMetadataUrl,
|
|
36
|
+
buildProtectedResourceMetadataUrl,
|
|
37
|
+
gradeOAuthMetadataGraph,
|
|
38
|
+
gradeOAuthMetadataGraphVector,
|
|
39
|
+
loadOAuthMetadataGraphVectors,
|
|
40
|
+
normalizeOAuthResourceForComparison,
|
|
41
|
+
redactOAuthUrlForOutput,
|
|
42
|
+
redactOAuthUrlsInText
|
|
43
|
+
});
|
|
44
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../src/lib/testing/storyboard/oauth-metadata-graph/index.ts"],"sourcesContent":["export {\n gradeOAuthMetadataGraph,\n buildAuthorizationServerMetadataUrl,\n buildProtectedResourceMetadataUrl,\n normalizeOAuthResourceForComparison,\n redactOAuthUrlForOutput,\n redactOAuthUrlsInText,\n} from './grader';\nexport type { GradeOAuthMetadataGraphOptions } from './grader';\nexport { gradeOAuthMetadataGraphVector, loadOAuthMetadataGraphVectors } from './vector-loader';\nexport type { OAuthMetadataGraphVector, OAuthMetadataGraphVectorCorpus } from './vector-loader';\nexport type {\n OAuthMetadataFetchResponse,\n OAuthMetadataFetchTransport,\n OAuthMetadataGraphErrorCode,\n OAuthMetadataGraphFinding,\n OAuthMetadataGraphGrade,\n OAuthMetadataGraphObservation,\n} from './types';\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,oBAOO;AAEP,2BAA6E;","names":[]}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import {
|
|
2
|
+
gradeOAuthMetadataGraph,
|
|
3
|
+
buildAuthorizationServerMetadataUrl,
|
|
4
|
+
buildProtectedResourceMetadataUrl,
|
|
5
|
+
normalizeOAuthResourceForComparison,
|
|
6
|
+
redactOAuthUrlForOutput,
|
|
7
|
+
redactOAuthUrlsInText
|
|
8
|
+
} from "./grader.mjs";
|
|
9
|
+
import { gradeOAuthMetadataGraphVector, loadOAuthMetadataGraphVectors } from "./vector-loader.mjs";
|
|
10
|
+
export {
|
|
11
|
+
buildAuthorizationServerMetadataUrl,
|
|
12
|
+
buildProtectedResourceMetadataUrl,
|
|
13
|
+
gradeOAuthMetadataGraph,
|
|
14
|
+
gradeOAuthMetadataGraphVector,
|
|
15
|
+
loadOAuthMetadataGraphVectors,
|
|
16
|
+
normalizeOAuthResourceForComparison,
|
|
17
|
+
redactOAuthUrlForOutput,
|
|
18
|
+
redactOAuthUrlsInText
|
|
19
|
+
};
|
|
20
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../src/lib/testing/storyboard/oauth-metadata-graph/index.ts"],"sourcesContent":["export {\n gradeOAuthMetadataGraph,\n buildAuthorizationServerMetadataUrl,\n buildProtectedResourceMetadataUrl,\n normalizeOAuthResourceForComparison,\n redactOAuthUrlForOutput,\n redactOAuthUrlsInText,\n} from './grader';\nexport type { GradeOAuthMetadataGraphOptions } from './grader';\nexport { gradeOAuthMetadataGraphVector, loadOAuthMetadataGraphVectors } from './vector-loader';\nexport type { OAuthMetadataGraphVector, OAuthMetadataGraphVectorCorpus } from './vector-loader';\nexport type {\n OAuthMetadataFetchResponse,\n OAuthMetadataFetchTransport,\n OAuthMetadataGraphErrorCode,\n OAuthMetadataGraphFinding,\n OAuthMetadataGraphGrade,\n OAuthMetadataGraphObservation,\n} from './types';\n"],"mappings":"AAAA;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAEP,SAAS,+BAA+B,qCAAqC;","names":[]}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { HttpProbeResult } from '../types.mjs';
|
|
2
|
+
export type OAuthMetadataGraphErrorCode = 'oauth_protected_resource_metadata_unavailable' | 'oauth_protected_resource_metadata_invalid' | 'oauth_resource_mismatch' | 'oauth_authorization_servers_empty' | 'oauth_authorization_server_url_invalid' | 'oauth_authorization_server_metadata_unavailable' | 'oauth_authorization_server_metadata_invalid' | 'oauth_issuer_mismatch' | 'oauth_endpoint_url_invalid' | 'oauth_endpoint_unreachable' | 'oauth_jwks_unavailable' | 'oauth_fetch_blocked' | 'oauth_graph_limit_exceeded';
|
|
3
|
+
export interface OAuthMetadataGraphObservation {
|
|
4
|
+
kind: 'protected_resource_metadata' | 'authorization_server_metadata' | 'authorization_endpoint' | 'token_endpoint' | 'jwks_uri';
|
|
5
|
+
url: string;
|
|
6
|
+
status: number;
|
|
7
|
+
authorization_server_index?: number;
|
|
8
|
+
}
|
|
9
|
+
export interface OAuthMetadataGraphFinding {
|
|
10
|
+
code: OAuthMetadataGraphErrorCode;
|
|
11
|
+
message: string;
|
|
12
|
+
url?: string;
|
|
13
|
+
field?: string;
|
|
14
|
+
authorization_server_index?: number;
|
|
15
|
+
}
|
|
16
|
+
export interface OAuthMetadataGraphGrade {
|
|
17
|
+
success: boolean;
|
|
18
|
+
protected_resource_url: string;
|
|
19
|
+
protected_resource_result: HttpProbeResult;
|
|
20
|
+
observations: OAuthMetadataGraphObservation[];
|
|
21
|
+
findings: OAuthMetadataGraphFinding[];
|
|
22
|
+
error_code?: OAuthMetadataGraphErrorCode;
|
|
23
|
+
error?: string;
|
|
24
|
+
total_requests: number;
|
|
25
|
+
total_response_bytes: number;
|
|
26
|
+
}
|
|
27
|
+
export interface OAuthMetadataFetchResponse {
|
|
28
|
+
status: number;
|
|
29
|
+
headers: Record<string, string>;
|
|
30
|
+
body: Uint8Array;
|
|
31
|
+
connectedPeerAddress?: string;
|
|
32
|
+
}
|
|
33
|
+
export interface OAuthMetadataFetchTransport {
|
|
34
|
+
fetch(url: string, options: {
|
|
35
|
+
signal: AbortSignal;
|
|
36
|
+
maxBodyBytes: number;
|
|
37
|
+
}): Promise<OAuthMetadataFetchResponse>;
|
|
38
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { HttpProbeResult } from '../types';
|
|
2
|
+
export type OAuthMetadataGraphErrorCode = 'oauth_protected_resource_metadata_unavailable' | 'oauth_protected_resource_metadata_invalid' | 'oauth_resource_mismatch' | 'oauth_authorization_servers_empty' | 'oauth_authorization_server_url_invalid' | 'oauth_authorization_server_metadata_unavailable' | 'oauth_authorization_server_metadata_invalid' | 'oauth_issuer_mismatch' | 'oauth_endpoint_url_invalid' | 'oauth_endpoint_unreachable' | 'oauth_jwks_unavailable' | 'oauth_fetch_blocked' | 'oauth_graph_limit_exceeded';
|
|
3
|
+
export interface OAuthMetadataGraphObservation {
|
|
4
|
+
kind: 'protected_resource_metadata' | 'authorization_server_metadata' | 'authorization_endpoint' | 'token_endpoint' | 'jwks_uri';
|
|
5
|
+
url: string;
|
|
6
|
+
status: number;
|
|
7
|
+
authorization_server_index?: number;
|
|
8
|
+
}
|
|
9
|
+
export interface OAuthMetadataGraphFinding {
|
|
10
|
+
code: OAuthMetadataGraphErrorCode;
|
|
11
|
+
message: string;
|
|
12
|
+
url?: string;
|
|
13
|
+
field?: string;
|
|
14
|
+
authorization_server_index?: number;
|
|
15
|
+
}
|
|
16
|
+
export interface OAuthMetadataGraphGrade {
|
|
17
|
+
success: boolean;
|
|
18
|
+
protected_resource_url: string;
|
|
19
|
+
protected_resource_result: HttpProbeResult;
|
|
20
|
+
observations: OAuthMetadataGraphObservation[];
|
|
21
|
+
findings: OAuthMetadataGraphFinding[];
|
|
22
|
+
error_code?: OAuthMetadataGraphErrorCode;
|
|
23
|
+
error?: string;
|
|
24
|
+
total_requests: number;
|
|
25
|
+
total_response_bytes: number;
|
|
26
|
+
}
|
|
27
|
+
export interface OAuthMetadataFetchResponse {
|
|
28
|
+
status: number;
|
|
29
|
+
headers: Record<string, string>;
|
|
30
|
+
body: Uint8Array;
|
|
31
|
+
connectedPeerAddress?: string;
|
|
32
|
+
}
|
|
33
|
+
export interface OAuthMetadataFetchTransport {
|
|
34
|
+
fetch(url: string, options: {
|
|
35
|
+
signal: AbortSignal;
|
|
36
|
+
maxBodyBytes: number;
|
|
37
|
+
}): Promise<OAuthMetadataFetchResponse>;
|
|
38
|
+
}
|
|
39
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../../src/lib/testing/storyboard/oauth-metadata-graph/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAEhD,MAAM,MAAM,2BAA2B,GACnC,+CAA+C,GAC/C,2CAA2C,GAC3C,yBAAyB,GACzB,mCAAmC,GACnC,wCAAwC,GACxC,iDAAiD,GACjD,6CAA6C,GAC7C,uBAAuB,GACvB,4BAA4B,GAC5B,4BAA4B,GAC5B,wBAAwB,GACxB,qBAAqB,GACrB,4BAA4B,CAAC;AAEjC,MAAM,WAAW,6BAA6B;IAC5C,IAAI,EACA,6BAA6B,GAC7B,+BAA+B,GAC/B,wBAAwB,GACxB,gBAAgB,GAChB,UAAU,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;IACf,0BAA0B,CAAC,EAAE,MAAM,CAAC;CACrC;AAED,MAAM,WAAW,yBAAyB;IACxC,IAAI,EAAE,2BAA2B,CAAC;IAClC,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,0BAA0B,CAAC,EAAE,MAAM,CAAC;CACrC;AAED,MAAM,WAAW,uBAAuB;IACtC,OAAO,EAAE,OAAO,CAAC;IACjB,sBAAsB,EAAE,MAAM,CAAC;IAC/B,yBAAyB,EAAE,eAAe,CAAC;IAC3C,YAAY,EAAE,6BAA6B,EAAE,CAAC;IAC9C,QAAQ,EAAE,yBAAyB,EAAE,CAAC;IACtC,UAAU,CAAC,EAAE,2BAA2B,CAAC;IACzC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,cAAc,EAAE,MAAM,CAAC;IACvB,oBAAoB,EAAE,MAAM,CAAC;CAC9B;AAED,MAAM,WAAW,0BAA0B;IACzC,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,IAAI,EAAE,UAAU,CAAC;IACjB,oBAAoB,CAAC,EAAE,MAAM,CAAC;CAC/B;AAED,MAAM,WAAW,2BAA2B;IAC1C,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE;QAAE,MAAM,EAAE,WAAW,CAAC;QAAC,YAAY,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,0BAA0B,CAAC,CAAC;CACjH"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __copyProps = (to, from, except, desc) => {
|
|
7
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
8
|
+
for (let key of __getOwnPropNames(from))
|
|
9
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
10
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
11
|
+
}
|
|
12
|
+
return to;
|
|
13
|
+
};
|
|
14
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
15
|
+
var types_exports = {};
|
|
16
|
+
module.exports = __toCommonJS(types_exports);
|
|
17
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../src/lib/testing/storyboard/oauth-metadata-graph/types.ts"],"sourcesContent":["import type { HttpProbeResult } from '../types';\n\nexport type OAuthMetadataGraphErrorCode =\n | 'oauth_protected_resource_metadata_unavailable'\n | 'oauth_protected_resource_metadata_invalid'\n | 'oauth_resource_mismatch'\n | 'oauth_authorization_servers_empty'\n | 'oauth_authorization_server_url_invalid'\n | 'oauth_authorization_server_metadata_unavailable'\n | 'oauth_authorization_server_metadata_invalid'\n | 'oauth_issuer_mismatch'\n | 'oauth_endpoint_url_invalid'\n | 'oauth_endpoint_unreachable'\n | 'oauth_jwks_unavailable'\n | 'oauth_fetch_blocked'\n | 'oauth_graph_limit_exceeded';\n\nexport interface OAuthMetadataGraphObservation {\n kind:\n | 'protected_resource_metadata'\n | 'authorization_server_metadata'\n | 'authorization_endpoint'\n | 'token_endpoint'\n | 'jwks_uri';\n url: string;\n status: number;\n authorization_server_index?: number;\n}\n\nexport interface OAuthMetadataGraphFinding {\n code: OAuthMetadataGraphErrorCode;\n message: string;\n url?: string;\n field?: string;\n authorization_server_index?: number;\n}\n\nexport interface OAuthMetadataGraphGrade {\n success: boolean;\n protected_resource_url: string;\n protected_resource_result: HttpProbeResult;\n observations: OAuthMetadataGraphObservation[];\n findings: OAuthMetadataGraphFinding[];\n error_code?: OAuthMetadataGraphErrorCode;\n error?: string;\n total_requests: number;\n total_response_bytes: number;\n}\n\nexport interface OAuthMetadataFetchResponse {\n status: number;\n headers: Record<string, string>;\n body: Uint8Array;\n connectedPeerAddress?: string;\n}\n\nexport interface OAuthMetadataFetchTransport {\n fetch(url: string, options: { signal: AbortSignal; maxBodyBytes: number }): Promise<OAuthMetadataFetchResponse>;\n}\n"],"mappings":";;;;;;;;;;;;;;AAAA;AAAA;","names":[]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
//# sourceMappingURL=types.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { OAuthMetadataGraphGrade } from './types.mjs';
|
|
2
|
+
interface VectorResponse {
|
|
3
|
+
status?: number;
|
|
4
|
+
content_type?: string;
|
|
5
|
+
json?: unknown;
|
|
6
|
+
body?: string;
|
|
7
|
+
headers?: Record<string, string>;
|
|
8
|
+
network_error?: string;
|
|
9
|
+
connected_peer_address?: string;
|
|
10
|
+
}
|
|
11
|
+
interface VectorDnsFixture {
|
|
12
|
+
addresses?: string[];
|
|
13
|
+
fixture_address_classes?: string[];
|
|
14
|
+
}
|
|
15
|
+
export interface OAuthMetadataGraphVector {
|
|
16
|
+
id: string;
|
|
17
|
+
agent_url: string;
|
|
18
|
+
coverage?: string[];
|
|
19
|
+
responses: Record<string, VectorResponse>;
|
|
20
|
+
dns?: Record<string, VectorDnsFixture>;
|
|
21
|
+
expected_outcome: {
|
|
22
|
+
success: boolean;
|
|
23
|
+
error_code?: string;
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
export interface OAuthMetadataGraphVectorCorpus {
|
|
27
|
+
positive: OAuthMetadataGraphVector[];
|
|
28
|
+
negative: OAuthMetadataGraphVector[];
|
|
29
|
+
}
|
|
30
|
+
export declare function loadOAuthMetadataGraphVectors(file: string): OAuthMetadataGraphVectorCorpus;
|
|
31
|
+
export declare function gradeOAuthMetadataGraphVector(vector: OAuthMetadataGraphVector): Promise<OAuthMetadataGraphGrade>;
|
|
32
|
+
export {};
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { OAuthMetadataGraphGrade } from './types';
|
|
2
|
+
interface VectorResponse {
|
|
3
|
+
status?: number;
|
|
4
|
+
content_type?: string;
|
|
5
|
+
json?: unknown;
|
|
6
|
+
body?: string;
|
|
7
|
+
headers?: Record<string, string>;
|
|
8
|
+
network_error?: string;
|
|
9
|
+
connected_peer_address?: string;
|
|
10
|
+
}
|
|
11
|
+
interface VectorDnsFixture {
|
|
12
|
+
addresses?: string[];
|
|
13
|
+
fixture_address_classes?: string[];
|
|
14
|
+
}
|
|
15
|
+
export interface OAuthMetadataGraphVector {
|
|
16
|
+
id: string;
|
|
17
|
+
agent_url: string;
|
|
18
|
+
coverage?: string[];
|
|
19
|
+
responses: Record<string, VectorResponse>;
|
|
20
|
+
dns?: Record<string, VectorDnsFixture>;
|
|
21
|
+
expected_outcome: {
|
|
22
|
+
success: boolean;
|
|
23
|
+
error_code?: string;
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
export interface OAuthMetadataGraphVectorCorpus {
|
|
27
|
+
positive: OAuthMetadataGraphVector[];
|
|
28
|
+
negative: OAuthMetadataGraphVector[];
|
|
29
|
+
}
|
|
30
|
+
export declare function loadOAuthMetadataGraphVectors(file: string): OAuthMetadataGraphVectorCorpus;
|
|
31
|
+
export declare function gradeOAuthMetadataGraphVector(vector: OAuthMetadataGraphVector): Promise<OAuthMetadataGraphGrade>;
|
|
32
|
+
export {};
|
|
33
|
+
//# sourceMappingURL=vector-loader.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vector-loader.d.ts","sourceRoot":"","sources":["../../../../../src/lib/testing/storyboard/oauth-metadata-graph/vector-loader.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAA2D,uBAAuB,EAAE,MAAM,SAAS,CAAC;AAEhH,UAAU,cAAc;IACtB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,sBAAsB,CAAC,EAAE,MAAM,CAAC;CACjC;AAED,UAAU,gBAAgB;IACxB,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;IACrB,uBAAuB,CAAC,EAAE,MAAM,EAAE,CAAC;CACpC;AAED,MAAM,WAAW,wBAAwB;IACvC,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;IAC1C,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC;IACvC,gBAAgB,EAAE;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,UAAU,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;CAC7D;AAED,MAAM,WAAW,8BAA8B;IAC7C,QAAQ,EAAE,wBAAwB,EAAE,CAAC;IACrC,QAAQ,EAAE,wBAAwB,EAAE,CAAC;CACtC;AAED,wBAAgB,6BAA6B,CAAC,IAAI,EAAE,MAAM,GAAG,8BAA8B,CAM1F;AAED,wBAAsB,6BAA6B,CACjD,MAAM,EAAE,wBAAwB,GAC/B,OAAO,CAAC,uBAAuB,CAAC,CAGlC"}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
var vector_loader_exports = {};
|
|
20
|
+
__export(vector_loader_exports, {
|
|
21
|
+
gradeOAuthMetadataGraphVector: () => gradeOAuthMetadataGraphVector,
|
|
22
|
+
loadOAuthMetadataGraphVectors: () => loadOAuthMetadataGraphVectors
|
|
23
|
+
});
|
|
24
|
+
module.exports = __toCommonJS(vector_loader_exports);
|
|
25
|
+
var import_node_fs = require("node:fs");
|
|
26
|
+
var import_address_guards = require('../../../net/address-guards.js');
|
|
27
|
+
var import_ssrf_fetch = require('../../../net/ssrf-fetch.js');
|
|
28
|
+
var import_grader = require('./grader.js');
|
|
29
|
+
function loadOAuthMetadataGraphVectors(file) {
|
|
30
|
+
const parsed = JSON.parse((0, import_node_fs.readFileSync)(file, "utf8"));
|
|
31
|
+
if (!Array.isArray(parsed.positive) || !Array.isArray(parsed.negative)) {
|
|
32
|
+
throw new Error("OAuth metadata graph vector corpus must contain positive and negative arrays.");
|
|
33
|
+
}
|
|
34
|
+
return { positive: parsed.positive, negative: parsed.negative };
|
|
35
|
+
}
|
|
36
|
+
async function gradeOAuthMetadataGraphVector(vector) {
|
|
37
|
+
const transport = new VectorTransport(vector);
|
|
38
|
+
return (0, import_grader.gradeOAuthMetadataGraphWithTransport)(vector.agent_url, { transport });
|
|
39
|
+
}
|
|
40
|
+
class VectorTransport {
|
|
41
|
+
constructor(vector) {
|
|
42
|
+
this.vector = vector;
|
|
43
|
+
}
|
|
44
|
+
vector;
|
|
45
|
+
async fetch(url, options) {
|
|
46
|
+
if (options.signal.aborted) throw options.signal.reason;
|
|
47
|
+
this.enforceFixtureAddressPolicy(url);
|
|
48
|
+
const response = this.vector.responses[`GET ${url}`];
|
|
49
|
+
if (!response) throw new Error(`Offline vector has no response for GET ${url}`);
|
|
50
|
+
if (response.network_error) throw new Error(`Offline vector network error: ${response.network_error}`);
|
|
51
|
+
const bodyText = response.body ?? (response.json === void 0 ? "" : JSON.stringify(response.json));
|
|
52
|
+
const body = new TextEncoder().encode(bodyText);
|
|
53
|
+
if (body.byteLength > options.maxBodyBytes) {
|
|
54
|
+
throw new import_ssrf_fetch.SsrfRefusedError("body_exceeds_limit", "Offline vector response exceeds the response-byte limit.", {
|
|
55
|
+
url,
|
|
56
|
+
hostname: new URL(url).hostname
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
const headers = lowerCaseHeaders(response.headers ?? {});
|
|
60
|
+
if (response.content_type) headers["content-type"] = response.content_type;
|
|
61
|
+
return {
|
|
62
|
+
status: response.status ?? 0,
|
|
63
|
+
headers,
|
|
64
|
+
body,
|
|
65
|
+
...response.connected_peer_address && { connectedPeerAddress: response.connected_peer_address }
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
enforceFixtureAddressPolicy(value) {
|
|
69
|
+
const url = new URL(value);
|
|
70
|
+
const hostname = url.hostname.replace(/^\[|\]$/g, "");
|
|
71
|
+
if ((0, import_address_guards.isAlwaysBlocked)(hostname) || (0, import_address_guards.isPrivateIp)(hostname)) {
|
|
72
|
+
throw new import_ssrf_fetch.SsrfRefusedError("private_address", "Vector target is blocked by SSRF policy.", {
|
|
73
|
+
url: value,
|
|
74
|
+
hostname
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
const dns = this.vector.dns?.[hostname];
|
|
78
|
+
if (!dns?.addresses) return;
|
|
79
|
+
for (let index = 0; index < dns.addresses.length; index++) {
|
|
80
|
+
const fixtureClass = dns.fixture_address_classes?.[index];
|
|
81
|
+
if (fixtureClass && fixtureClass !== "globally_routable_fixture_only") {
|
|
82
|
+
throw new import_ssrf_fetch.SsrfRefusedError("private_address", "Vector DNS answer is blocked by SSRF policy.", {
|
|
83
|
+
url: value,
|
|
84
|
+
hostname
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
const address = dns.addresses[index];
|
|
88
|
+
if (!fixtureClass && ((0, import_address_guards.isAlwaysBlocked)(address) || (0, import_address_guards.isPrivateIp)(address))) {
|
|
89
|
+
throw new import_ssrf_fetch.SsrfRefusedError("private_address", "Vector DNS answer is blocked by SSRF policy.", {
|
|
90
|
+
url: value,
|
|
91
|
+
hostname
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
function lowerCaseHeaders(headers) {
|
|
98
|
+
return Object.fromEntries(Object.entries(headers).map(([key, value]) => [key.toLowerCase(), value]));
|
|
99
|
+
}
|
|
100
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
101
|
+
0 && (module.exports = {
|
|
102
|
+
gradeOAuthMetadataGraphVector,
|
|
103
|
+
loadOAuthMetadataGraphVectors
|
|
104
|
+
});
|
|
105
|
+
//# sourceMappingURL=vector-loader.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../src/lib/testing/storyboard/oauth-metadata-graph/vector-loader.ts"],"sourcesContent":["import { readFileSync } from 'node:fs';\nimport { isAlwaysBlocked, isPrivateIp } from '../../../net/address-guards';\nimport { SsrfRefusedError } from '../../../net/ssrf-fetch';\nimport { gradeOAuthMetadataGraphWithTransport } from './grader';\nimport type { OAuthMetadataFetchResponse, OAuthMetadataFetchTransport, OAuthMetadataGraphGrade } from './types';\n\ninterface VectorResponse {\n status?: number;\n content_type?: string;\n json?: unknown;\n body?: string;\n headers?: Record<string, string>;\n network_error?: string;\n connected_peer_address?: string;\n}\n\ninterface VectorDnsFixture {\n addresses?: string[];\n fixture_address_classes?: string[];\n}\n\nexport interface OAuthMetadataGraphVector {\n id: string;\n agent_url: string;\n coverage?: string[];\n responses: Record<string, VectorResponse>;\n dns?: Record<string, VectorDnsFixture>;\n expected_outcome: { success: boolean; error_code?: string };\n}\n\nexport interface OAuthMetadataGraphVectorCorpus {\n positive: OAuthMetadataGraphVector[];\n negative: OAuthMetadataGraphVector[];\n}\n\nexport function loadOAuthMetadataGraphVectors(file: string): OAuthMetadataGraphVectorCorpus {\n const parsed = JSON.parse(readFileSync(file, 'utf8')) as Partial<OAuthMetadataGraphVectorCorpus>;\n if (!Array.isArray(parsed.positive) || !Array.isArray(parsed.negative)) {\n throw new Error('OAuth metadata graph vector corpus must contain positive and negative arrays.');\n }\n return { positive: parsed.positive, negative: parsed.negative };\n}\n\nexport async function gradeOAuthMetadataGraphVector(\n vector: OAuthMetadataGraphVector\n): Promise<OAuthMetadataGraphGrade> {\n const transport = new VectorTransport(vector);\n return gradeOAuthMetadataGraphWithTransport(vector.agent_url, { transport });\n}\n\nclass VectorTransport implements OAuthMetadataFetchTransport {\n constructor(private readonly vector: OAuthMetadataGraphVector) {}\n\n async fetch(\n url: string,\n options: { signal: AbortSignal; maxBodyBytes: number }\n ): Promise<OAuthMetadataFetchResponse> {\n if (options.signal.aborted) throw options.signal.reason;\n this.enforceFixtureAddressPolicy(url);\n const response = this.vector.responses[`GET ${url}`];\n if (!response) throw new Error(`Offline vector has no response for GET ${url}`);\n if (response.network_error) throw new Error(`Offline vector network error: ${response.network_error}`);\n const bodyText = response.body ?? (response.json === undefined ? '' : JSON.stringify(response.json));\n const body = new TextEncoder().encode(bodyText);\n if (body.byteLength > options.maxBodyBytes) {\n throw new SsrfRefusedError('body_exceeds_limit', 'Offline vector response exceeds the response-byte limit.', {\n url,\n hostname: new URL(url).hostname,\n });\n }\n const headers = lowerCaseHeaders(response.headers ?? {});\n if (response.content_type) headers['content-type'] = response.content_type;\n return {\n status: response.status ?? 0,\n headers,\n body,\n ...(response.connected_peer_address && { connectedPeerAddress: response.connected_peer_address }),\n };\n }\n\n private enforceFixtureAddressPolicy(value: string): void {\n const url = new URL(value);\n const hostname = url.hostname.replace(/^\\[|\\]$/g, '');\n if (isAlwaysBlocked(hostname) || isPrivateIp(hostname)) {\n throw new SsrfRefusedError('private_address', 'Vector target is blocked by SSRF policy.', {\n url: value,\n hostname,\n });\n }\n const dns = this.vector.dns?.[hostname];\n if (!dns?.addresses) return;\n for (let index = 0; index < dns.addresses.length; index++) {\n const fixtureClass = dns.fixture_address_classes?.[index];\n if (fixtureClass && fixtureClass !== 'globally_routable_fixture_only') {\n throw new SsrfRefusedError('private_address', 'Vector DNS answer is blocked by SSRF policy.', {\n url: value,\n hostname,\n });\n }\n const address = dns.addresses[index]!;\n if (!fixtureClass && (isAlwaysBlocked(address) || isPrivateIp(address))) {\n throw new SsrfRefusedError('private_address', 'Vector DNS answer is blocked by SSRF policy.', {\n url: value,\n hostname,\n });\n }\n }\n }\n}\n\nfunction lowerCaseHeaders(headers: Record<string, string>): Record<string, string> {\n return Object.fromEntries(Object.entries(headers).map(([key, value]) => [key.toLowerCase(), value]));\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { readFileSync } from "node:fs";
|
|
2
|
+
import { isAlwaysBlocked, isPrivateIp } from "../../../net/address-guards.mjs";
|
|
3
|
+
import { SsrfRefusedError } from "../../../net/ssrf-fetch.mjs";
|
|
4
|
+
import { gradeOAuthMetadataGraphWithTransport } from "./grader.mjs";
|
|
5
|
+
function loadOAuthMetadataGraphVectors(file) {
|
|
6
|
+
const parsed = JSON.parse(readFileSync(file, "utf8"));
|
|
7
|
+
if (!Array.isArray(parsed.positive) || !Array.isArray(parsed.negative)) {
|
|
8
|
+
throw new Error("OAuth metadata graph vector corpus must contain positive and negative arrays.");
|
|
9
|
+
}
|
|
10
|
+
return { positive: parsed.positive, negative: parsed.negative };
|
|
11
|
+
}
|
|
12
|
+
async function gradeOAuthMetadataGraphVector(vector) {
|
|
13
|
+
const transport = new VectorTransport(vector);
|
|
14
|
+
return gradeOAuthMetadataGraphWithTransport(vector.agent_url, { transport });
|
|
15
|
+
}
|
|
16
|
+
class VectorTransport {
|
|
17
|
+
constructor(vector) {
|
|
18
|
+
this.vector = vector;
|
|
19
|
+
}
|
|
20
|
+
vector;
|
|
21
|
+
async fetch(url, options) {
|
|
22
|
+
if (options.signal.aborted) throw options.signal.reason;
|
|
23
|
+
this.enforceFixtureAddressPolicy(url);
|
|
24
|
+
const response = this.vector.responses[`GET ${url}`];
|
|
25
|
+
if (!response) throw new Error(`Offline vector has no response for GET ${url}`);
|
|
26
|
+
if (response.network_error) throw new Error(`Offline vector network error: ${response.network_error}`);
|
|
27
|
+
const bodyText = response.body ?? (response.json === void 0 ? "" : JSON.stringify(response.json));
|
|
28
|
+
const body = new TextEncoder().encode(bodyText);
|
|
29
|
+
if (body.byteLength > options.maxBodyBytes) {
|
|
30
|
+
throw new SsrfRefusedError("body_exceeds_limit", "Offline vector response exceeds the response-byte limit.", {
|
|
31
|
+
url,
|
|
32
|
+
hostname: new URL(url).hostname
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
const headers = lowerCaseHeaders(response.headers ?? {});
|
|
36
|
+
if (response.content_type) headers["content-type"] = response.content_type;
|
|
37
|
+
return {
|
|
38
|
+
status: response.status ?? 0,
|
|
39
|
+
headers,
|
|
40
|
+
body,
|
|
41
|
+
...response.connected_peer_address && { connectedPeerAddress: response.connected_peer_address }
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
enforceFixtureAddressPolicy(value) {
|
|
45
|
+
const url = new URL(value);
|
|
46
|
+
const hostname = url.hostname.replace(/^\[|\]$/g, "");
|
|
47
|
+
if (isAlwaysBlocked(hostname) || isPrivateIp(hostname)) {
|
|
48
|
+
throw new SsrfRefusedError("private_address", "Vector target is blocked by SSRF policy.", {
|
|
49
|
+
url: value,
|
|
50
|
+
hostname
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
const dns = this.vector.dns?.[hostname];
|
|
54
|
+
if (!dns?.addresses) return;
|
|
55
|
+
for (let index = 0; index < dns.addresses.length; index++) {
|
|
56
|
+
const fixtureClass = dns.fixture_address_classes?.[index];
|
|
57
|
+
if (fixtureClass && fixtureClass !== "globally_routable_fixture_only") {
|
|
58
|
+
throw new SsrfRefusedError("private_address", "Vector DNS answer is blocked by SSRF policy.", {
|
|
59
|
+
url: value,
|
|
60
|
+
hostname
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
const address = dns.addresses[index];
|
|
64
|
+
if (!fixtureClass && (isAlwaysBlocked(address) || isPrivateIp(address))) {
|
|
65
|
+
throw new SsrfRefusedError("private_address", "Vector DNS answer is blocked by SSRF policy.", {
|
|
66
|
+
url: value,
|
|
67
|
+
hostname
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
function lowerCaseHeaders(headers) {
|
|
74
|
+
return Object.fromEntries(Object.entries(headers).map(([key, value]) => [key.toLowerCase(), value]));
|
|
75
|
+
}
|
|
76
|
+
export {
|
|
77
|
+
gradeOAuthMetadataGraphVector,
|
|
78
|
+
loadOAuthMetadataGraphVectors
|
|
79
|
+
};
|
|
80
|
+
//# sourceMappingURL=vector-loader.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../src/lib/testing/storyboard/oauth-metadata-graph/vector-loader.ts"],"sourcesContent":["import { readFileSync } from 'node:fs';\nimport { isAlwaysBlocked, isPrivateIp } from '../../../net/address-guards';\nimport { SsrfRefusedError } from '../../../net/ssrf-fetch';\nimport { gradeOAuthMetadataGraphWithTransport } from './grader';\nimport type { OAuthMetadataFetchResponse, OAuthMetadataFetchTransport, OAuthMetadataGraphGrade } from './types';\n\ninterface VectorResponse {\n status?: number;\n content_type?: string;\n json?: unknown;\n body?: string;\n headers?: Record<string, string>;\n network_error?: string;\n connected_peer_address?: string;\n}\n\ninterface VectorDnsFixture {\n addresses?: string[];\n fixture_address_classes?: string[];\n}\n\nexport interface OAuthMetadataGraphVector {\n id: string;\n agent_url: string;\n coverage?: string[];\n responses: Record<string, VectorResponse>;\n dns?: Record<string, VectorDnsFixture>;\n expected_outcome: { success: boolean; error_code?: string };\n}\n\nexport interface OAuthMetadataGraphVectorCorpus {\n positive: OAuthMetadataGraphVector[];\n negative: OAuthMetadataGraphVector[];\n}\n\nexport function loadOAuthMetadataGraphVectors(file: string): OAuthMetadataGraphVectorCorpus {\n const parsed = JSON.parse(readFileSync(file, 'utf8')) as Partial<OAuthMetadataGraphVectorCorpus>;\n if (!Array.isArray(parsed.positive) || !Array.isArray(parsed.negative)) {\n throw new Error('OAuth metadata graph vector corpus must contain positive and negative arrays.');\n }\n return { positive: parsed.positive, negative: parsed.negative };\n}\n\nexport async function gradeOAuthMetadataGraphVector(\n vector: OAuthMetadataGraphVector\n): Promise<OAuthMetadataGraphGrade> {\n const transport = new VectorTransport(vector);\n return gradeOAuthMetadataGraphWithTransport(vector.agent_url, { transport });\n}\n\nclass VectorTransport implements OAuthMetadataFetchTransport {\n constructor(private readonly vector: OAuthMetadataGraphVector) {}\n\n async fetch(\n url: string,\n options: { signal: AbortSignal; maxBodyBytes: number }\n ): Promise<OAuthMetadataFetchResponse> {\n if (options.signal.aborted) throw options.signal.reason;\n this.enforceFixtureAddressPolicy(url);\n const response = this.vector.responses[`GET ${url}`];\n if (!response) throw new Error(`Offline vector has no response for GET ${url}`);\n if (response.network_error) throw new Error(`Offline vector network error: ${response.network_error}`);\n const bodyText = response.body ?? (response.json === undefined ? '' : JSON.stringify(response.json));\n const body = new TextEncoder().encode(bodyText);\n if (body.byteLength > options.maxBodyBytes) {\n throw new SsrfRefusedError('body_exceeds_limit', 'Offline vector response exceeds the response-byte limit.', {\n url,\n hostname: new URL(url).hostname,\n });\n }\n const headers = lowerCaseHeaders(response.headers ?? {});\n if (response.content_type) headers['content-type'] = response.content_type;\n return {\n status: response.status ?? 0,\n headers,\n body,\n ...(response.connected_peer_address && { connectedPeerAddress: response.connected_peer_address }),\n };\n }\n\n private enforceFixtureAddressPolicy(value: string): void {\n const url = new URL(value);\n const hostname = url.hostname.replace(/^\\[|\\]$/g, '');\n if (isAlwaysBlocked(hostname) || isPrivateIp(hostname)) {\n throw new SsrfRefusedError('private_address', 'Vector target is blocked by SSRF policy.', {\n url: value,\n hostname,\n });\n }\n const dns = this.vector.dns?.[hostname];\n if (!dns?.addresses) return;\n for (let index = 0; index < dns.addresses.length; index++) {\n const fixtureClass = dns.fixture_address_classes?.[index];\n if (fixtureClass && fixtureClass !== 'globally_routable_fixture_only') {\n throw new SsrfRefusedError('private_address', 'Vector DNS answer is blocked by SSRF policy.', {\n url: value,\n hostname,\n });\n }\n const address = dns.addresses[index]!;\n if (!fixtureClass && (isAlwaysBlocked(address) || isPrivateIp(address))) {\n throw new SsrfRefusedError('private_address', 'Vector DNS answer is blocked by SSRF policy.', {\n url: value,\n hostname,\n });\n }\n }\n }\n}\n\nfunction lowerCaseHeaders(headers: Record<string, string>): Record<string, string> {\n return Object.fromEntries(Object.entries(headers).map(([key, value]) => [key.toLowerCase(), value]));\n}\n"],"mappings":"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","names":[]}
|