@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.
- package/README.md +67 -2
- package/dist/api/gateway-request.js +49 -0
- package/dist/api/gateway-request.js.map +1 -0
- package/dist/api/projects.js +67 -0
- package/dist/api/projects.js.map +1 -1
- package/dist/api/repository-link.js +157 -0
- package/dist/api/repository-link.js.map +1 -0
- package/dist/auth/auth-store.js +107 -25
- package/dist/auth/auth-store.js.map +1 -1
- package/dist/auth/auth.js +21 -0
- package/dist/auth/auth.js.map +1 -1
- package/dist/clients/cursor.js +5 -1
- package/dist/clients/cursor.js.map +1 -1
- package/dist/clients/vscode.js +38 -29
- package/dist/clients/vscode.js.map +1 -1
- package/dist/commands/mcp/allow.js +219 -0
- package/dist/commands/mcp/allow.js.map +1 -0
- package/dist/commands/mcp/connect.js +337 -0
- package/dist/commands/mcp/connect.js.map +1 -0
- package/dist/commands/mcp/index.js +170 -0
- package/dist/commands/mcp/index.js.map +1 -0
- package/dist/commands/mcp/proxy.js +264 -0
- package/dist/commands/mcp/proxy.js.map +1 -0
- package/dist/commands/mcp/scan.js +349 -0
- package/dist/commands/mcp/scan.js.map +1 -0
- package/dist/commands/mcp/sync.js +215 -0
- package/dist/commands/mcp/sync.js.map +1 -0
- package/dist/config.js +6 -0
- package/dist/config.js.map +1 -1
- package/dist/diagnostics.js +78 -12
- package/dist/diagnostics.js.map +1 -1
- package/dist/doctor.js +19 -0
- package/dist/doctor.js.map +1 -1
- package/dist/guards-evaluator/analysis-types.js +27 -0
- package/dist/guards-evaluator/analysis-types.js.map +1 -0
- package/dist/guards-evaluator/command-analysis.js +572 -0
- package/dist/guards-evaluator/command-analysis.js.map +1 -0
- package/dist/guards-evaluator/command-view.js +586 -0
- package/dist/guards-evaluator/command-view.js.map +1 -0
- package/dist/guards-evaluator/env.js +11 -0
- package/dist/guards-evaluator/env.js.map +1 -0
- package/dist/guards-evaluator/evaluate.js +91 -41
- package/dist/guards-evaluator/evaluate.js.map +1 -1
- package/dist/guards-evaluator/index.js +2 -1
- package/dist/guards-evaluator/index.js.map +1 -1
- package/dist/guards-evaluator/inline-code.js +259 -0
- package/dist/guards-evaluator/inline-code.js.map +1 -0
- package/dist/guards-evaluator/normalize.js +716 -0
- package/dist/guards-evaluator/normalize.js.map +1 -0
- package/dist/guards-evaluator/preprocess.js +13 -5
- package/dist/guards-evaluator/preprocess.js.map +1 -1
- package/dist/guards-evaluator/redact.js +19 -0
- package/dist/guards-evaluator/redact.js.map +1 -1
- package/dist/guards-evaluator/regex.js +25 -3
- package/dist/guards-evaluator/regex.js.map +1 -1
- package/dist/guards-evaluator/safe-commands.js +155 -0
- package/dist/guards-evaluator/safe-commands.js.map +1 -0
- package/dist/guards-evaluator/triage.js +315 -0
- package/dist/guards-evaluator/triage.js.map +1 -0
- package/dist/hook-runner.js +24679 -5079
- package/dist/hooks/install.js +38 -0
- package/dist/hooks/install.js.map +1 -1
- package/dist/hooks/runtime/config.js +38 -0
- package/dist/hooks/runtime/config.js.map +1 -1
- package/dist/hooks/runtime/emit.js +94 -61
- package/dist/hooks/runtime/emit.js.map +1 -1
- package/dist/hooks/runtime/fetch-rules.js +22 -7
- package/dist/hooks/runtime/fetch-rules.js.map +1 -1
- package/dist/hooks/runtime/gateway-http-error.js +34 -0
- package/dist/hooks/runtime/gateway-http-error.js.map +1 -0
- package/dist/hooks/runtime/guard-rules-cache.js +139 -17
- package/dist/hooks/runtime/guard-rules-cache.js.map +1 -1
- package/dist/hooks/runtime/guard-violations-buffer.js +321 -48
- package/dist/hooks/runtime/guard-violations-buffer.js.map +1 -1
- package/dist/hooks/runtime/judge-cache.js +6 -0
- package/dist/hooks/runtime/judge-cache.js.map +1 -1
- package/dist/hooks/runtime/judge-triage.js +193 -0
- package/dist/hooks/runtime/judge-triage.js.map +1 -0
- package/dist/hooks/runtime/judge.js +7 -1
- package/dist/hooks/runtime/judge.js.map +1 -1
- package/dist/hooks/runtime/pre-compact.js +12 -6
- package/dist/hooks/runtime/pre-compact.js.map +1 -1
- package/dist/hooks/runtime/resolve.js +90 -8
- package/dist/hooks/runtime/resolve.js.map +1 -1
- package/dist/hooks/runtime/retry-policy.js +57 -0
- package/dist/hooks/runtime/retry-policy.js.map +1 -0
- package/dist/hooks/runtime/review-sessions.js +74 -0
- package/dist/hooks/runtime/review-sessions.js.map +1 -0
- package/dist/hooks/runtime/script-resolve.js +695 -0
- package/dist/hooks/runtime/script-resolve.js.map +1 -0
- package/dist/hooks/runtime/session-activation.js +5 -1
- package/dist/hooks/runtime/session-activation.js.map +1 -1
- package/dist/hooks/runtime/session-review.js +163 -45
- package/dist/hooks/runtime/session-review.js.map +1 -1
- package/dist/hooks/runtime/session-start.js +2 -1
- package/dist/hooks/runtime/session-start.js.map +1 -1
- package/dist/hooks/runtime/session-writes.js +232 -0
- package/dist/hooks/runtime/session-writes.js.map +1 -0
- package/dist/hooks/runtime/sniff.js +26 -7
- package/dist/hooks/runtime/sniff.js.map +1 -1
- package/dist/hooks/runtime/target-resolve.js +336 -0
- package/dist/hooks/runtime/target-resolve.js.map +1 -0
- package/dist/hooks/runtime/unmapped-tools.js +93 -0
- package/dist/hooks/runtime/unmapped-tools.js.map +1 -0
- package/dist/hooks/runtime/usage-block.js +4 -0
- package/dist/hooks/runtime/usage-block.js.map +1 -1
- package/dist/hooks/runtime/user-prompt-submit.js +2 -1
- package/dist/hooks/runtime/user-prompt-submit.js.map +1 -1
- package/dist/hooks/runtime/wire-text.js +41 -0
- package/dist/hooks/runtime/wire-text.js.map +1 -0
- package/dist/hooks/runtime/work-dir.js +84 -0
- package/dist/hooks/runtime/work-dir.js.map +1 -0
- package/dist/index.js +45 -12
- package/dist/index.js.map +1 -1
- package/dist/install.js +174 -11
- package/dist/install.js.map +1 -1
- package/dist/link.js +74 -48
- package/dist/link.js.map +1 -1
- package/dist/login.js +77 -11
- package/dist/login.js.map +1 -1
- package/dist/mcp/auth/agent-sign-ins.js +174 -0
- package/dist/mcp/auth/agent-sign-ins.js.map +1 -0
- package/dist/mcp/auth/callback.js +200 -0
- package/dist/mcp/auth/callback.js.map +1 -0
- package/dist/mcp/auth/connect.js +392 -0
- package/dist/mcp/auth/connect.js.map +1 -0
- package/dist/mcp/auth/discovery.js +399 -0
- package/dist/mcp/auth/discovery.js.map +1 -0
- package/dist/mcp/auth/oauth.js +326 -0
- package/dist/mcp/auth/oauth.js.map +1 -0
- package/dist/mcp/auth/probe.js +128 -0
- package/dist/mcp/auth/probe.js.map +1 -0
- package/dist/mcp/auth/secrets.js +446 -0
- package/dist/mcp/auth/secrets.js.map +1 -0
- package/dist/mcp/auth/store.js +260 -0
- package/dist/mcp/auth/store.js.map +1 -0
- package/dist/mcp/auth/tokenSource.js +55 -0
- package/dist/mcp/auth/tokenSource.js.map +1 -0
- package/dist/mcp/config/atomicWrite.js +98 -0
- package/dist/mcp/config/atomicWrite.js.map +1 -0
- package/dist/mcp/config/clients.js +171 -0
- package/dist/mcp/config/clients.js.map +1 -0
- package/dist/mcp/config/cybedefend-servers.js +48 -0
- package/dist/mcp/config/cybedefend-servers.js.map +1 -0
- package/dist/mcp/config/discover.js +114 -0
- package/dist/mcp/config/discover.js.map +1 -0
- package/dist/mcp/config/hook.js +123 -0
- package/dist/mcp/config/hook.js.map +1 -0
- package/dist/mcp/config/index.js +11 -0
- package/dist/mcp/config/index.js.map +1 -0
- package/dist/mcp/config/install.js +132 -0
- package/dist/mcp/config/install.js.map +1 -0
- package/dist/mcp/config/installed.js +37 -0
- package/dist/mcp/config/installed.js.map +1 -0
- package/dist/mcp/config/jsonEdit.js +119 -0
- package/dist/mcp/config/jsonEdit.js.map +1 -0
- package/dist/mcp/config/lock.js +102 -0
- package/dist/mcp/config/lock.js.map +1 -0
- package/dist/mcp/config/proxyBinary.js +211 -0
- package/dist/mcp/config/proxyBinary.js.map +1 -0
- package/dist/mcp/config/rewrite.js +388 -0
- package/dist/mcp/config/rewrite.js.map +1 -0
- package/dist/mcp/config/types.js +34 -0
- package/dist/mcp/config/types.js.map +1 -0
- package/dist/mcp/proxy/audit.js +83 -0
- package/dist/mcp/proxy/audit.js.map +1 -0
- package/dist/mcp/proxy/authInterceptor.js +119 -0
- package/dist/mcp/proxy/authInterceptor.js.map +1 -0
- package/dist/mcp/proxy/expand.js +93 -0
- package/dist/mcp/proxy/expand.js.map +1 -0
- package/dist/mcp/proxy/inject.js +397 -0
- package/dist/mcp/proxy/inject.js.map +1 -0
- package/dist/mcp/proxy/intercept.js +37 -0
- package/dist/mcp/proxy/intercept.js.map +1 -0
- package/dist/mcp/proxy/jsonrpc.js +153 -0
- package/dist/mcp/proxy/jsonrpc.js.map +1 -0
- package/dist/mcp/proxy/log.js +109 -0
- package/dist/mcp/proxy/log.js.map +1 -0
- package/dist/mcp/proxy/paths.js +53 -0
- package/dist/mcp/proxy/paths.js.map +1 -0
- package/dist/mcp/proxy/proxy.js +510 -0
- package/dist/mcp/proxy/proxy.js.map +1 -0
- package/dist/mcp/proxy/spec.js +203 -0
- package/dist/mcp/proxy/spec.js.map +1 -0
- package/dist/mcp/proxy/sse.js +77 -0
- package/dist/mcp/proxy/sse.js.map +1 -0
- package/dist/mcp/proxy/upstream.js +2 -0
- package/dist/mcp/proxy/upstream.js.map +1 -0
- package/dist/mcp/proxy/upstreamHttp.js +499 -0
- package/dist/mcp/proxy/upstreamHttp.js.map +1 -0
- package/dist/mcp/proxy/upstreamStdio.js +188 -0
- package/dist/mcp/proxy/upstreamStdio.js.map +1 -0
- package/dist/mcp/proxy/upstreams.js +143 -0
- package/dist/mcp/proxy/upstreams.js.map +1 -0
- package/dist/mcp/proxy/verifyInterceptor.js +559 -0
- package/dist/mcp/proxy/verifyInterceptor.js.map +1 -0
- package/dist/mcp/proxy/windowsCommand.js +99 -0
- package/dist/mcp/proxy/windowsCommand.js.map +1 -0
- package/dist/mcp/scan/args.js +228 -0
- package/dist/mcp/scan/args.js.map +1 -0
- package/dist/mcp/scan/results.js +253 -0
- package/dist/mcp/scan/results.js.map +1 -0
- package/dist/mcp/scan/types.js +40 -0
- package/dist/mcp/scan/types.js.map +1 -0
- package/dist/mcp/verify/api.js +475 -0
- package/dist/mcp/verify/api.js.map +1 -0
- package/dist/mcp/verify/backend.js +143 -0
- package/dist/mcp/verify/backend.js.map +1 -0
- package/dist/mcp/verify/cache.js +182 -0
- package/dist/mcp/verify/cache.js.map +1 -0
- package/dist/mcp/verify/contract-errors.js +198 -0
- package/dist/mcp/verify/contract-errors.js.map +1 -0
- package/dist/mcp/verify/digest.js +91 -0
- package/dist/mcp/verify/digest.js.map +1 -0
- package/dist/mcp/verify/factory.js +56 -0
- package/dist/mcp/verify/factory.js.map +1 -0
- package/dist/mcp/verify/identity.js +173 -0
- package/dist/mcp/verify/identity.js.map +1 -0
- package/dist/mcp/verify/policy-store.js +63 -0
- package/dist/mcp/verify/policy-store.js.map +1 -0
- package/dist/mcp/verify/policy.js +158 -0
- package/dist/mcp/verify/policy.js.map +1 -0
- package/dist/mcp/verify/pool.js +27 -0
- package/dist/mcp/verify/pool.js.map +1 -0
- package/dist/mcp/verify/token-refresh.js +44 -0
- package/dist/mcp/verify/token-refresh.js.map +1 -0
- package/dist/mcp/verify/token.js +67 -0
- package/dist/mcp/verify/token.js.map +1 -0
- package/dist/mcp/verify/verifier.js +1009 -0
- package/dist/mcp/verify/verifier.js.map +1 -0
- package/dist/org-gate.js +1 -1
- package/dist/org-gate.js.map +1 -1
- package/dist/org.js +146 -0
- package/dist/org.js.map +1 -0
- package/dist/project-create.js +129 -0
- package/dist/project-create.js.map +1 -0
- package/dist/prompts.js +21 -0
- package/dist/prompts.js.map +1 -1
- package/dist/re2.wasm +0 -0
- package/dist/status.js +61 -0
- package/dist/status.js.map +1 -1
- package/dist/uninstall.js +49 -4
- package/dist/uninstall.js.map +1 -1
- package/dist/utils.js +62 -6
- package/dist/utils.js.map +1 -1
- 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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"}
|