@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,475 @@
1
+ /**
2
+ * Client for the ORG-SCOPED backend routes of the MCP verifier.
3
+ *
4
+ * The response envelopes are read from the real gateway controllers
5
+ * (`gateway-service/src/modules/mcp-verify/mcp-org-verify.controller.ts`,
6
+ * `organization/mcp-org-policy.controller.ts`), not inferred from a spec:
7
+ *
8
+ * POST /mcp/organization/:id/servers/check -> `{ success, data: { policy, results }, message }`
9
+ * POST /mcp/organization/:id/tools/verify -> `{ success, data: { policy, verdicts }, message }`
10
+ * GET /mcp/organization/:id/policy -> `{ success, data: { mode, strictIssuer }, message }`
11
+ *
12
+ * The organization is in the PATH, so the backend checks membership before
13
+ * anything else, and the organization's policy is applied SERVER-SIDE: every
14
+ * verdict and every result carries the `action` the client executes, and the
15
+ * envelope carries the `policy` it was decided under. The deprecated
16
+ * user-scoped routes (`/mcp/tools/verify`, `/mcp/servers/check`) are never
17
+ * called by this binary: they return a verdict without an action, and a
18
+ * client that turned a verdict into a decision by reading a mode out of its
19
+ * own environment is exactly what the org-scoped line removes.
20
+ *
21
+ * Invariants held by this module:
22
+ * - `known: false` on a tool verdict means "not verified", never "nothing to
23
+ * report". It is carried as an explicit boolean all the way to the caller.
24
+ * - An `action` this binary cannot read — absent, or out of vocabulary — is
25
+ * `pending`, NEVER `allow`. Nothing opens a door nobody decided to open.
26
+ * - A response without a readable `policy` throws: the deprecated envelope
27
+ * is not this contract, and reading it as one would decide on nothing.
28
+ * - An untrusted `apiBase` is refused BEFORE any network call
29
+ * (`isTrustedApiBase`, applied on the real request path of both functions).
30
+ * - A `401` triggers EXACTLY one token refresh and one retry, never a loop
31
+ * (`requestWithAuthRetry`).
32
+ * - The backend refuses more than 500 tools / 200 servers per request
33
+ * (`VerifyToolsBodyDto`, `CheckMcpServersBodyDto` on the gateway side).
34
+ * This client splits into batches instead of letting the caller hit
35
+ * `INVALID_ARGUMENT`, and declares the split through `onBatchSplit`.
36
+ * - A response missing an expected field (`data.results`, `data.verdicts`)
37
+ * throws, never a silence that would read as "empty, nothing to report".
38
+ * - No token ever leaves this module inside an error message.
39
+ */
40
+ import { resolveTokenWithRefresh } from '../../hooks/runtime/resolve.js';
41
+ import { DIGEST_VERSION } from './digest.js';
42
+ import { isContractErrorStatus } from './contract-errors.js';
43
+ import { isPolicyMode, isServerAction } from './policy.js';
44
+ import { isTrustedApiBase, } from '../../hooks/runtime/trusted-api-base.js';
45
+ const DEFAULT_TIMEOUT_MS = 10_000;
46
+ // ─── Failure taxonomy ───────────────────────────────────────────────────────
47
+ /**
48
+ * Any non-2xx from the backend.
49
+ *
50
+ * Carries the STATUS and the ROUTE, because the caller's decision depends on
51
+ * both and re-parsing a message to recover them is how that decision starts
52
+ * being made on prose.
53
+ */
54
+ export class McpBackendError extends Error {
55
+ route;
56
+ status;
57
+ constructor(route, status, message) {
58
+ super(message ?? `/mcp/organization/:organizationId/${route} failed with HTTP ${status}`);
59
+ this.route = route;
60
+ this.status = status;
61
+ this.name = 'McpBackendError';
62
+ }
63
+ }
64
+ /**
65
+ * The backend ANSWERED, and refused what we sent: 400, 409, 422 — and, on the
66
+ * org-scoped routes, 403 (not a member of that organization) and 404 (no such
67
+ * organization).
68
+ *
69
+ * ⚠️ This is not an outage, and the difference is the whole point. An outage
70
+ * is fail-open — the tool list goes through, the verdict is `pending`, the
71
+ * agent is told nothing, because the user can do nothing about it. A contract
72
+ * refusal filed the same way says "we will retry and it will work", when
73
+ * retrying cannot help: the request that was refused is the request we would
74
+ * send again.
75
+ *
76
+ * Left as `pending`, a client that has stopped verifying ANYTHING looks
77
+ * exactly like a client waiting on a slow backend — for as long as nobody
78
+ * looks.
79
+ */
80
+ export class McpContractError extends McpBackendError {
81
+ constructor(route, status, message) {
82
+ super(route, status, message);
83
+ this.name = 'McpContractError';
84
+ }
85
+ }
86
+ /**
87
+ * `409 DIGEST_VERSION_UNSUPPORTED` — the backend cannot compute the digest
88
+ * formula this binary uses.
89
+ *
90
+ * Its own class because its remedy is its own: every digest we send is
91
+ * unmatchable, so nothing can be verified until one side is upgraded, and the
92
+ * `supported` list is the only actionable thing in the whole exchange.
93
+ */
94
+ export class DigestVersionUnsupportedError extends McpContractError {
95
+ code;
96
+ supported;
97
+ constructor(route, code, supported, message) {
98
+ super(route, 409, message);
99
+ this.code = code;
100
+ this.supported = supported;
101
+ this.name = 'DigestVersionUnsupportedError';
102
+ }
103
+ }
104
+ /** `{ error: { code, supported } }`, when the backend sent one. */
105
+ function errorEnvelope(body) {
106
+ if (!body || typeof body !== 'object')
107
+ return {};
108
+ const error = body.error;
109
+ if (!error || typeof error !== 'object')
110
+ return {};
111
+ const shape = error;
112
+ return {
113
+ code: typeof shape.code === 'string' ? shape.code : undefined,
114
+ supported: Array.isArray(shape.supported)
115
+ ? shape.supported.filter((value) => typeof value === 'string')
116
+ : undefined,
117
+ };
118
+ }
119
+ /**
120
+ * Turns a non-2xx into the right kind of error.
121
+ *
122
+ * The body is read ONLY to name the refusal. A body that is not JSON, or that
123
+ * carries no `error.code`, still produces a contract error when the status
124
+ * says so: a server being terse does not make its refusal an outage.
125
+ *
126
+ * ⚠️ Nothing from the request — token, headers, payload — goes into the
127
+ * message. A contract error is precisely what ends up in a log.
128
+ */
129
+ async function backendError(res, route, requestLabel) {
130
+ const label = `${requestLabel} failed with HTTP ${res.status}`;
131
+ if (!isContractErrorStatus(res.status))
132
+ return new McpBackendError(route, res.status, label);
133
+ let body;
134
+ try {
135
+ body = await res.json();
136
+ }
137
+ catch {
138
+ body = undefined;
139
+ }
140
+ const { code, supported } = errorEnvelope(body);
141
+ if (res.status === 409 && code === 'DIGEST_VERSION_UNSUPPORTED') {
142
+ const list = supported ?? [];
143
+ return new DigestVersionUnsupportedError(route, code, list, `${label}: this backend cannot compute digest version "${DIGEST_VERSION}"` +
144
+ (list.length > 0 ? `; it supports ${list.join(', ')}` : ''));
145
+ }
146
+ return new McpContractError(route, res.status, code ? `${label} (${code})` : label);
147
+ }
148
+ /**
149
+ * Mirrors `VerifyToolsBodyDto` (`ArrayMaxSize(500)`) on the gateway. A request
150
+ * over this bound is refused as `INVALID_ARGUMENT`; we split client-side.
151
+ */
152
+ export const MAX_TOOLS_PER_REQUEST = 500;
153
+ /**
154
+ * Mirrors `CheckMcpServersBodyDto`'s own `MAX_SERVERS_PER_REQUEST` (200), a
155
+ * different bound than {@link MAX_TOOLS_PER_REQUEST}.
156
+ */
157
+ export const MAX_SERVERS_PER_REQUEST = 200;
158
+ /**
159
+ * Gate every outbound call behind the same origin allowlist that protects the
160
+ * hooks (`isTrustedApiBase`, `src/hooks/runtime/trusted-api-base.ts`). Throws,
161
+ * never a silent `false`-shaped result: a rejected apiBase has no valid
162
+ * representation in any of the return types below, so failing the promise is
163
+ * the only way to guarantee it cannot be mistaken for "checked, nothing to
164
+ * report".
165
+ */
166
+ /**
167
+ * We refused to call, and the backend has nothing to do with it.
168
+ *
169
+ * ⚠️ Its own class because the caller was filing it as "backend unreachable,
170
+ * fail-open" — the same words an outage produces. A user whose region is
171
+ * mis-declared then goes and checks their network, their VPN and the
172
+ * backend's health, for a local configuration problem with a one-line fix.
173
+ * The direction is right (fail-open either way); the DIAGNOSIS was wrong, and
174
+ * a wrong diagnosis costs more than a missing one.
175
+ */
176
+ export class UntrustedApiBaseError extends Error {
177
+ apiBase;
178
+ constructor(apiBase) {
179
+ super(`refusing to call the CybeDefend gateway: the origin of "${apiBase}" is not in the ` +
180
+ 'trusted allowlist. Declare it in ~/.cybedefend/regions.json, which is the ' +
181
+ 'documented escape hatch for an internal or local cluster.');
182
+ this.apiBase = apiBase;
183
+ this.name = 'UntrustedApiBaseError';
184
+ }
185
+ }
186
+ function assertTrustedApiBase(apiBase, opts) {
187
+ if (!isTrustedApiBase(apiBase, opts.trustDeps)) {
188
+ throw new UntrustedApiBaseError(apiBase);
189
+ }
190
+ }
191
+ /**
192
+ * Run `attempt(ctx.token)`. On a `401`, ask {@link McpApiOpts.resolveTokenFn}
193
+ * for a fresh token and retry EXACTLY ONCE with it, never a loop. If no
194
+ * fresher token is available, or the retry ALSO comes back `401`, the second
195
+ * response (or the first, if there was no retry) is returned as-is: the
196
+ * caller's own `!res.ok` handling turns it into a thrown error, same as any
197
+ * other non-2xx status.
198
+ */
199
+ async function requestWithAuthRetry(ctx, opts, attempt) {
200
+ const res = await attempt(ctx.token);
201
+ if (res.status !== 401)
202
+ return res;
203
+ const resolveTokenFn = opts.resolveTokenFn ?? resolveTokenWithRefresh;
204
+ const refreshed = await resolveTokenFn(ctx.mcpName);
205
+ if (!refreshed)
206
+ return res;
207
+ return attempt(refreshed); // the ONE retry; its outcome is final either way
208
+ }
209
+ /** Split `items` into batches of at most `size`. Never yields an empty batch. */
210
+ function chunk(items, size) {
211
+ if (items.length === 0)
212
+ return [];
213
+ if (items.length <= size)
214
+ return [items.slice()];
215
+ const batches = [];
216
+ for (let i = 0; i < items.length; i += size)
217
+ batches.push(items.slice(i, i + size));
218
+ return batches;
219
+ }
220
+ /**
221
+ * A silent split would be indistinguishable from a machine that never needed
222
+ * one, so the split is always declared, through `opts.onBatchSplit` rather
223
+ * than a direct write to `stderr`.
224
+ */
225
+ function declareSplit(route, total, batches, cap, opts) {
226
+ opts.onBatchSplit?.(route, total, batches, cap);
227
+ }
228
+ async function parseJsonBody(res, label) {
229
+ try {
230
+ return await res.json();
231
+ }
232
+ catch {
233
+ throw new Error(`${label}: response body is not valid JSON`);
234
+ }
235
+ }
236
+ /**
237
+ * Unwrap the gateway's `{ success, data, message }` envelope down to `data`,
238
+ * and ONLY `data`. `body` and `body.data` are both rejected as insufficient.
239
+ */
240
+ function unwrapEnvelope(body, label) {
241
+ if (!body || typeof body !== 'object') {
242
+ throw new Error(`${label}: response is not a JSON object`);
243
+ }
244
+ const root = body;
245
+ if (root.success !== true) {
246
+ throw new Error(`${label}: response missing \`success: true\``);
247
+ }
248
+ const data = root.data;
249
+ if (!data || typeof data !== 'object') {
250
+ throw new Error(`${label}: response missing \`data\``);
251
+ }
252
+ return data;
253
+ }
254
+ /** A named field that MUST be an array: `data.results` / `data.verdicts`. */
255
+ function extractArray(data, field, label) {
256
+ const value = data[field];
257
+ if (!Array.isArray(value)) {
258
+ throw new Error(`${label}: response missing \`data.${field}\` array`);
259
+ }
260
+ return value;
261
+ }
262
+ /**
263
+ * `{ mode, strictIssuer }`, or a throw. Strict on purpose: a policy is what
264
+ * every action in the response was decided under, and a response without
265
+ * one is the deprecated envelope — reading it as this contract would decide
266
+ * on nothing. A mode out of vocabulary is refused rather than widened.
267
+ */
268
+ function parseOrgPolicy(raw, label) {
269
+ const shape = raw && typeof raw === 'object' ? raw : undefined;
270
+ if (!shape || !isPolicyMode(shape.mode) || typeof shape.strictIssuer !== 'boolean') {
271
+ throw new Error(`${label}: response missing a readable \`policy\` ({ mode, strictIssuer })`);
272
+ }
273
+ return { mode: shape.mode, strictIssuer: shape.strictIssuer };
274
+ }
275
+ /**
276
+ * The action to execute, or `pending` when the backend sent none we can read.
277
+ * `pending` and not `allow`: an unreadable decision is no decision.
278
+ */
279
+ function parseAction(raw) {
280
+ return isServerAction(raw) ? raw : 'pending';
281
+ }
282
+ /** The org-scoped path of a route. The id goes in the PATH, encoded. */
283
+ function orgRoute(ctx, suffix) {
284
+ return `${ctx.apiBase}/mcp/organization/${encodeURIComponent(ctx.organizationId)}/${suffix}`;
285
+ }
286
+ // ═══════════════════════════════════════════════════════════════════════════
287
+ // POST /mcp/organization/:organizationId/servers/check
288
+ // ═══════════════════════════════════════════════════════════════════════════
289
+ const SERVERS_CHECK_LABEL = 'POST /mcp/organization/:organizationId/servers/check';
290
+ function toMcpServerCheckInput(raw) {
291
+ const r = (raw ?? {});
292
+ return {
293
+ url: typeof r.url === 'string' ? r.url : undefined,
294
+ purl: typeof r.purl === 'string' ? r.purl : undefined,
295
+ };
296
+ }
297
+ function toAdvisory(raw) {
298
+ const a = (raw ?? {});
299
+ return {
300
+ id: typeof a.id === 'string' ? a.id : '',
301
+ category: typeof a.category === 'string' ? a.category : '',
302
+ severity: typeof a.severity === 'string' ? a.severity : '',
303
+ summary: typeof a.summary === 'string' ? a.summary : '',
304
+ source: typeof a.source === 'string' ? a.source : '',
305
+ };
306
+ }
307
+ function toServerCheckResult(raw) {
308
+ const r = (raw ?? {});
309
+ const advisories = Array.isArray(r.advisories) ? r.advisories : [];
310
+ return {
311
+ input: toMcpServerCheckInput(r.input),
312
+ advisories: advisories.map(toAdvisory),
313
+ mirrored: Boolean(r.mirrored),
314
+ action: parseAction(r.action),
315
+ };
316
+ }
317
+ async function checkMcpServersOnce(ctx, servers, opts) {
318
+ const f = opts.fetchImpl ?? globalThis.fetch;
319
+ const timeoutMs = opts.timeoutMs ?? DEFAULT_TIMEOUT_MS;
320
+ const url = orgRoute(ctx, 'servers/check');
321
+ const attempt = (token) => f(url, {
322
+ method: 'POST',
323
+ headers: {
324
+ Authorization: `Bearer ${token}`,
325
+ 'Content-Type': 'application/json',
326
+ },
327
+ body: JSON.stringify({ digestVersion: DIGEST_VERSION, servers }),
328
+ signal: AbortSignal.timeout(timeoutMs),
329
+ });
330
+ const res = await requestWithAuthRetry(ctx, opts, attempt);
331
+ if (!res.ok) {
332
+ throw await backendError(res, 'servers/check', SERVERS_CHECK_LABEL);
333
+ }
334
+ const body = await parseJsonBody(res, SERVERS_CHECK_LABEL);
335
+ const data = unwrapEnvelope(body, SERVERS_CHECK_LABEL);
336
+ const policy = parseOrgPolicy(data.policy, SERVERS_CHECK_LABEL);
337
+ const results = extractArray(data, 'results', SERVERS_CHECK_LABEL);
338
+ return { policy, results: results.map(toServerCheckResult) };
339
+ }
340
+ /**
341
+ * Checks a list of MCP server coordinates against the backend's advisory
342
+ * lookup + registry mirror.
343
+ *
344
+ * The gateway refuses more than {@link MAX_SERVERS_PER_REQUEST} servers in a
345
+ * single request (`CheckMcpServersBodyDto`): larger inputs are split into
346
+ * sequential batches here and the split is declared through `onBatchSplit`.
347
+ */
348
+ export async function checkMcpServers(ctx, servers, opts = {}) {
349
+ assertTrustedApiBase(ctx.apiBase, opts);
350
+ if (servers.length === 0)
351
+ return { policy: null, results: [] };
352
+ const batches = chunk(servers, MAX_SERVERS_PER_REQUEST);
353
+ if (batches.length > 1) {
354
+ declareSplit(SERVERS_CHECK_LABEL, servers.length, batches.length, MAX_SERVERS_PER_REQUEST, opts);
355
+ }
356
+ // One organization, one policy: every batch answers with the same one, and
357
+ // the last is as fresh as any.
358
+ let policy = null;
359
+ const results = [];
360
+ for (const batch of batches) {
361
+ const answer = await checkMcpServersOnce(ctx, batch, opts);
362
+ policy = answer.policy;
363
+ results.push(...answer.results);
364
+ }
365
+ return { policy, results };
366
+ }
367
+ // ═══════════════════════════════════════════════════════════════════════════
368
+ // POST /mcp/organization/:organizationId/tools/verify
369
+ // ═══════════════════════════════════════════════════════════════════════════
370
+ const TOOLS_VERIFY_LABEL = 'POST /mcp/organization/:organizationId/tools/verify';
371
+ /**
372
+ * CLOSED vocabulary of the tool verdicts THIS client knows how to interpret.
373
+ *
374
+ * `verdict` stays typed `string` in {@link ToolVerdict}: the backend is free
375
+ * to emit a value this binary does not know, and that is precisely the case
376
+ * that must be NAMED rather than collapsed. The list below says what this
377
+ * client can DO, not what the backend is allowed to say.
378
+ */
379
+ export const KNOWN_TOOL_VERDICTS = ['CLEAN', 'SUSPICIOUS', 'MALICIOUS'];
380
+ /** `true` only for a verdict this binary knows how to interpret. */
381
+ export function isKnownToolVerdict(verdict) {
382
+ return KNOWN_TOOL_VERDICTS.includes(verdict);
383
+ }
384
+ function toToolVerdict(raw) {
385
+ const v = (raw ?? {});
386
+ return {
387
+ digest: typeof v.digest === 'string' ? v.digest : '',
388
+ known: Boolean(v.known),
389
+ verdict: typeof v.verdict === 'string' ? v.verdict : '',
390
+ category: typeof v.category === 'string' ? v.category : '',
391
+ severity: typeof v.severity === 'string' ? v.severity : '',
392
+ reason: typeof v.reason === 'string' ? v.reason : '',
393
+ action: parseAction(v.action),
394
+ };
395
+ }
396
+ async function verifyMcpToolsOnce(ctx, tools, opts) {
397
+ const f = opts.fetchImpl ?? globalThis.fetch;
398
+ const timeoutMs = opts.timeoutMs ?? DEFAULT_TIMEOUT_MS;
399
+ const url = orgRoute(ctx, 'tools/verify');
400
+ const attempt = (token) => f(url, {
401
+ method: 'POST',
402
+ headers: {
403
+ Authorization: `Bearer ${token}`,
404
+ 'Content-Type': 'application/json',
405
+ },
406
+ body: JSON.stringify({ digestVersion: DIGEST_VERSION, tools }),
407
+ signal: AbortSignal.timeout(timeoutMs),
408
+ });
409
+ const res = await requestWithAuthRetry(ctx, opts, attempt);
410
+ if (!res.ok) {
411
+ throw await backendError(res, 'tools/verify', TOOLS_VERIFY_LABEL);
412
+ }
413
+ const body = await parseJsonBody(res, TOOLS_VERIFY_LABEL);
414
+ const data = unwrapEnvelope(body, TOOLS_VERIFY_LABEL);
415
+ const policy = parseOrgPolicy(data.policy, TOOLS_VERIFY_LABEL);
416
+ const verdicts = extractArray(data, 'verdicts', TOOLS_VERIFY_LABEL);
417
+ return { policy, verdicts: verdicts.map(toToolVerdict) };
418
+ }
419
+ /**
420
+ * Verifies a list of MCP tool submissions by digest, judging the ones that
421
+ * carry a `definition`.
422
+ *
423
+ * The gateway refuses more than {@link MAX_TOOLS_PER_REQUEST} tools in a
424
+ * single request (`VerifyToolsBodyDto`, `ArrayMaxSize(500)`): larger inputs
425
+ * are split into sequential batches here and the split is declared through
426
+ * `onBatchSplit`.
427
+ */
428
+ export async function verifyMcpTools(ctx, submissions, opts = {}) {
429
+ assertTrustedApiBase(ctx.apiBase, opts);
430
+ if (submissions.length === 0)
431
+ return { policy: null, verdicts: [] };
432
+ const batches = chunk(submissions, MAX_TOOLS_PER_REQUEST);
433
+ if (batches.length > 1) {
434
+ declareSplit(TOOLS_VERIFY_LABEL, submissions.length, batches.length, MAX_TOOLS_PER_REQUEST, opts);
435
+ }
436
+ let policy = null;
437
+ const verdicts = [];
438
+ for (const batch of batches) {
439
+ const answer = await verifyMcpToolsOnce(ctx, batch, opts);
440
+ policy = answer.policy;
441
+ verdicts.push(...answer.verdicts);
442
+ }
443
+ return { policy, verdicts };
444
+ }
445
+ // ═══════════════════════════════════════════════════════════════════════════
446
+ // GET /mcp/organization/:organizationId/policy
447
+ // ═══════════════════════════════════════════════════════════════════════════
448
+ const POLICY_LABEL = 'GET /mcp/organization/:organizationId/policy';
449
+ /**
450
+ * Reads the organization's policy on its own.
451
+ *
452
+ * Not needed to APPLY it — every verify/check response already carries the
453
+ * policy its actions were decided under — but it is how the policy is shown
454
+ * (`vibedefend status`, `vibedefend org`), how `connect` learns
455
+ * `strictIssuer` before any OAuth discovery, and how a verifier refreshes
456
+ * what it knows before a decision that only the cache will answer.
457
+ */
458
+ export async function getOrgPolicy(ctx, opts = {}) {
459
+ assertTrustedApiBase(ctx.apiBase, opts);
460
+ const f = opts.fetchImpl ?? globalThis.fetch;
461
+ const timeoutMs = opts.timeoutMs ?? DEFAULT_TIMEOUT_MS;
462
+ const url = orgRoute(ctx, 'policy');
463
+ const attempt = (token) => f(url, {
464
+ method: 'GET',
465
+ headers: { Authorization: `Bearer ${token}` },
466
+ signal: AbortSignal.timeout(timeoutMs),
467
+ });
468
+ const res = await requestWithAuthRetry(ctx, opts, attempt);
469
+ if (!res.ok) {
470
+ throw await backendError(res, 'policy', POLICY_LABEL);
471
+ }
472
+ const body = await parseJsonBody(res, POLICY_LABEL);
473
+ return parseOrgPolicy(unwrapEnvelope(body, POLICY_LABEL), POLICY_LABEL);
474
+ }
475
+ //# sourceMappingURL=api.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api.js","sourceRoot":"","sources":["../../../src/mcp/verify/api.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,143 @@
1
+ /**
2
+ * Which CybeDefend backend this machine talks to, with what — and AS WHICH
3
+ * ORGANIZATION.
4
+ *
5
+ * The region lives in `~/.cybedefend/runtime-config.json`, written by
6
+ * `vibedefend install`. A machine that has never been installed has no
7
+ * region, no token, and therefore no verification — which is a supported
8
+ * state, not a failure: the proxy still forwards everything, and the log says
9
+ * why nothing was verified.
10
+ *
11
+ * The organization lives in the same file (`organization`), selected at
12
+ * `vibedefend login`, with `vibedefend org`, or here, lazily: the verification
13
+ * routes are org-scoped, most accounts belong to exactly one organization,
14
+ * and asking those users to pick would be a prompt with one answer. An
15
+ * account that belongs to several must choose explicitly, and until it does
16
+ * there is no backend to talk to — unverified, never an allow.
17
+ *
18
+ * `VIBEDEFEND_MCP_API_BASE` overrides the region's `apiBase`. It is how the
19
+ * tests point the proxy at a local mock, and it goes through the same origin
20
+ * allowlist as everything else in `api.ts` — a URL that is not trusted is
21
+ * refused before any request leaves, the organization listing included.
22
+ */
23
+ import { listOrganizations } from '../../api/projects.js';
24
+ import { REGIONS } from '../../config.js';
25
+ import { loadRuntimeConfig, saveRuntimeOrganization } from '../../hooks/runtime/config.js';
26
+ import { isTrustedApiBase } from '../../hooks/runtime/trusted-api-base.js';
27
+ import { PROXY_ENV_PREFIX } from '../proxy/paths.js';
28
+ import { resolveVerifyToken } from './token.js';
29
+ import { resolveVerifyTokenWithRefresh } from './token-refresh.js';
30
+ /** The region the installer recorded, or the EU default. */
31
+ export function activeRegion(deps = {}) {
32
+ try {
33
+ const runtime = (deps.loadRuntimeConfigFn ?? loadRuntimeConfig)();
34
+ const id = runtime?.region?.id;
35
+ const found = id ? REGIONS.find((region) => region.id === id) : undefined;
36
+ if (found)
37
+ return found;
38
+ // A custom region carries its own apiBase; it is honoured as it stands.
39
+ const custom = runtime?.region;
40
+ if (custom?.apiBase && custom.mcpName)
41
+ return custom;
42
+ }
43
+ catch {
44
+ /* no runtime config: fall through to the default */
45
+ }
46
+ return REGIONS[0];
47
+ }
48
+ /** The organization on file, without any network call. */
49
+ export function persistedOrganization(deps = {}) {
50
+ try {
51
+ const runtime = (deps.loadRuntimeConfigFn ?? loadRuntimeConfig)();
52
+ const organization = runtime?.organization;
53
+ if (organization && typeof organization.id === 'string' && organization.id !== '') {
54
+ return { id: organization.id, name: typeof organization.name === 'string' ? organization.name : '' };
55
+ }
56
+ }
57
+ catch {
58
+ /* no runtime config, or an unreadable one */
59
+ }
60
+ return null;
61
+ }
62
+ /**
63
+ * Everything the verifier needs to reach the backend, or `null` when there is
64
+ * nothing to reach it with — no token, or no organization to speak for.
65
+ */
66
+ export async function resolveBackendConfig(deps = {}) {
67
+ const env = deps.env ?? process.env;
68
+ const region = activeRegion(deps);
69
+ const apiBase = env[`${PROXY_ENV_PREFIX}API_BASE`]?.trim() || region.apiBase;
70
+ // Renewed if it expired: a human is waiting for this answer, and a stale
71
+ // token here turned a whole scan `unverified` (2026-09-16).
72
+ const token = await (deps.resolveTokenFn ?? resolveVerifyTokenWithRefresh)(region.mcpName, { env });
73
+ if (!token)
74
+ return null;
75
+ const organizationId = await resolveOrganizationId(apiBase, token, deps);
76
+ if (!organizationId)
77
+ return null;
78
+ return { apiBase, mcpName: region.mcpName, token, organizationId };
79
+ }
80
+ /**
81
+ * What the PROXY builds its verifier with — synchronously, and without a
82
+ * single network call.
83
+ *
84
+ * Section 0: nothing reaches CybeDefend before `initialize` is answered, and
85
+ * the verifier is wired before the proxy starts. So when no organization is
86
+ * persisted, the id is left to `resolveOrganizationId`, a thunk the verifier
87
+ * runs at its FIRST backend call — after `initialize`, on the `tools/list`
88
+ * path, where a network call already belongs. `scan`, `connect` and `org`
89
+ * are typed by a human and use `resolveBackendConfig` instead, which resolves
90
+ * up front.
91
+ */
92
+ export function resolveBackendAccess(deps = {}) {
93
+ const env = deps.env ?? process.env;
94
+ const region = activeRegion(deps);
95
+ const apiBase = env[`${PROXY_ENV_PREFIX}API_BASE`]?.trim() || region.apiBase;
96
+ const token = resolveVerifyToken(region.mcpName, { env });
97
+ if (!token)
98
+ return null;
99
+ const persisted = persistedOrganization(deps);
100
+ return {
101
+ apiBase,
102
+ mcpName: region.mcpName,
103
+ token,
104
+ ...(persisted
105
+ ? { organizationId: persisted.id }
106
+ : { resolveOrganizationId: () => resolveOrganizationId(apiBase, token, deps) }),
107
+ };
108
+ }
109
+ /**
110
+ * The persisted organization, or the account's only one — persisted on the
111
+ * way. Several, none, or an answer that could not be obtained: `null`, with
112
+ * one line naming the count, and nothing else. A guess here would be a
113
+ * verification under the wrong organization's policy.
114
+ */
115
+ export async function resolveOrganizationId(apiBase, token, deps) {
116
+ const persisted = persistedOrganization(deps);
117
+ if (persisted)
118
+ return persisted.id;
119
+ const warn = deps.warn ?? (() => undefined);
120
+ if (!isTrustedApiBase(apiBase)) {
121
+ warn('verify: apiBase is not a trusted origin — the organization was not resolved, nothing was sent');
122
+ return null;
123
+ }
124
+ const organizations = await (deps.listOrganizationsFn ?? listOrganizations)(apiBase, token);
125
+ if (organizations === null) {
126
+ warn('verify: could not list the organizations, no backend for this session');
127
+ return null;
128
+ }
129
+ if (organizations.length !== 1) {
130
+ warn('verify: no organization selected and the account belongs to several (or none); ' +
131
+ 'run `vibedefend org <id>` — no backend for this session', { organizations: organizations.length });
132
+ return null;
133
+ }
134
+ const only = organizations[0];
135
+ try {
136
+ (deps.saveOrganizationFn ?? saveRuntimeOrganization)(only);
137
+ }
138
+ catch {
139
+ /* remembered for this session at least; the next one resolves it again */
140
+ }
141
+ return only.id;
142
+ }
143
+ //# sourceMappingURL=backend.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"backend.js","sourceRoot":"","sources":["../../../src/mcp/verify/backend.ts"],"names":[],"mappings":"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"}