@cybedefend/vibedefend 1.4.7 → 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 +77 -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 +24873 -5068
  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 +126 -53
  66. package/dist/hooks/runtime/emit.js.map +1 -1
  67. package/dist/hooks/runtime/fetch-rules.js +27 -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 +21 -6
  82. package/dist/hooks/runtime/pre-compact.js.map +1 -1
  83. package/dist/hooks/runtime/resolve.js +108 -17
  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 +226 -0
  92. package/dist/hooks/runtime/session-activation.js.map +1 -0
  93. package/dist/hooks/runtime/session-review.js +174 -46
  94. package/dist/hooks/runtime/session-review.js.map +1 -1
  95. package/dist/hooks/runtime/session-start.js +35 -4
  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 +8 -2
  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 +99 -14
  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,173 @@
1
+ /** Identity of a coordinate, used to deduplicate one server across configs. */
2
+ export function coordinateKey(c) {
3
+ if (c.url)
4
+ return `url:${c.url}`;
5
+ if (c.purl)
6
+ return `purl:${c.purl}`;
7
+ return `pkg:${c.ecosystem ?? ''}:${c.packageName ?? ''}:${c.version ?? ''}`;
8
+ }
9
+ // ─── Stable identity of a discovered server ──────────────────────────────────
10
+ /**
11
+ * Names the identity of a server in a way that is stable across runs. The
12
+ * discovery layer has already deduplicated by coordinate, so every entry it
13
+ * yields is one physical server; `source` and `name` only matter for servers
14
+ * that resolved to no coordinate.
15
+ */
16
+ export function serverIdOf(server) {
17
+ if (server.coordinate)
18
+ return coordinateKey(server.coordinate);
19
+ return `local:${server.source}:${server.name}`;
20
+ }
21
+ // ─── purl / server-check coordinate ──────────────────────────────────────────
22
+ /**
23
+ * Builds a `purl` (package URL) from an npm/pip coordinate.
24
+ *
25
+ * The namespace of a SCOPED npm package is a path segment of its own
26
+ * (`pkg:npm/%40scope/name@version`), not a prefix encoded together with the
27
+ * name (`pkg:npm/%40scope%2Fname@version`). Encoding `@scope/name` as one
28
+ * string also encodes the `/` separator, which produced a non-conforming purl
29
+ * for every `@modelcontextprotocol/server-*` package.
30
+ */
31
+ export function purlOf(coordinate) {
32
+ if (coordinate.purl)
33
+ return coordinate.purl;
34
+ if (!coordinate.ecosystem || !coordinate.packageName)
35
+ return undefined;
36
+ const type = coordinate.ecosystem === 'pip' ? 'pypi' : coordinate.ecosystem;
37
+ const versionSuffix = coordinate.version ? `@${encodeURIComponent(coordinate.version)}` : '';
38
+ if (type === 'npm' && coordinate.packageName.startsWith('@')) {
39
+ const withoutAt = coordinate.packageName.slice(1); // "scope/name"
40
+ const slashIdx = withoutAt.indexOf('/');
41
+ if (slashIdx > 0) {
42
+ const namespace = withoutAt.slice(0, slashIdx);
43
+ const name = withoutAt.slice(slashIdx + 1);
44
+ return `pkg:npm/%40${encodeURIComponent(namespace)}/${encodeURIComponent(name)}${versionSuffix}`;
45
+ }
46
+ }
47
+ return `pkg:${type}/${encodeURIComponent(coordinate.packageName)}${versionSuffix}`;
48
+ }
49
+ /** What `POST /mcp/servers/check` accepts for this coordinate, or `null` when nothing applies. */
50
+ export function checkInputOf(coordinate) {
51
+ if (!coordinate)
52
+ return null;
53
+ if (coordinate.url)
54
+ return { url: coordinate.url };
55
+ const purl = purlOf(coordinate);
56
+ return purl ? { purl } : null;
57
+ }
58
+ export function checkInputKey(input) {
59
+ return input.url ? `url:${input.url}` : `purl:${input.purl ?? ''}`;
60
+ }
61
+ // ─── Identity of an upstream the proxy runs in front of (Phase 3) ────────────
62
+ import { createHash } from 'node:crypto';
63
+ import { canonicalMcpUrl } from '../auth/oauth.js';
64
+ /**
65
+ * Package launchers whose first non-flag argument names a package.
66
+ *
67
+ * `npx`, `pnpm dlx` and `bunx` for npm; `uvx` and `pipx` for PyPI. Anything
68
+ * else — a path to a script, a binary on `PATH`, a shell wrapper — has no
69
+ * package coordinate to read, and pretending otherwise would file two
70
+ * unrelated servers under one identity.
71
+ */
72
+ const NPM_LAUNCHERS = new Set(['npx', 'bunx', 'pnpx']);
73
+ const PYPI_LAUNCHERS = new Set(['uvx', 'pipx']);
74
+ /** Flags a launcher takes before the package name. */
75
+ const LAUNCHER_FLAGS = new Set(['-y', '--yes', '-q', '--quiet', '--silent', '-p', '--package', 'run']);
76
+ /** Strips a `.cmd` / `.exe` suffix and any directory, so `C:\…\npx.cmd` reads as `npx`. */
77
+ function launcherName(command) {
78
+ const base = command.replace(/\\/g, '/').split('/').pop() ?? command;
79
+ return base.replace(/\.(cmd|exe|bat|ps1)$/i, '').toLowerCase();
80
+ }
81
+ /**
82
+ * The package a launcher command runs, or `null`.
83
+ *
84
+ * `npx -y @scope/name@1.2.3` reads as `@scope/name` at `1.2.3`. The leading
85
+ * `@` of a scope is not a version separator, which is the one thing a naive
86
+ * `split('@')` gets wrong on exactly the packages MCP servers ship as.
87
+ */
88
+ export function parsePackageCoordinate(command, args) {
89
+ const launcher = launcherName(command);
90
+ const ecosystem = NPM_LAUNCHERS.has(launcher)
91
+ ? 'npm'
92
+ : PYPI_LAUNCHERS.has(launcher)
93
+ ? 'pypi'
94
+ : null;
95
+ if (!ecosystem)
96
+ return null;
97
+ const spec = args.find((arg) => !arg.startsWith('-') && !LAUNCHER_FLAGS.has(arg));
98
+ if (!spec)
99
+ return null;
100
+ // A scope starts with `@`; the version separator is the `@` AFTER it.
101
+ const separator = spec.lastIndexOf('@');
102
+ if (separator > 0) {
103
+ const version = spec.slice(separator + 1);
104
+ // `@scope/name` has its only `@` at index 0, already excluded above; a
105
+ // trailing `@` with nothing after it pins nothing.
106
+ if (version !== '' && !version.includes('/')) {
107
+ return { ecosystem, name: spec.slice(0, separator), version };
108
+ }
109
+ }
110
+ return { ecosystem, name: spec };
111
+ }
112
+ /**
113
+ * The identity the proxy files a server under.
114
+ *
115
+ * Four forms, in the order they are tried:
116
+ *
117
+ * `url:<canonical>` an HTTP server, keyed the same way its
118
+ * credential is (`canonicalMcpUrl`), so the two
119
+ * never disagree about which server this is;
120
+ * `npm:<pkg>@<version>` a pinned package, shareable across
121
+ * `pypi:<pkg>@<version>` organisations because it names the same bytes
122
+ * for everybody;
123
+ * `cmd:sha256(...)` anything else, including an UNPINNED package.
124
+ *
125
+ * The last one covers more than it looks: `npx -y @scope/server` with no
126
+ * version is the shape most people use, and it lands here on purpose. A
127
+ * verdict on "whatever npx resolves today" is not a fact about a package, it
128
+ * is a fact about one machine on one day, and sharing it between
129
+ * organisations would be sharing a guess.
130
+ */
131
+ export function upstreamServerId(spec) {
132
+ if (spec.transport === 'http') {
133
+ return `url:${canonicalMcpUrl(spec.url) ?? spec.url}`;
134
+ }
135
+ const coordinate = parsePackageCoordinate(spec.command, spec.args);
136
+ if (coordinate?.version) {
137
+ return `${coordinate.ecosystem}:${coordinate.name}@${coordinate.version}`;
138
+ }
139
+ // Every argument, in order, separated by a character no argument can
140
+ // contain: `a b` and `a` `b` must not hash alike.
141
+ const material = [spec.command, ...spec.args].join('\u0000');
142
+ return `cmd:${createHash('sha256').update(material).digest('hex')}`;
143
+ }
144
+ /** A filesystem-safe name for the cache file of a server id. */
145
+ export function serverIdFileName(serverId) {
146
+ // The id may contain `/`, `:` and `@`; the hash keeps it unambiguous while
147
+ // the readable prefix keeps the cache directory browsable.
148
+ const readable = serverId.replace(/[^A-Za-z0-9._-]+/g, '-').slice(0, 48).replace(/^-+|-+$/g, '');
149
+ const fingerprint = createHash('sha256').update(serverId).digest('hex').slice(0, 16);
150
+ return `${readable || 'server'}.${fingerprint}.json`;
151
+ }
152
+ /**
153
+ * What `POST /mcp/servers/check` accepts for an upstream, or `null`.
154
+ *
155
+ * A `cmd:` identity has nothing to send: there is no public coordinate to
156
+ * look up, and posting the hash would tell the backend a server exists
157
+ * without letting it say anything useful about it.
158
+ */
159
+ export function upstreamCheckInput(spec) {
160
+ if (spec.transport === 'http') {
161
+ const url = canonicalMcpUrl(spec.url);
162
+ return url ? { url } : null;
163
+ }
164
+ const coordinate = parsePackageCoordinate(spec.command, spec.args);
165
+ if (!coordinate)
166
+ return null;
167
+ return checkInputOf({
168
+ ecosystem: coordinate.ecosystem === 'pypi' ? 'pip' : 'npm',
169
+ packageName: coordinate.name,
170
+ version: coordinate.version,
171
+ });
172
+ }
173
+ //# sourceMappingURL=identity.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"identity.js","sourceRoot":"","sources":["../../../src/mcp/verify/identity.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,63 @@
1
+ /**
2
+ * The last known policy of each organization, on this machine.
3
+ *
4
+ * `~/.cybedefend/mcp/policy.json`, keyed by organization id. It exists for
5
+ * the degraded session — backend unreachable, or no credential — which must
6
+ * still decide under the policy the organization actually has rather than
7
+ * under the default: an organization that set `block` last week has set it
8
+ * for the day the network is down too. Keyed by organization so a machine
9
+ * that moves between two of them never carries one's `block` into the
10
+ * other.
11
+ *
12
+ * Written by the verifier every time a response carries a policy, and by
13
+ * `GET …/policy`. Never a lever: nothing on this machine reads it as a
14
+ * setting, only as a memory of what the server said.
15
+ */
16
+ import { readFileSync } from 'node:fs';
17
+ import { join } from 'node:path';
18
+ import { writeFileAtomic } from '../config/atomicWrite.js';
19
+ import { mcpHomeDir } from '../proxy/paths.js';
20
+ import { isPolicyMode } from './policy.js';
21
+ export function policyFile(env = process.env) {
22
+ return join(mcpHomeDir(env), 'policy.json');
23
+ }
24
+ function readAll(env) {
25
+ let parsed;
26
+ try {
27
+ parsed = JSON.parse(readFileSync(policyFile(env), 'utf8'));
28
+ }
29
+ catch {
30
+ return {};
31
+ }
32
+ return parsed && typeof parsed === 'object' && !Array.isArray(parsed) ? parsed : {};
33
+ }
34
+ function toStoredPolicy(raw) {
35
+ if (!raw || typeof raw !== 'object')
36
+ return null;
37
+ const shape = raw;
38
+ if (!isPolicyMode(shape.mode) || typeof shape.strictIssuer !== 'boolean')
39
+ return null;
40
+ return {
41
+ mode: shape.mode,
42
+ strictIssuer: shape.strictIssuer,
43
+ learnedAt: typeof shape.learnedAt === 'string' ? shape.learnedAt : new Date(0).toISOString(),
44
+ };
45
+ }
46
+ /** What was last learned about this organization, or `null`. A corrupt entry is nothing known. */
47
+ export function readStoredPolicy(organizationId, options = {}) {
48
+ const env = options.env ?? process.env;
49
+ return toStoredPolicy(readAll(env)[organizationId]);
50
+ }
51
+ /** Records the policy for this organization, leaving every other organization's entry as it is. */
52
+ export function writeStoredPolicy(organizationId, policy, options = {}) {
53
+ const env = options.env ?? process.env;
54
+ const all = readAll(env);
55
+ const entry = {
56
+ mode: policy.mode,
57
+ strictIssuer: policy.strictIssuer,
58
+ learnedAt: (options.now ?? new Date()).toISOString(),
59
+ };
60
+ all[organizationId] = entry;
61
+ writeFileAtomic(policyFile(env), `${JSON.stringify(all, null, 2)}\n`, { mode: 0o600 });
62
+ }
63
+ //# sourceMappingURL=policy-store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"policy-store.js","sourceRoot":"","sources":["../../../src/mcp/verify/policy-store.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,OAAO,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAC3D,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,EAAE,YAAY,EAAkB,MAAM,aAAa,CAAC;AAY3D,MAAM,UAAU,UAAU,CAAC,MAAyB,OAAO,CAAC,GAAG;IAC7D,OAAO,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,aAAa,CAAC,CAAC;AAC9C,CAAC;AAED,SAAS,OAAO,CAAC,GAAsB;IACrC,IAAI,MAAe,CAAC;IACpB,IAAI,CAAC;QACH,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC;IAC7D,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;IACD,OAAO,MAAM,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,CAAE,MAAkC,CAAC,CAAC,CAAC,EAAE,CAAC;AACnH,CAAC;AAED,SAAS,cAAc,CAAC,GAAY;IAClC,IAAI,CAAC,GAAG,IAAI,OAAO,GAAG,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAC;IACjD,MAAM,KAAK,GAAG,GAA8B,CAAC;IAC7C,IAAI,CAAC,YAAY,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,OAAO,KAAK,CAAC,YAAY,KAAK,SAAS;QAAE,OAAO,IAAI,CAAC;IACtF,OAAO;QACL,IAAI,EAAE,KAAK,CAAC,IAAI;QAChB,YAAY,EAAE,KAAK,CAAC,YAAY;QAChC,SAAS,EAAE,OAAO,KAAK,CAAC,SAAS,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE;KAC7F,CAAC;AACJ,CAAC;AAED,kGAAkG;AAClG,MAAM,UAAU,gBAAgB,CAAC,cAAsB,EAAE,UAA8B,EAAE;IACvF,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,IAAI,OAAO,CAAC,GAAG,CAAC;IACvC,OAAO,cAAc,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC;AACtD,CAAC;AAED,mGAAmG;AACnG,MAAM,UAAU,iBAAiB,CAAC,cAAsB,EAAE,MAAiB,EAAE,UAA8B,EAAE;IAC3G,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,IAAI,OAAO,CAAC,GAAG,CAAC;IACvC,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC;IACzB,MAAM,KAAK,GAAiB;QAC1B,IAAI,EAAE,MAAM,CAAC,IAAI;QACjB,YAAY,EAAE,MAAM,CAAC,YAAY;QACjC,SAAS,EAAE,CAAC,OAAO,CAAC,GAAG,IAAI,IAAI,IAAI,EAAE,CAAC,CAAC,WAAW,EAAE;KACrD,CAAC;IACF,GAAG,CAAC,cAAc,CAAC,GAAG,KAAK,CAAC;IAC5B,eAAe,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;AACzF,CAAC"}
@@ -0,0 +1,158 @@
1
+ export const SERVER_ACTIONS = ['allow', 'ask', 'warn', 'block', 'pending'];
2
+ export function isServerAction(value) {
3
+ return typeof value === 'string' && SERVER_ACTIONS.includes(value);
4
+ }
5
+ export function isPolicyMode(value) {
6
+ return value === 'warn' || value === 'block';
7
+ }
8
+ /**
9
+ * Used ONLY when nothing was ever learned from the backend — the same
10
+ * fallback global-management applies to an organization that never set a
11
+ * policy. Not a lever: nothing on this machine can change it.
12
+ */
13
+ /**
14
+ * Used only when this machine never learned any policy for the organization —
15
+ * the same fallback global-management serves for an organization that never
16
+ * set one (`block` since 2026-09-15): a verdict nobody chose to relax is refused.
17
+ */
18
+ export const DEFAULT_ORG_POLICY = Object.freeze({ mode: 'block', strictIssuer: false });
19
+ /**
20
+ * The backend's table, recomputed for a record that has no fresh answer.
21
+ *
22
+ * `docs/mcp-verify-integration.md` §1, in our verdict vocabulary: not
23
+ * verified ⇒ `pending`; CLEAN ⇒ `allow`; SUSPICIOUS ⇒ `ask` in every mode
24
+ * (the user decides, at the call); MALICIOUS ⇒ the mode. A `contract-error`
25
+ * is not a verdict about the server and stays undecided. A fresh action from
26
+ * a response always wins over this — see `Verifier`; this is for the cache.
27
+ */
28
+ export function actionFor(verdict, policy) {
29
+ switch (verdict) {
30
+ case 'ok':
31
+ return 'allow';
32
+ case 'suspicious':
33
+ return 'ask';
34
+ case 'malicious':
35
+ return policy.mode;
36
+ default:
37
+ return 'pending';
38
+ }
39
+ }
40
+ /**
41
+ * The worst of a set of actions, `block` first. `pending` ranks above
42
+ * `allow` so that a list with anything unverified in it is not called clean.
43
+ */
44
+ const ACTION_SEVERITY = {
45
+ block: 5,
46
+ warn: 4,
47
+ ask: 3,
48
+ pending: 2,
49
+ allow: 1,
50
+ };
51
+ export function worstAction(actions) {
52
+ let worst = 'allow';
53
+ for (const action of actions) {
54
+ if (ACTION_SEVERITY[action] > ACTION_SEVERITY[worst])
55
+ worst = action;
56
+ }
57
+ return worst;
58
+ }
59
+ /**
60
+ * The worst of a set of verdicts.
61
+ *
62
+ * "Worst" is not the same as "most recent": a server whose ten tools are
63
+ * clean and whose eleventh is malicious is a malicious server, and averaging
64
+ * would be a way of losing exactly the finding that matters.
65
+ */
66
+ const SEVERITY = {
67
+ malicious: 5,
68
+ suspicious: 4,
69
+ // ⚠️ Above `pending`, below `suspicious`, and both halves matter.
70
+ //
71
+ // Above, because a server whose tools are all unverified BECAUSE the backend
72
+ // refuses our requests is not in the same state as one we simply have not
73
+ // got to yet — and only the aggregate is rendered.
74
+ //
75
+ // Below, because a contract error must never MASK a real security verdict.
76
+ // If it outranked `malicious`, breaking the contract would become a way of
77
+ // erasing a blacklist.
78
+ 'contract-error': 3,
79
+ pending: 2,
80
+ unknown: 1,
81
+ ok: 0,
82
+ };
83
+ export function worstVerdict(verdicts) {
84
+ let worst = 'ok';
85
+ for (const verdict of verdicts) {
86
+ if (SEVERITY[verdict] > SEVERITY[worst])
87
+ worst = verdict;
88
+ }
89
+ return worst;
90
+ }
91
+ export function serverVerdict(server, tools) {
92
+ return worstVerdict([server.verdict, ...tools.map((tool) => tool.verdict)]);
93
+ }
94
+ /**
95
+ * Decides — that is, executes.
96
+ *
97
+ * The user's own decision comes first, in both directions: an `allow` on a
98
+ * server the backend calls malicious is a decision they were shown the reason
99
+ * for and made anyway, and overriding it would be telling them they were
100
+ * wrong every session. A `deny` blocks whatever the backend says, including
101
+ * `allow`.
102
+ *
103
+ * Then the server's action, verbatim: `block` blocks, `warn` alerts and lets
104
+ * the call through, `ask` alerts and has the user confirm the call. The
105
+ * rationale names the verdict the action came from, which is what the agent
106
+ * and the user are told.
107
+ */
108
+ export function decide(input) {
109
+ if (input.override === 'deny')
110
+ return { action: 'block', rationale: 'denied-by-user' };
111
+ if (input.override === 'allow')
112
+ return { action: 'pass', rationale: 'allowed-by-user' };
113
+ if (input.action === 'block')
114
+ return { action: 'block', rationale: 'malicious' };
115
+ if (input.action === 'warn')
116
+ return { action: 'alert', rationale: 'malicious' };
117
+ if (input.action === 'ask')
118
+ return { action: 'alert', rationale: 'suspicious' };
119
+ // Our own machinery, not this server. It ALERTS and never blocks: blocking
120
+ // on a contract disagreement would be a fail-closed triggered by a bug of
121
+ // ours, on servers we know strictly nothing about — the most expensive
122
+ // possible mistake, where the product becomes the incident. And it only
123
+ // speaks once it has survived a restart; see `contractErrorPersisted`.
124
+ if (input.verdict === 'contract-error') {
125
+ return input.contractErrorPersisted
126
+ ? { action: 'alert', rationale: 'contract-error' }
127
+ : { action: 'pass', rationale: 'not-verified' };
128
+ }
129
+ // A list that changed under us is worth a word even when the new content
130
+ // reads clean: the change itself is the signal, and it is the shape a
131
+ // rug-pull takes.
132
+ if (input.changedDuringSession)
133
+ return { action: 'alert', rationale: 'changed-mid-session' };
134
+ if (input.action === 'pending')
135
+ return { action: 'pass', rationale: 'not-verified' };
136
+ return { action: 'pass', rationale: 'clean' };
137
+ }
138
+ /**
139
+ * Whether to refuse a server the backend cannot be asked about right now.
140
+ *
141
+ * Section 0: fail-open by default, fail-closed only for a server the LOCAL
142
+ * cache already calls malicious and that the user has not allowed. The
143
+ * asymmetry is the point — an outage must not turn every server off, and it
144
+ * must not turn a known-bad one back on either.
145
+ *
146
+ * It takes the AGGREGATE verdict, not the server record's own: a server whose
147
+ * ten tools are clean and whose eleventh is known malicious is a malicious
148
+ * server, and the record on the server itself is often `unknown` — a local
149
+ * command has no public coordinate for the advisory list to be about.
150
+ */
151
+ export function failClosed(verdict, override) {
152
+ if (override === 'allow')
153
+ return false;
154
+ if (override === 'deny')
155
+ return true;
156
+ return verdict === 'malicious';
157
+ }
158
+ //# sourceMappingURL=policy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"policy.js","sourceRoot":"","sources":["../../../src/mcp/verify/policy.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,27 @@
1
+ /**
2
+ * A few things at a time, and no dependency to do it.
3
+ *
4
+ * Used where a list of independent calls would otherwise run one after the
5
+ * other — the slices of an audit, the servers of a scan — and where running
6
+ * them all at once would be as wrong: a backend has a request cap, and a
7
+ * hundred stdio servers spawned together is a fork bomb with a security
8
+ * label on it.
9
+ */
10
+ /**
11
+ * Runs `worker` over `items`, at most `concurrency` at a time, in order of
12
+ * start. Resolves when every item is done; a worker that throws fails the
13
+ * whole run, so a worker that must not takes care of its own errors.
14
+ */
15
+ export async function runPool(items, concurrency, worker) {
16
+ const width = Math.max(1, Math.min(Math.floor(concurrency), items.length));
17
+ let next = 0;
18
+ const lane = async () => {
19
+ while (next < items.length) {
20
+ const index = next;
21
+ next += 1;
22
+ await worker(items[index], index);
23
+ }
24
+ };
25
+ await Promise.all(Array.from({ length: width }, () => lane()));
26
+ }
27
+ //# sourceMappingURL=pool.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pool.js","sourceRoot":"","sources":["../../../src/mcp/verify/pool.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,OAAO,CAC3B,KAAmB,EACnB,WAAmB,EACnB,MAAiD;IAEjD,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC;IAC3E,IAAI,IAAI,GAAG,CAAC,CAAC;IACb,MAAM,IAAI,GAAG,KAAK,IAAmB,EAAE;QACrC,OAAO,IAAI,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC;YAC3B,MAAM,KAAK,GAAG,IAAI,CAAC;YACnB,IAAI,IAAI,CAAC,CAAC;YACV,MAAM,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,KAAK,CAAC,CAAC;QACpC,CAAC;IACH,CAAC,CAAC;IACF,MAAM,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,EAAE,GAAG,EAAE,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;AACjE,CAAC"}
@@ -0,0 +1,44 @@
1
+ /**
2
+ * The verify token for a command a HUMAN typed — renewed when it expired.
3
+ *
4
+ * Kept apart from `token.ts` on purpose: that module reads exactly two
5
+ * sources (the environment and our own store) and a guard test holds it to
6
+ * that. The renewal goes through `getValidAccessToken`, which talks to Logto;
7
+ * a module the proxy imports must not.
8
+ */
9
+ import { getValidAccessToken } from '../../auth/auth.js';
10
+ import { TOKEN_ENV_VARS, resolveVerifyToken } from './token.js';
11
+ /**
12
+ * A usable token for a command a HUMAN typed (`scan`, `connect`, `org`),
13
+ * renewed when it expired.
14
+ *
15
+ * `resolveVerifyToken` reads and never refreshes, which is right for the
16
+ * proxy (see `verifyTokenResolver`) and wrong at the keyboard: on 2026-09-16
17
+ * a scan started two seconds before the stored token's expiry, and every row
18
+ * after the second read `unverified` while a good refresh token sat in the
19
+ * store. The hooks already refresh through the same path; this is the CLI
20
+ * catching up.
21
+ *
22
+ * The environment still wins, and a refresh that cannot happen — nothing
23
+ * stored, no refresh token, a rejected one, a network failure — degrades to
24
+ * the plain read: a still-valid token is still worth sending, and `null`
25
+ * stays an ordinary outcome, never an exception.
26
+ */
27
+ export async function resolveVerifyTokenWithRefresh(mcpName, deps = {}) {
28
+ const env = deps.env ?? process.env;
29
+ for (const name of TOKEN_ENV_VARS) {
30
+ const value = env[name];
31
+ if (value && value.trim() !== '')
32
+ return value.trim();
33
+ }
34
+ try {
35
+ const fresh = await (deps.refreshFn ?? getValidAccessToken)(mcpName);
36
+ if (fresh)
37
+ return fresh;
38
+ }
39
+ catch {
40
+ /* login required, or the renewal failed: what the store holds decides */
41
+ }
42
+ return resolveVerifyToken(mcpName, deps);
43
+ }
44
+ //# sourceMappingURL=token-refresh.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"token-refresh.js","sourceRoot":"","sources":["../../../src/mcp/verify/token-refresh.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAEzD,OAAO,EAAE,cAAc,EAAE,kBAAkB,EAAwB,MAAM,YAAY,CAAC;AAWtF;;;;;;;;;;;;;;;GAeG;AACH,MAAM,CAAC,KAAK,UAAU,6BAA6B,CACjD,OAAe,EACf,OAA4B,EAAE;IAE9B,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,IAAI,OAAO,CAAC,GAAG,CAAC;IACpC,KAAK,MAAM,IAAI,IAAI,cAAc,EAAE,CAAC;QAClC,MAAM,KAAK,GAAG,GAAG,CAAC,IAAI,CAAC,CAAC;QACxB,IAAI,KAAK,IAAI,KAAK,CAAC,IAAI,EAAE,KAAK,EAAE;YAAE,OAAO,KAAK,CAAC,IAAI,EAAE,CAAC;IACxD,CAAC;IACD,IAAI,CAAC;QACH,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,SAAS,IAAI,mBAAmB,CAAC,CAAC,OAAO,CAAC,CAAC;QACrE,IAAI,KAAK;YAAE,OAAO,KAAK,CAAC;IAC1B,CAAC;IAAC,MAAM,CAAC;QACP,yEAAyE;IAC3E,CAAC;IACD,OAAO,kBAAkB,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;AAC3C,CAAC"}
@@ -0,0 +1,67 @@
1
+ /**
2
+ * The proxy's own CybeDefend token, and where it refuses to look for it.
3
+ *
4
+ * `api.ts` came off the legacy branch with `resolveTokenWithRefresh` as its
5
+ * default resolver, and that function's fallback reads the keychains of
6
+ * Claude Code and Codex. The plan forbids the proxy from reading another
7
+ * client's credential store — the entries are theirs, the user consented to
8
+ * them for that client, and a proxy that helps itself to them is exactly what
9
+ * this product exists to stop other people doing.
10
+ *
11
+ * So the proxy injects this instead. Two places, both ours:
12
+ *
13
+ * 1. the environment (`CYBEDEFEND_TOKEN`, then `VIBEDEFEND_TOKEN`), which
14
+ * is how CI supplies one;
15
+ * 2. `~/.cybedefend/`, written by `vibedefend login`.
16
+ *
17
+ * No refresh, and none is missing: a token that expired means verification
18
+ * degrades to `unknown` for this session, which is a fail-open the plan
19
+ * already provides for, and `vibedefend login` is a command the user runs
20
+ * when they choose to.
21
+ */
22
+ import { loadAuth } from '../../auth/auth-store.js';
23
+ /** Read before the store, so CI can override without a login. */
24
+ export const TOKEN_ENV_VARS = ['CYBEDEFEND_TOKEN', 'VIBEDEFEND_TOKEN'];
25
+ /**
26
+ * A usable token, or `null`.
27
+ *
28
+ * `null` is an ordinary outcome, not an error: most of the time it means
29
+ * nobody has run `vibedefend login` yet, and the caller turns it into
30
+ * "unverified" rather than into a failure.
31
+ */
32
+ export function resolveVerifyToken(mcpName, deps = {}) {
33
+ const env = deps.env ?? process.env;
34
+ for (const name of TOKEN_ENV_VARS) {
35
+ const value = env[name];
36
+ if (value && value.trim() !== '')
37
+ return value.trim();
38
+ }
39
+ try {
40
+ const stored = (deps.loadAuthFn ?? loadAuth)(mcpName);
41
+ if (!stored)
42
+ return null;
43
+ const now = (deps.now ?? Date.now)();
44
+ // `expiresAt` already carries its own margin. An expired token would cost
45
+ // a round trip and come back 401, which the caller cannot repair without
46
+ // a login it must not start on its own.
47
+ if (typeof stored.expiresAt === 'number' && stored.expiresAt <= now)
48
+ return null;
49
+ return stored.accessToken || null;
50
+ }
51
+ catch {
52
+ return null;
53
+ }
54
+ }
55
+ /**
56
+ * What `api.ts` calls after a 401.
57
+ *
58
+ * It re-reads the same two places rather than refreshing. The proxy has no
59
+ * business running an OAuth refresh against our own gateway from inside an
60
+ * agent session: if `vibedefend login` ran in another terminal in the
61
+ * meantime, this picks the new token up; if not, the 401 stands and
62
+ * verification degrades.
63
+ */
64
+ export function verifyTokenResolver(deps = {}) {
65
+ return async (mcpName) => resolveVerifyToken(mcpName, deps);
66
+ }
67
+ //# sourceMappingURL=token.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"token.js","sourceRoot":"","sources":["../../../src/mcp/verify/token.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,OAAO,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAC;AAEpD,iEAAiE;AACjE,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,kBAAkB,EAAE,kBAAkB,CAAU,CAAC;AAShF;;;;;;GAMG;AACH,MAAM,UAAU,kBAAkB,CAAC,OAAe,EAAE,OAAwB,EAAE;IAC5E,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,IAAI,OAAO,CAAC,GAAG,CAAC;IACpC,KAAK,MAAM,IAAI,IAAI,cAAc,EAAE,CAAC;QAClC,MAAM,KAAK,GAAG,GAAG,CAAC,IAAI,CAAC,CAAC;QACxB,IAAI,KAAK,IAAI,KAAK,CAAC,IAAI,EAAE,KAAK,EAAE;YAAE,OAAO,KAAK,CAAC,IAAI,EAAE,CAAC;IACxD,CAAC;IAED,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,CAAC,IAAI,CAAC,UAAU,IAAI,QAAQ,CAAC,CAAC,OAAO,CAAC,CAAC;QACtD,IAAI,CAAC,MAAM;YAAE,OAAO,IAAI,CAAC;QACzB,MAAM,GAAG,GAAG,CAAC,IAAI,CAAC,GAAG,IAAI,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;QACrC,0EAA0E;QAC1E,yEAAyE;QACzE,wCAAwC;QACxC,IAAI,OAAO,MAAM,CAAC,SAAS,KAAK,QAAQ,IAAI,MAAM,CAAC,SAAS,IAAI,GAAG;YAAE,OAAO,IAAI,CAAC;QACjF,OAAO,MAAM,CAAC,WAAW,IAAI,IAAI,CAAC;IACpC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,mBAAmB,CACjC,OAAwB,EAAE;IAE1B,OAAO,KAAK,EAAE,OAAe,EAAE,EAAE,CAAC,kBAAkB,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;AACtE,CAAC"}