@cybedefend/vibedefend 1.5.0 → 1.6.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.
Files changed (246) hide show
  1. package/README.md +67 -2
  2. package/dist/api/gateway-request.js +49 -0
  3. package/dist/api/gateway-request.js.map +1 -0
  4. package/dist/api/projects.js +67 -0
  5. package/dist/api/projects.js.map +1 -1
  6. package/dist/api/repository-link.js +157 -0
  7. package/dist/api/repository-link.js.map +1 -0
  8. package/dist/auth/auth-store.js +107 -25
  9. package/dist/auth/auth-store.js.map +1 -1
  10. package/dist/auth/auth.js +21 -0
  11. package/dist/auth/auth.js.map +1 -1
  12. package/dist/clients/cursor.js +5 -1
  13. package/dist/clients/cursor.js.map +1 -1
  14. package/dist/clients/vscode.js +38 -29
  15. package/dist/clients/vscode.js.map +1 -1
  16. package/dist/commands/mcp/allow.js +219 -0
  17. package/dist/commands/mcp/allow.js.map +1 -0
  18. package/dist/commands/mcp/connect.js +337 -0
  19. package/dist/commands/mcp/connect.js.map +1 -0
  20. package/dist/commands/mcp/index.js +170 -0
  21. package/dist/commands/mcp/index.js.map +1 -0
  22. package/dist/commands/mcp/proxy.js +264 -0
  23. package/dist/commands/mcp/proxy.js.map +1 -0
  24. package/dist/commands/mcp/scan.js +349 -0
  25. package/dist/commands/mcp/scan.js.map +1 -0
  26. package/dist/commands/mcp/sync.js +215 -0
  27. package/dist/commands/mcp/sync.js.map +1 -0
  28. package/dist/config.js +6 -0
  29. package/dist/config.js.map +1 -1
  30. package/dist/diagnostics.js +78 -12
  31. package/dist/diagnostics.js.map +1 -1
  32. package/dist/doctor.js +19 -0
  33. package/dist/doctor.js.map +1 -1
  34. package/dist/guards-evaluator/analysis-types.js +27 -0
  35. package/dist/guards-evaluator/analysis-types.js.map +1 -0
  36. package/dist/guards-evaluator/command-analysis.js +572 -0
  37. package/dist/guards-evaluator/command-analysis.js.map +1 -0
  38. package/dist/guards-evaluator/command-view.js +586 -0
  39. package/dist/guards-evaluator/command-view.js.map +1 -0
  40. package/dist/guards-evaluator/env.js +11 -0
  41. package/dist/guards-evaluator/env.js.map +1 -0
  42. package/dist/guards-evaluator/evaluate.js +91 -41
  43. package/dist/guards-evaluator/evaluate.js.map +1 -1
  44. package/dist/guards-evaluator/index.js +2 -1
  45. package/dist/guards-evaluator/index.js.map +1 -1
  46. package/dist/guards-evaluator/inline-code.js +259 -0
  47. package/dist/guards-evaluator/inline-code.js.map +1 -0
  48. package/dist/guards-evaluator/normalize.js +716 -0
  49. package/dist/guards-evaluator/normalize.js.map +1 -0
  50. package/dist/guards-evaluator/preprocess.js +13 -5
  51. package/dist/guards-evaluator/preprocess.js.map +1 -1
  52. package/dist/guards-evaluator/redact.js +19 -0
  53. package/dist/guards-evaluator/redact.js.map +1 -1
  54. package/dist/guards-evaluator/regex.js +25 -3
  55. package/dist/guards-evaluator/regex.js.map +1 -1
  56. package/dist/guards-evaluator/safe-commands.js +155 -0
  57. package/dist/guards-evaluator/safe-commands.js.map +1 -0
  58. package/dist/guards-evaluator/triage.js +315 -0
  59. package/dist/guards-evaluator/triage.js.map +1 -0
  60. package/dist/hook-runner.js +24679 -5079
  61. package/dist/hooks/install.js +38 -0
  62. package/dist/hooks/install.js.map +1 -1
  63. package/dist/hooks/runtime/config.js +38 -0
  64. package/dist/hooks/runtime/config.js.map +1 -1
  65. package/dist/hooks/runtime/emit.js +94 -61
  66. package/dist/hooks/runtime/emit.js.map +1 -1
  67. package/dist/hooks/runtime/fetch-rules.js +22 -7
  68. package/dist/hooks/runtime/fetch-rules.js.map +1 -1
  69. package/dist/hooks/runtime/gateway-http-error.js +34 -0
  70. package/dist/hooks/runtime/gateway-http-error.js.map +1 -0
  71. package/dist/hooks/runtime/guard-rules-cache.js +139 -17
  72. package/dist/hooks/runtime/guard-rules-cache.js.map +1 -1
  73. package/dist/hooks/runtime/guard-violations-buffer.js +321 -48
  74. package/dist/hooks/runtime/guard-violations-buffer.js.map +1 -1
  75. package/dist/hooks/runtime/judge-cache.js +6 -0
  76. package/dist/hooks/runtime/judge-cache.js.map +1 -1
  77. package/dist/hooks/runtime/judge-triage.js +193 -0
  78. package/dist/hooks/runtime/judge-triage.js.map +1 -0
  79. package/dist/hooks/runtime/judge.js +7 -1
  80. package/dist/hooks/runtime/judge.js.map +1 -1
  81. package/dist/hooks/runtime/pre-compact.js +12 -6
  82. package/dist/hooks/runtime/pre-compact.js.map +1 -1
  83. package/dist/hooks/runtime/resolve.js +90 -8
  84. package/dist/hooks/runtime/resolve.js.map +1 -1
  85. package/dist/hooks/runtime/retry-policy.js +57 -0
  86. package/dist/hooks/runtime/retry-policy.js.map +1 -0
  87. package/dist/hooks/runtime/review-sessions.js +74 -0
  88. package/dist/hooks/runtime/review-sessions.js.map +1 -0
  89. package/dist/hooks/runtime/script-resolve.js +695 -0
  90. package/dist/hooks/runtime/script-resolve.js.map +1 -0
  91. package/dist/hooks/runtime/session-activation.js +5 -1
  92. package/dist/hooks/runtime/session-activation.js.map +1 -1
  93. package/dist/hooks/runtime/session-review.js +163 -45
  94. package/dist/hooks/runtime/session-review.js.map +1 -1
  95. package/dist/hooks/runtime/session-start.js +2 -1
  96. package/dist/hooks/runtime/session-start.js.map +1 -1
  97. package/dist/hooks/runtime/session-writes.js +232 -0
  98. package/dist/hooks/runtime/session-writes.js.map +1 -0
  99. package/dist/hooks/runtime/sniff.js +26 -7
  100. package/dist/hooks/runtime/sniff.js.map +1 -1
  101. package/dist/hooks/runtime/target-resolve.js +336 -0
  102. package/dist/hooks/runtime/target-resolve.js.map +1 -0
  103. package/dist/hooks/runtime/unmapped-tools.js +93 -0
  104. package/dist/hooks/runtime/unmapped-tools.js.map +1 -0
  105. package/dist/hooks/runtime/usage-block.js +4 -0
  106. package/dist/hooks/runtime/usage-block.js.map +1 -1
  107. package/dist/hooks/runtime/user-prompt-submit.js +2 -1
  108. package/dist/hooks/runtime/user-prompt-submit.js.map +1 -1
  109. package/dist/hooks/runtime/wire-text.js +41 -0
  110. package/dist/hooks/runtime/wire-text.js.map +1 -0
  111. package/dist/hooks/runtime/work-dir.js +84 -0
  112. package/dist/hooks/runtime/work-dir.js.map +1 -0
  113. package/dist/index.js +45 -12
  114. package/dist/index.js.map +1 -1
  115. package/dist/install.js +174 -11
  116. package/dist/install.js.map +1 -1
  117. package/dist/link.js +74 -48
  118. package/dist/link.js.map +1 -1
  119. package/dist/login.js +77 -11
  120. package/dist/login.js.map +1 -1
  121. package/dist/mcp/auth/agent-sign-ins.js +174 -0
  122. package/dist/mcp/auth/agent-sign-ins.js.map +1 -0
  123. package/dist/mcp/auth/callback.js +200 -0
  124. package/dist/mcp/auth/callback.js.map +1 -0
  125. package/dist/mcp/auth/connect.js +392 -0
  126. package/dist/mcp/auth/connect.js.map +1 -0
  127. package/dist/mcp/auth/discovery.js +399 -0
  128. package/dist/mcp/auth/discovery.js.map +1 -0
  129. package/dist/mcp/auth/oauth.js +326 -0
  130. package/dist/mcp/auth/oauth.js.map +1 -0
  131. package/dist/mcp/auth/probe.js +128 -0
  132. package/dist/mcp/auth/probe.js.map +1 -0
  133. package/dist/mcp/auth/secrets.js +446 -0
  134. package/dist/mcp/auth/secrets.js.map +1 -0
  135. package/dist/mcp/auth/store.js +260 -0
  136. package/dist/mcp/auth/store.js.map +1 -0
  137. package/dist/mcp/auth/tokenSource.js +55 -0
  138. package/dist/mcp/auth/tokenSource.js.map +1 -0
  139. package/dist/mcp/config/atomicWrite.js +98 -0
  140. package/dist/mcp/config/atomicWrite.js.map +1 -0
  141. package/dist/mcp/config/clients.js +171 -0
  142. package/dist/mcp/config/clients.js.map +1 -0
  143. package/dist/mcp/config/cybedefend-servers.js +48 -0
  144. package/dist/mcp/config/cybedefend-servers.js.map +1 -0
  145. package/dist/mcp/config/discover.js +114 -0
  146. package/dist/mcp/config/discover.js.map +1 -0
  147. package/dist/mcp/config/hook.js +123 -0
  148. package/dist/mcp/config/hook.js.map +1 -0
  149. package/dist/mcp/config/index.js +11 -0
  150. package/dist/mcp/config/index.js.map +1 -0
  151. package/dist/mcp/config/install.js +132 -0
  152. package/dist/mcp/config/install.js.map +1 -0
  153. package/dist/mcp/config/installed.js +37 -0
  154. package/dist/mcp/config/installed.js.map +1 -0
  155. package/dist/mcp/config/jsonEdit.js +119 -0
  156. package/dist/mcp/config/jsonEdit.js.map +1 -0
  157. package/dist/mcp/config/lock.js +102 -0
  158. package/dist/mcp/config/lock.js.map +1 -0
  159. package/dist/mcp/config/proxyBinary.js +211 -0
  160. package/dist/mcp/config/proxyBinary.js.map +1 -0
  161. package/dist/mcp/config/rewrite.js +388 -0
  162. package/dist/mcp/config/rewrite.js.map +1 -0
  163. package/dist/mcp/config/types.js +34 -0
  164. package/dist/mcp/config/types.js.map +1 -0
  165. package/dist/mcp/proxy/audit.js +83 -0
  166. package/dist/mcp/proxy/audit.js.map +1 -0
  167. package/dist/mcp/proxy/authInterceptor.js +119 -0
  168. package/dist/mcp/proxy/authInterceptor.js.map +1 -0
  169. package/dist/mcp/proxy/expand.js +93 -0
  170. package/dist/mcp/proxy/expand.js.map +1 -0
  171. package/dist/mcp/proxy/inject.js +397 -0
  172. package/dist/mcp/proxy/inject.js.map +1 -0
  173. package/dist/mcp/proxy/intercept.js +37 -0
  174. package/dist/mcp/proxy/intercept.js.map +1 -0
  175. package/dist/mcp/proxy/jsonrpc.js +153 -0
  176. package/dist/mcp/proxy/jsonrpc.js.map +1 -0
  177. package/dist/mcp/proxy/log.js +109 -0
  178. package/dist/mcp/proxy/log.js.map +1 -0
  179. package/dist/mcp/proxy/paths.js +53 -0
  180. package/dist/mcp/proxy/paths.js.map +1 -0
  181. package/dist/mcp/proxy/proxy.js +510 -0
  182. package/dist/mcp/proxy/proxy.js.map +1 -0
  183. package/dist/mcp/proxy/spec.js +203 -0
  184. package/dist/mcp/proxy/spec.js.map +1 -0
  185. package/dist/mcp/proxy/sse.js +77 -0
  186. package/dist/mcp/proxy/sse.js.map +1 -0
  187. package/dist/mcp/proxy/upstream.js +2 -0
  188. package/dist/mcp/proxy/upstream.js.map +1 -0
  189. package/dist/mcp/proxy/upstreamHttp.js +499 -0
  190. package/dist/mcp/proxy/upstreamHttp.js.map +1 -0
  191. package/dist/mcp/proxy/upstreamStdio.js +188 -0
  192. package/dist/mcp/proxy/upstreamStdio.js.map +1 -0
  193. package/dist/mcp/proxy/upstreams.js +143 -0
  194. package/dist/mcp/proxy/upstreams.js.map +1 -0
  195. package/dist/mcp/proxy/verifyInterceptor.js +559 -0
  196. package/dist/mcp/proxy/verifyInterceptor.js.map +1 -0
  197. package/dist/mcp/proxy/windowsCommand.js +99 -0
  198. package/dist/mcp/proxy/windowsCommand.js.map +1 -0
  199. package/dist/mcp/scan/args.js +228 -0
  200. package/dist/mcp/scan/args.js.map +1 -0
  201. package/dist/mcp/scan/results.js +253 -0
  202. package/dist/mcp/scan/results.js.map +1 -0
  203. package/dist/mcp/scan/types.js +40 -0
  204. package/dist/mcp/scan/types.js.map +1 -0
  205. package/dist/mcp/verify/api.js +475 -0
  206. package/dist/mcp/verify/api.js.map +1 -0
  207. package/dist/mcp/verify/backend.js +143 -0
  208. package/dist/mcp/verify/backend.js.map +1 -0
  209. package/dist/mcp/verify/cache.js +182 -0
  210. package/dist/mcp/verify/cache.js.map +1 -0
  211. package/dist/mcp/verify/contract-errors.js +198 -0
  212. package/dist/mcp/verify/contract-errors.js.map +1 -0
  213. package/dist/mcp/verify/digest.js +91 -0
  214. package/dist/mcp/verify/digest.js.map +1 -0
  215. package/dist/mcp/verify/factory.js +56 -0
  216. package/dist/mcp/verify/factory.js.map +1 -0
  217. package/dist/mcp/verify/identity.js +173 -0
  218. package/dist/mcp/verify/identity.js.map +1 -0
  219. package/dist/mcp/verify/policy-store.js +63 -0
  220. package/dist/mcp/verify/policy-store.js.map +1 -0
  221. package/dist/mcp/verify/policy.js +158 -0
  222. package/dist/mcp/verify/policy.js.map +1 -0
  223. package/dist/mcp/verify/pool.js +27 -0
  224. package/dist/mcp/verify/pool.js.map +1 -0
  225. package/dist/mcp/verify/token-refresh.js +44 -0
  226. package/dist/mcp/verify/token-refresh.js.map +1 -0
  227. package/dist/mcp/verify/token.js +67 -0
  228. package/dist/mcp/verify/token.js.map +1 -0
  229. package/dist/mcp/verify/verifier.js +1009 -0
  230. package/dist/mcp/verify/verifier.js.map +1 -0
  231. package/dist/org-gate.js +1 -1
  232. package/dist/org-gate.js.map +1 -1
  233. package/dist/org.js +146 -0
  234. package/dist/org.js.map +1 -0
  235. package/dist/project-create.js +129 -0
  236. package/dist/project-create.js.map +1 -0
  237. package/dist/prompts.js +21 -0
  238. package/dist/prompts.js.map +1 -1
  239. package/dist/re2.wasm +0 -0
  240. package/dist/status.js +61 -0
  241. package/dist/status.js.map +1 -1
  242. package/dist/uninstall.js +49 -4
  243. package/dist/uninstall.js.map +1 -1
  244. package/dist/utils.js +62 -6
  245. package/dist/utils.js.map +1 -1
  246. package/package.json +10 -3
@@ -0,0 +1,399 @@
1
+ /**
2
+ * OAuth discovery for a third-party MCP server — MCP 2025-06-18, RFC 9728
3
+ * (protected resource metadata), RFC 8414 (authorization server metadata).
4
+ *
5
+ * ─── Why ────────────────────────────────────────────────────────────────────
6
+ *
7
+ * A third-party server's OAuth token belongs to whoever obtained it, not to
8
+ * VibeDefend. There is no shortcut: to talk to an auth-gated server the proxy
9
+ * has to authenticate as itself. The good news is that the spec makes the
10
+ * discovery automatic — the server refuses (401, or 403) and says where its
11
+ * metadata lives, and from there we climb to the endpoints. With no such
12
+ * pointer, the well-known path is derived from the server URL (RFC 9728
13
+ * §3.1). The user types nothing: no URL, no client id.
14
+ *
15
+ * ─── What is refused ────────────────────────────────────────────────────────
16
+ *
17
+ * Everything here is dictated by the server being asked. Four requirements,
18
+ * or we give up:
19
+ * • HTTPS everywhere — a cleartext endpoint would expose the whole
20
+ * authorization flow. The single exception is a loopback address, which
21
+ * no network attacker can reach and which is how a local authorization
22
+ * server (a test one, or a self-hosted gateway) is reachable at all;
23
+ * • PKCE S256 advertised — the ONLY protection the authorization code has
24
+ * for a public client like ours (see `oauth.ts`); never a silent
25
+ * downgrade to `plain`;
26
+ * • the protected resource (PRM) and the authorization server's issuer must
27
+ * DESIGNATE THE SERVER BEING ASKED (RFC 9728 §3.3, RFC 8414 §3.3) — see
28
+ * the mix-up section below;
29
+ * • no redirect followed on OUR OWN requests — a 3xx would move the request
30
+ * to an origin we did not choose to talk to.
31
+ *
32
+ * ─── The mix-up (confused deputy), and why the binding is vital ─────────────
33
+ *
34
+ * The server chooses its metadata URL, the issuer they announce and the
35
+ * resource they name. Unchecked, `evil.example` could return a challenge
36
+ * pointing at ANOTHER service's metadata (Linear, say): VibeDefend would
37
+ * climb that third party's AUTHENTIC chain, open its real consent page — which
38
+ * the user would approve in good faith — exchange the code there, and file a
39
+ * REAL third-party token under `evil.example`'s identity. The next request
40
+ * would then present it as `Authorization: Bearer`… to `evil.example`. A
41
+ * `.mcp.json` in a cloned repository is enough to pick that server, which is
42
+ * this file's reason to exist.
43
+ *
44
+ * Hence: the metadata URL must be on the server's origin, the `resource` they
45
+ * announce must canonicalise to the server, and the authorization server's
46
+ * `issuer` must designate itself (RFC 8414 §3.3).
47
+ *
48
+ * An accepted limit: a server may legitimately delegate its authorization to
49
+ * a third-party issuer, so we cannot require the `issuer` to be on the
50
+ * server's origin. What remains is the `resource` parameter (RFC 8707), which
51
+ * the exchange (`oauth.ts`) ALWAYS sets to the server being asked: a
52
+ * conforming issuer refuses a resource that is not its own
53
+ * (`invalid_target`). That last line of defence depends on the third party,
54
+ * not on us.
55
+ *
56
+ * ─── A check that was tried and removed, deliberately ───────────────────────
57
+ *
58
+ * Binding `authorization_endpoint` / `token_endpoint` /
59
+ * `registration_endpoint` to the issuer's origin looks attractive and is not
60
+ * done, for two reasons that together condemn it:
61
+ *
62
+ * 1. It closes the INSTANCE, not the CLASS — the attacker simply hosts a
63
+ * 302 on their OWN `authorization_endpoint`: the browser lands on the
64
+ * real consent page of a genuine third party, with THEIR `client_id` and
65
+ * OUR `code_challenge`. The check structurally cannot see it: that
66
+ * endpoint opens in the BROWSER, we never fetch it, and `redirect:
67
+ * 'manual'` does not apply to it.
68
+ * 2. It refuses PERFECTLY CONFORMING deployments: AWS Cognito (issuer
69
+ * `cognito-idp.<region>.amazonaws.com/<pool>`, endpoints
70
+ * `<domain>.auth.<region>.amazoncognito.com` — the pattern AWS
71
+ * recommends for MCP) and Google (`accounts.google.com` /
72
+ * `oauth2.googleapis.com`) would come back `unusable`. Neither RFC 8414
73
+ * nor the MCP spec requires co-origin, and `amazonaws.com` /
74
+ * `amazoncognito.com` are two distinct registrable domains — no
75
+ * loosening ("same parent domain") saves the rule without emptying it.
76
+ *
77
+ * The structural difference with the PRM ↔ server binding, which IS kept: that
78
+ * one anchors on a TRUSTED value, the URL the USER chose (`serverUrl`). The
79
+ * removed check anchored on a value CHOSEN BY THE ATTACKER — the issuer they
80
+ * designate themselves — which they only have to be self-consistent to
81
+ * satisfy.
82
+ *
83
+ * What closes the class: `state` (anti-CSRF) plus `iss` (RFC 9207, the issuer
84
+ * announced in the authorization response itself, compared to the expected
85
+ * one), both enforced by `callback.ts` on the BROWSER's response rather than
86
+ * on a document the attacker controls.
87
+ */
88
+ import { pkg } from '../../version.js';
89
+ import { canonicalMcpUrl } from './oauth.js';
90
+ const DEFAULT_TIMEOUT_MS = 10_000;
91
+ /**
92
+ * Common options for every discovery request.
93
+ *
94
+ * `redirect: 'manual'` is not cosmetic: without it a 3xx would be enough to
95
+ * have us read metadata on an origin we never chose to ask, and the checks
96
+ * below would be about the URL REQUESTED rather than the one that answered.
97
+ */
98
+ function requestInit(signal, extra = {}) {
99
+ return { redirect: 'manual', signal, ...extra };
100
+ }
101
+ const LOOPBACK_HOSTS = new Set(['127.0.0.1', 'localhost', '::1', '[::1]']);
102
+ /**
103
+ * A usable URL, or `null`.
104
+ *
105
+ * https, or http on a loopback address. The loopback exception is RFC 8252's
106
+ * reasoning applied to the other side of the flow: a listener on 127.0.0.1 is
107
+ * not reachable by a network attacker, so cleartext buys them nothing, and
108
+ * refusing it would make a self-hosted or local authorization server
109
+ * impossible to talk to — including the mock one the tests run against.
110
+ */
111
+ function usableUrl(value) {
112
+ if (typeof value !== 'string' || !value.trim())
113
+ return null;
114
+ let parsed;
115
+ try {
116
+ parsed = new URL(value);
117
+ }
118
+ catch {
119
+ return null;
120
+ }
121
+ if (parsed.protocol === 'https:')
122
+ return value;
123
+ if (parsed.protocol === 'http:' && LOOPBACK_HOSTS.has(parsed.hostname.toLowerCase())) {
124
+ return value;
125
+ }
126
+ return null;
127
+ }
128
+ /** `scheme//host[:port]` of the server, or `null`. */
129
+ function mcpOrigin(url) {
130
+ const canonical = canonicalMcpUrl(url);
131
+ if (!canonical)
132
+ return null;
133
+ const pathStart = canonical.indexOf('/', canonical.indexOf('//') + 2);
134
+ return pathStart === -1 ? canonical : canonical.slice(0, pathStart);
135
+ }
136
+ /** Do two URLs designate the SAME MCP server? */
137
+ function sameMcpServer(a, b) {
138
+ const ca = canonicalMcpUrl(a);
139
+ return ca !== null && ca === canonicalMcpUrl(b);
140
+ }
141
+ /** A comparable issuer identifier — RFC 8414 tolerates the trailing slash. */
142
+ function issuerId(value) {
143
+ const url = usableUrl(value);
144
+ return url ? url.replace(/\/+$/, '') : null;
145
+ }
146
+ /**
147
+ * The metadata URL announced by the `WWW-Authenticate` challenge, or `null`.
148
+ *
149
+ * The challenge comes from the server being asked, so two requirements:
150
+ * • a usable scheme — following an `http://` on a public host would expose
151
+ * the rest of the flow to anyone on the path;
152
+ * • the SAME ORIGIN as `serverUrl` — a protected resource's metadata is read
153
+ * from IT (RFC 9728 §3). A server pointing them at a third party is
154
+ * trying to make us go looking for that third party's token; see the
155
+ * mix-up section at the top of this file.
156
+ *
157
+ * `serverUrl` is not optional: without it a caller could forget the binding,
158
+ * which is exactly what made the mix-up possible.
159
+ */
160
+ export function parseResourceMetadataUrl(header, serverUrl) {
161
+ if (!header)
162
+ return null;
163
+ const m = /resource_metadata\s*=\s*"?([^",\s]+)"?/i.exec(header);
164
+ const url = m ? usableUrl(m[1]) : null;
165
+ if (!url)
166
+ return null;
167
+ const expected = mcpOrigin(serverUrl);
168
+ return expected && mcpOrigin(url) === expected ? url : null;
169
+ }
170
+ /**
171
+ * The well-known path derived from the server URL (RFC 9728 §3.1), or `null`.
172
+ *
173
+ * A server may refuse without carrying a `WWW-Authenticate`; no RFC makes it
174
+ * mandatory. This path is derived from the server ASKED, so it is bound to it
175
+ * by construction and does not reopen the door the origin check just closed.
176
+ */
177
+ function derivedResourceMetadataUrl(serverUrl) {
178
+ const canonical = canonicalMcpUrl(serverUrl);
179
+ const origin = mcpOrigin(serverUrl);
180
+ if (!canonical || !origin || !usableUrl(origin))
181
+ return null;
182
+ // `https://host/mcp` → `https://host/.well-known/oauth-protected-resource/mcp`
183
+ return `${origin}/.well-known/oauth-protected-resource${canonical.slice(origin.length)}`;
184
+ }
185
+ /**
186
+ * Every place an authorization server may publish its metadata, in the order
187
+ * they are tried.
188
+ *
189
+ * RFC 8414 §3.1 inserts the well-known segment BETWEEN the host and the
190
+ * issuer's path (`https://host/.well-known/oauth-authorization-server/tenant`),
191
+ * while OpenID Connect Discovery appends it (`https://host/tenant/.well-known/
192
+ * openid-configuration`). Real deployments implement one, the other, or both:
193
+ * Auth0 and Okta answer the OIDC form, Keycloak answers the RFC 8414 form
194
+ * under its realm path, and an AS with no path at all answers either. Trying
195
+ * a single shape is why a conforming tenant-scoped issuer came back
196
+ * `unusable`.
197
+ */
198
+ export function authorizationServerMetadataUrls(issuer) {
199
+ let parsed;
200
+ try {
201
+ parsed = new URL(issuer);
202
+ }
203
+ catch {
204
+ return [];
205
+ }
206
+ const origin = `${parsed.protocol}//${parsed.host}`;
207
+ const path = parsed.pathname.replace(/\/+$/, '');
208
+ if (path === '') {
209
+ return [
210
+ `${origin}/.well-known/oauth-authorization-server`,
211
+ `${origin}/.well-known/openid-configuration`,
212
+ ];
213
+ }
214
+ return [
215
+ `${origin}/.well-known/oauth-authorization-server${path}`,
216
+ // The appended form is not in RFC 8414, but it is what earlier clients
217
+ // asked for and what a great many deployments still serve — AWS Cognito
218
+ // among them. Dropping it turns a working tenant-scoped issuer into
219
+ // `unusable`, which is a regression dressed as strictness.
220
+ `${origin}${path}/.well-known/oauth-authorization-server`,
221
+ `${origin}/.well-known/openid-configuration${path}`,
222
+ `${origin}${path}/.well-known/openid-configuration`,
223
+ ];
224
+ }
225
+ /**
226
+ * Climbs the discovery chain to the endpoints. NEVER throws: a silent server,
227
+ * a 404 on the metadata or a dead network are ordinary outcomes, not
228
+ * exceptions. `reason` tells the caller what to do next — "leave this server
229
+ * alone" (`no-oauth`) is not the same decision as "report a problem"
230
+ * (`unusable`, `closed-door`, `unreachable`).
231
+ */
232
+ export async function discoverMcpOAuthOutcome(serverUrl, deps = {}) {
233
+ const fail = (reason) => ({
234
+ endpoints: null,
235
+ reason,
236
+ });
237
+ const f = deps.fetchImpl ?? globalThis.fetch;
238
+ const timeout = deps.timeoutMs ?? DEFAULT_TIMEOUT_MS;
239
+ const signal = () => AbortSignal.timeout(timeout);
240
+ try {
241
+ // 1. Get the challenge, unless the caller already has one.
242
+ let status;
243
+ let header;
244
+ if (deps.knownChallenge) {
245
+ status = deps.knownChallenge.status;
246
+ header = deps.knownChallenge.wwwAuthenticate;
247
+ }
248
+ else {
249
+ // An unauthenticated request is enough, and it has the same shape as
250
+ // the real handshake so that a server inspecting `clientInfo` sees the
251
+ // same identity in both places.
252
+ const challenge = await f(serverUrl, requestInit(signal(), {
253
+ method: 'POST',
254
+ headers: {
255
+ 'Content-Type': 'application/json',
256
+ Accept: 'application/json, text/event-stream',
257
+ },
258
+ body: JSON.stringify({
259
+ jsonrpc: '2.0',
260
+ id: 1,
261
+ method: 'initialize',
262
+ params: {
263
+ protocolVersion: '2025-06-18',
264
+ capabilities: {},
265
+ clientInfo: { name: pkg.name, version: pkg.version },
266
+ },
267
+ }),
268
+ }));
269
+ status = challenge.status;
270
+ header = challenge.headers.get('www-authenticate');
271
+ }
272
+ // Neither 401 nor 403: this server refuses nothing, there is no OAuth to
273
+ // discover. The 403 counts — a real MCP server was measured closing its
274
+ // door that way, and reading only the 401 would condemn it to never being
275
+ // authorized.
276
+ if (status !== 401 && status !== 403)
277
+ return fail('no-oauth');
278
+ // Bound to the origin of the server being asked, see the module header.
279
+ const announced = parseResourceMetadataUrl(header, serverUrl);
280
+ // A challenge that POINTS AT metadata we refuse is a hostile signal, not
281
+ // an oversight: we do not go looking elsewhere to be accommodating.
282
+ if (!announced && header && /resource_metadata/i.test(header))
283
+ return fail('unusable');
284
+ const resource = usableUrl(canonicalMcpUrl(serverUrl));
285
+ // Never the URL as received: `httpsUrl(serverUrl)` returned the string
286
+ // VERBATIM, query string included, so a hosted server's identifier
287
+ // (`?api_key=`) travelled as the `resource=` parameter of the
288
+ // authorization URL — into the user's BROWSER, their history, and the
289
+ // access logs of a THIRD-PARTY authorization server named by the MCP
290
+ // server itself — then into the POST to the token endpoint, then
291
+ // persisted. `canonicalMcpUrl` is also exactly what RFC 8707 expects of a
292
+ // resource identifier (scheme, host, path; neither query nor fragment),
293
+ // so closing the leak and conforming are the same gesture here.
294
+ if (!resource)
295
+ return fail('unusable');
296
+ // 2. Protected resource metadata (RFC 9728).
297
+ const prmUrl = announced ?? derivedResourceMetadataUrl(serverUrl);
298
+ const prm = prmUrl ? await readJson(f, prmUrl, signal()) : null;
299
+ let issuer;
300
+ let scopesSupported;
301
+ if (prm) {
302
+ // The resource announced must be THE ONE WE ARE ASKING (RFC 9728 §3.3).
303
+ // Otherwise we would go and negotiate a token at a third party and bring
304
+ // it back here — the heart of the mix-up.
305
+ const declared = typeof prm.resource === 'string' ? prm.resource : null;
306
+ if (declared && !sameMcpServer(declared, serverUrl))
307
+ return fail('unusable');
308
+ // "Nothing announced" (`closed-door`, no advice beyond "this server is
309
+ // shut") and "something IS announced but we refuse it" (`unusable`) are
310
+ // different outcomes; merging them would have the caller give the wrong
311
+ // advice.
312
+ const servers = Array.isArray(prm.authorization_servers) ? prm.authorization_servers : [];
313
+ if (servers.length === 0)
314
+ return fail('closed-door');
315
+ issuer = issuerId(servers[0]);
316
+ if (!issuer)
317
+ return fail('unusable');
318
+ scopesSupported = readScopes(prm.scopes_supported);
319
+ }
320
+ else {
321
+ // No PRM. Plenty of MCP servers publish RFC 8414 metadata on their own
322
+ // origin and skip RFC 9728 entirely; treating that as a closed door
323
+ // would refuse to log into servers that are perfectly usable. The
324
+ // fallback issuer is the server's OWN origin, which is a trusted value
325
+ // — the URL the user chose — so it does not reopen the mix-up.
326
+ issuer = issuerId(mcpOrigin(serverUrl));
327
+ if (!issuer)
328
+ return fail('closed-door');
329
+ scopesSupported = [];
330
+ }
331
+ // 3. Authorization server metadata (RFC 8414), at whichever of the
332
+ // standard locations this deployment uses.
333
+ let as = null;
334
+ for (const candidate of authorizationServerMetadataUrls(issuer)) {
335
+ as = await readJson(f, candidate, signal());
336
+ if (as)
337
+ break;
338
+ }
339
+ if (!as)
340
+ return fail(prm ? 'unusable' : 'closed-door');
341
+ // RFC 8414 §3.3: the `issuer` returned must be identical to the one we
342
+ // inserted the well-known path into. Second link of the anti mix-up chain:
343
+ // without it, a document served by another issuer would pass for the one
344
+ // we aimed at.
345
+ if (issuerId(as.issuer) !== issuer)
346
+ return fail('unusable');
347
+ const authorizationEndpoint = usableUrl(as.authorization_endpoint);
348
+ const tokenEndpoint = usableUrl(as.token_endpoint);
349
+ if (!authorizationEndpoint || !tokenEndpoint)
350
+ return fail('unusable');
351
+ // Treated uniformly with the two above: usable or absent, nothing else,
352
+ // never a special case for this optional field.
353
+ const registrationEndpoint = usableUrl(as.registration_endpoint);
354
+ // PKCE S256 required. Never `plain`: a public client without S256 has NO
355
+ // protection left on the code exchange.
356
+ const methods = Array.isArray(as.code_challenge_methods_supported)
357
+ ? as.code_challenge_methods_supported
358
+ : [];
359
+ if (!methods.includes('S256'))
360
+ return fail('unusable');
361
+ return {
362
+ endpoints: {
363
+ authorizationEndpoint,
364
+ tokenEndpoint,
365
+ registrationEndpoint,
366
+ resource,
367
+ issuer,
368
+ issParameterSupported: as.authorization_response_iss_parameter_supported === true,
369
+ scopesSupported,
370
+ },
371
+ reason: 'ok',
372
+ };
373
+ }
374
+ catch {
375
+ return fail('unreachable');
376
+ }
377
+ }
378
+ /** A metadata document, or `null` when it is absent or unreadable. */
379
+ async function readJson(f, url, signal) {
380
+ try {
381
+ const res = await f(url, requestInit(signal));
382
+ if (!res.ok)
383
+ return null;
384
+ return (await res.json());
385
+ }
386
+ catch {
387
+ return null;
388
+ }
389
+ }
390
+ /**
391
+ * The scopes the PRM advertises, filtered to non-empty strings. An absent or
392
+ * malformed value means "none", never an exception.
393
+ */
394
+ function readScopes(value) {
395
+ if (!Array.isArray(value))
396
+ return [];
397
+ return value.filter((v) => typeof v === 'string' && v.trim().length > 0);
398
+ }
399
+ //# sourceMappingURL=discovery.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"discovery.js","sourceRoot":"","sources":["../../../src/mcp/auth/discovery.ts"],"names":[],"mappings":"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"}