@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.
- package/README.md +77 -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 +24873 -5068
- 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 +126 -53
- package/dist/hooks/runtime/emit.js.map +1 -1
- package/dist/hooks/runtime/fetch-rules.js +27 -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 +21 -6
- package/dist/hooks/runtime/pre-compact.js.map +1 -1
- package/dist/hooks/runtime/resolve.js +108 -17
- 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 +226 -0
- package/dist/hooks/runtime/session-activation.js.map +1 -0
- package/dist/hooks/runtime/session-review.js +174 -46
- package/dist/hooks/runtime/session-review.js.map +1 -1
- package/dist/hooks/runtime/session-start.js +35 -4
- 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 +8 -2
- 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 +99 -14
- 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,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"}
|