@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,174 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The sign-ins the user's agents already hold for an MCP server.
|
|
3
|
+
*
|
|
4
|
+
* Putting the proxy in front of a server used to cost a second OAuth sign-in
|
|
5
|
+
* in the browser, for every server Claude Code or Codex was already signed in
|
|
6
|
+
* to. `vibedefend connect` now takes over the agent's sign-in first: once the
|
|
7
|
+
* entry is wrapped, the agent reaches that server through the proxy and no
|
|
8
|
+
* longer uses its own copy.
|
|
9
|
+
*
|
|
10
|
+
* What is read, and only for the EXACT server asked for (canonical URL, as
|
|
11
|
+
* the proxy's own store is keyed):
|
|
12
|
+
*
|
|
13
|
+
* - Claude Code: the `mcpOAuth` map of the `Claude Code-credentials` macOS
|
|
14
|
+
* keychain item, and of `~/.claude/.credentials.json` (where Claude Code
|
|
15
|
+
* keeps it off macOS; `CLAUDE_CONFIG_DIR` moves it). Claude Code writes
|
|
16
|
+
* that item with the `security` tool, so reading it asks nothing.
|
|
17
|
+
* - Codex: its file store, `$CODEX_HOME/.credentials.json` (default
|
|
18
|
+
* `~/.codex`). Never its keychain items: Codex writes them through the
|
|
19
|
+
* native keychain API, so `security` cannot read them without macOS asking
|
|
20
|
+
* the user, once per server (measured 2026-10-20: errSecUserCanceled).
|
|
21
|
+
*
|
|
22
|
+
* Cursor and Windsurf keep theirs in their own databases: not read.
|
|
23
|
+
*
|
|
24
|
+
* `VIBEDEFEND_AGENT_SIGN_INS=off` reads none of it (the test suite sets it:
|
|
25
|
+
* no test may read the developer's credentials). `vibedefend connect
|
|
26
|
+
* --no-reuse` does the same for one run.
|
|
27
|
+
*
|
|
28
|
+
* Never throws, never logs: an unreadable store is an empty one, and what is
|
|
29
|
+
* read is a credential.
|
|
30
|
+
*/
|
|
31
|
+
import { execFileSync } from 'node:child_process';
|
|
32
|
+
import { readFileSync } from 'node:fs';
|
|
33
|
+
import { homedir, userInfo } from 'node:os';
|
|
34
|
+
import { join } from 'node:path';
|
|
35
|
+
import { canonicalMcpUrl } from './oauth.js';
|
|
36
|
+
/** How each agent is named to the user. */
|
|
37
|
+
export const AGENT_SIGN_IN_LABELS = {
|
|
38
|
+
'claude-code': 'Claude Code',
|
|
39
|
+
codex: 'Codex',
|
|
40
|
+
};
|
|
41
|
+
const CLAUDE_CODE_KEYCHAIN_SERVICE = 'Claude Code-credentials';
|
|
42
|
+
/** Every sign-in the agents hold for `serverUrl`, Claude Code first. */
|
|
43
|
+
export function findAgentSignIns(serverUrl, deps = {}) {
|
|
44
|
+
const wanted = canonicalMcpUrl(serverUrl);
|
|
45
|
+
if (!wanted)
|
|
46
|
+
return [];
|
|
47
|
+
const env = deps.env ?? process.env;
|
|
48
|
+
if (env.VIBEDEFEND_AGENT_SIGN_INS === 'off')
|
|
49
|
+
return [];
|
|
50
|
+
const home = env.HOME ?? env.USERPROFILE ?? homedir();
|
|
51
|
+
const found = [];
|
|
52
|
+
const add = (signIn) => {
|
|
53
|
+
if (!signIn)
|
|
54
|
+
return;
|
|
55
|
+
const duplicate = found.some((known) => known.clientId === signIn.clientId &&
|
|
56
|
+
known.accessToken === signIn.accessToken &&
|
|
57
|
+
known.refreshToken === signIn.refreshToken);
|
|
58
|
+
if (!duplicate)
|
|
59
|
+
found.push(signIn);
|
|
60
|
+
};
|
|
61
|
+
if ((deps.platform ?? process.platform) === 'darwin') {
|
|
62
|
+
const read = deps.readKeychain ?? readMacOSKeychain;
|
|
63
|
+
const blob = quietly(() => read(CLAUDE_CODE_KEYCHAIN_SERVICE, deps.username ?? loginName()));
|
|
64
|
+
for (const signIn of claudeCodeSignIns(blob, wanted))
|
|
65
|
+
add(signIn);
|
|
66
|
+
}
|
|
67
|
+
const claudeDir = env.CLAUDE_CONFIG_DIR ?? join(home, '.claude');
|
|
68
|
+
for (const signIn of claudeCodeSignIns(readQuietly(join(claudeDir, '.credentials.json')), wanted))
|
|
69
|
+
add(signIn);
|
|
70
|
+
const codexDir = env.CODEX_HOME ?? join(home, '.codex');
|
|
71
|
+
for (const signIn of codexSignIns(readQuietly(join(codexDir, '.credentials.json')), wanted))
|
|
72
|
+
add(signIn);
|
|
73
|
+
return found;
|
|
74
|
+
}
|
|
75
|
+
function claudeCodeSignIns(blob, wanted) {
|
|
76
|
+
const data = parseObject(blob);
|
|
77
|
+
const entries = data && isObject(data.mcpOAuth) ? Object.values(data.mcpOAuth) : [];
|
|
78
|
+
return entries.flatMap((entry) => {
|
|
79
|
+
if (!isObject(entry) || !sameServer(entry.serverUrl, wanted))
|
|
80
|
+
return [];
|
|
81
|
+
const signIn = signInFrom('claude-code', {
|
|
82
|
+
accessToken: entry.accessToken,
|
|
83
|
+
refreshToken: entry.refreshToken,
|
|
84
|
+
expiresAt: entry.expiresAt,
|
|
85
|
+
clientId: entry.clientId,
|
|
86
|
+
clientSecret: entry.clientSecret,
|
|
87
|
+
issuer: entry.issuer,
|
|
88
|
+
});
|
|
89
|
+
return signIn ? [signIn] : [];
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
function codexSignIns(blob, wanted) {
|
|
93
|
+
const data = parseObject(blob);
|
|
94
|
+
return Object.values(data ?? {}).flatMap((entry) => {
|
|
95
|
+
if (!isObject(entry) || !sameServer(entry.server_url ?? entry.url, wanted))
|
|
96
|
+
return [];
|
|
97
|
+
const response = isObject(entry.token_response) ? entry.token_response : {};
|
|
98
|
+
const signIn = signInFrom('codex', {
|
|
99
|
+
accessToken: entry.access_token ?? response.access_token,
|
|
100
|
+
refreshToken: entry.refresh_token ?? response.refresh_token,
|
|
101
|
+
expiresAt: entry.expires_at,
|
|
102
|
+
clientId: entry.client_id,
|
|
103
|
+
clientSecret: entry.client_secret,
|
|
104
|
+
issuer: null,
|
|
105
|
+
});
|
|
106
|
+
return signIn ? [signIn] : [];
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
function signInFrom(source, raw) {
|
|
110
|
+
const clientId = text(raw.clientId);
|
|
111
|
+
const accessToken = text(raw.accessToken);
|
|
112
|
+
const refreshToken = text(raw.refreshToken);
|
|
113
|
+
if (!clientId || (!accessToken && !refreshToken))
|
|
114
|
+
return null;
|
|
115
|
+
return {
|
|
116
|
+
source,
|
|
117
|
+
accessToken,
|
|
118
|
+
refreshToken,
|
|
119
|
+
expiresAt: epochMs(raw.expiresAt),
|
|
120
|
+
clientId,
|
|
121
|
+
clientSecret: text(raw.clientSecret),
|
|
122
|
+
issuer: text(raw.issuer),
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
function sameServer(url, wanted) {
|
|
126
|
+
return typeof url === 'string' && canonicalMcpUrl(url) === wanted;
|
|
127
|
+
}
|
|
128
|
+
/** Claude Code and Codex record milliseconds; a value below 1e11 can only be seconds. */
|
|
129
|
+
function epochMs(value) {
|
|
130
|
+
if (typeof value !== 'number' || !Number.isFinite(value) || value <= 0)
|
|
131
|
+
return null;
|
|
132
|
+
return value < 1e11 ? value * 1000 : value;
|
|
133
|
+
}
|
|
134
|
+
function text(value) {
|
|
135
|
+
return typeof value === 'string' && value !== '' ? value : null;
|
|
136
|
+
}
|
|
137
|
+
function isObject(value) {
|
|
138
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
139
|
+
}
|
|
140
|
+
function parseObject(blob) {
|
|
141
|
+
if (!blob)
|
|
142
|
+
return null;
|
|
143
|
+
try {
|
|
144
|
+
const parsed = JSON.parse(blob);
|
|
145
|
+
return isObject(parsed) ? parsed : null;
|
|
146
|
+
}
|
|
147
|
+
catch {
|
|
148
|
+
return null;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
function quietly(read) {
|
|
152
|
+
try {
|
|
153
|
+
return read();
|
|
154
|
+
}
|
|
155
|
+
catch {
|
|
156
|
+
return null;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
function readQuietly(path) {
|
|
160
|
+
return quietly(() => readFileSync(path, 'utf8'));
|
|
161
|
+
}
|
|
162
|
+
function loginName() {
|
|
163
|
+
try {
|
|
164
|
+
return userInfo().username;
|
|
165
|
+
}
|
|
166
|
+
catch {
|
|
167
|
+
return undefined;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
function readMacOSKeychain(service, account) {
|
|
171
|
+
const blob = execFileSync('security', ['find-generic-password', '-s', service, ...(account === undefined ? [] : ['-a', account]), '-w'], { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'], timeout: 2000 }).trim();
|
|
172
|
+
return blob === '' ? null : blob;
|
|
173
|
+
}
|
|
174
|
+
//# sourceMappingURL=agent-sign-ins.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent-sign-ins.js","sourceRoot":"","sources":["../../../src/mcp/auth/agent-sign-ins.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The loopback callback server of the MCP authorization flow (RFC 8252).
|
|
3
|
+
*
|
|
4
|
+
* ─── Why `state` and `iss` are checked HERE ─────────────────────────────────
|
|
5
|
+
*
|
|
6
|
+
* `discovery.ts` binds the authorization server's metadata to the MCP server
|
|
7
|
+
* being asked. That check closes an INSTANCE of the mix-up, not the class:
|
|
8
|
+
* the attacker only has to host a 302 on THEIR OWN `authorization_endpoint`
|
|
9
|
+
* — an endpoint that opens in the BROWSER, never fetched by us, so
|
|
10
|
+
* structurally invisible to anything we could check on the metadata. The
|
|
11
|
+
* browser then lands on the REAL consent page of a genuine third party, with
|
|
12
|
+
* THEIR `client_id` and OUR `code_challenge`; that third party redirects to
|
|
13
|
+
* our loopback with a code that is VALID AT THEIR END. Without a further
|
|
14
|
+
* defence the exchange would send that code — and our `code_verifier` — to
|
|
15
|
+
* the attacker's SELF-DECLARED `token_endpoint`.
|
|
16
|
+
*
|
|
17
|
+
* Two standard defences answer that:
|
|
18
|
+
*
|
|
19
|
+
* • `state` — random, single-use, bound to THIS attempt. A response
|
|
20
|
+
* carrying an unknown or ALREADY CONSUMED state is rejected. It closes
|
|
21
|
+
* the class UNCONDITIONALLY as an anti-CSRF measure, and depends on no
|
|
22
|
+
* capability of the third party.
|
|
23
|
+
* • `iss` (RFC 9207) — the issuer announced BY THE AUTHORIZATION RESPONSE
|
|
24
|
+
* ITSELF, so by the REAL third party that handled it and never by a
|
|
25
|
+
* document the attacker controls, must be exactly the one discovery kept
|
|
26
|
+
* for this MCP server.
|
|
27
|
+
*
|
|
28
|
+
* `iss` only closes the class if the real third party actually sends `iss`,
|
|
29
|
+
* which nothing guarantees: most authorization servers deployed today do not
|
|
30
|
+
* implement RFC 9207. And `issParameterSupported` is itself read from the
|
|
31
|
+
* metadata of the authorization server the HOSTILE server designated — the
|
|
32
|
+
* attacker only has to OMIT the field for the guarantee never to arm, then
|
|
33
|
+
* aim at a genuine non-RFC-9207 third party. `state` does not cover that
|
|
34
|
+
* case either: the attacker's `authorization_endpoint` SEES our `state`
|
|
35
|
+
* before redirecting and can simply pass it along.
|
|
36
|
+
*
|
|
37
|
+
* There is therefore **no purely technical client-side check that closes this
|
|
38
|
+
* class when the AS does not emit `iss`** — which is precisely why RFC 9207
|
|
39
|
+
* exists. The decision is one of POLICY, taken in `connect.ts`: by default
|
|
40
|
+
* the flow warns and goes on when `issParameterSupported !== true` (RFC 9207
|
|
41
|
+
* is absent from nearly every public server measured, and the issuer is
|
|
42
|
+
* still the one discovery bound to the resource); `strictIssuer` — the
|
|
43
|
+
* `--strict-issuer` flag, or the organisation's policy (`strictIssuer`,
|
|
44
|
+
* served by the backend) — turns that into a refusal before any browser
|
|
45
|
+
* opens.
|
|
46
|
+
*
|
|
47
|
+
* Both checks live in this file, BEFORE the redirect promise resolves:
|
|
48
|
+
* `code` and `code_verifier` structurally CANNOT reach the exchange until
|
|
49
|
+
* they have passed. It is not a question of call order in the caller — the
|
|
50
|
+
* code does not exist anywhere else before that.
|
|
51
|
+
*
|
|
52
|
+
* Moved from `682d413:src/mcp/login.ts:194-396` and translated.
|
|
53
|
+
*/
|
|
54
|
+
import { createServer } from 'node:http';
|
|
55
|
+
/**
|
|
56
|
+
* Raised by the loopback callback server. Never a free-form string: a closed
|
|
57
|
+
* `kind`, so the caller can tell `issuer-mismatch` — the heart of the RFC
|
|
58
|
+
* 9207 defence — from the rest without parsing a message.
|
|
59
|
+
*/
|
|
60
|
+
export class AuthorizationRedirectError extends Error {
|
|
61
|
+
kind;
|
|
62
|
+
constructor(kind, message) {
|
|
63
|
+
super(message);
|
|
64
|
+
this.kind = kind;
|
|
65
|
+
this.name = 'AuthorizationRedirectError';
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
const CALLBACK_PATH = '/mcp-oauth-callback';
|
|
69
|
+
/**
|
|
70
|
+
* Strips one or more trailing slashes. RFC 9207 and RFC 8414 both tolerate an
|
|
71
|
+
* issuer with or without a trailing `/` (the same normalisation `issuerId`
|
|
72
|
+
* applies in `discovery.ts`), and conforming third parties — Auth0 among
|
|
73
|
+
* others — commonly emit one. The CASE is left alone: RFC 9207 §2.4 mandates
|
|
74
|
+
* simple string comparison.
|
|
75
|
+
*/
|
|
76
|
+
function stripTrailingSlash(value) {
|
|
77
|
+
return value.replace(/\/+$/, '');
|
|
78
|
+
}
|
|
79
|
+
const SUCCESS_PAGE = '<!DOCTYPE html><html><head><meta charset="utf-8"><title>VibeDefend</title></head>' +
|
|
80
|
+
'<body style="font-family: system-ui, sans-serif; padding: 3rem; text-align: center">' +
|
|
81
|
+
'<h1>Authentication successful</h1>' +
|
|
82
|
+
'<p>VibeDefend has the token for this MCP server. You can close this tab.</p>' +
|
|
83
|
+
'</body></html>';
|
|
84
|
+
/**
|
|
85
|
+
* The real implementation: a loopback HTTP server (127.0.0.1 only, never
|
|
86
|
+
* 0.0.0.0) on a port the OS assigns. RFC 8252 recommends a random port for a
|
|
87
|
+
* native client, and `registerMcpClient` registers THIS exact `redirect_uri`
|
|
88
|
+
* dynamically, so no fixed port is needed — unlike `vibedefend login`, which
|
|
89
|
+
* targets a client pre-registered with Logto.
|
|
90
|
+
*
|
|
91
|
+
* Single use: once the response has been consumed, any further request — even
|
|
92
|
+
* with the right `state` — is refused rather than replayed.
|
|
93
|
+
*/
|
|
94
|
+
export async function startMcpAuthorizationCallbackServer(expectedState, expectedIssuer, issParameterRequired, options = {}) {
|
|
95
|
+
let settled = false;
|
|
96
|
+
let resolveRedirect;
|
|
97
|
+
let rejectRedirect;
|
|
98
|
+
const redirectPromise = new Promise((res, rej) => {
|
|
99
|
+
resolveRedirect = res;
|
|
100
|
+
rejectRedirect = rej;
|
|
101
|
+
});
|
|
102
|
+
const server = createServer((req, res) => {
|
|
103
|
+
const url = new URL(req.url ?? '/', 'http://127.0.0.1');
|
|
104
|
+
if (url.pathname !== CALLBACK_PATH) {
|
|
105
|
+
res.writeHead(404).end();
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
if (settled) {
|
|
109
|
+
res
|
|
110
|
+
.writeHead(400, { 'Content-Type': 'text/plain' })
|
|
111
|
+
.end('This authorization response was already used.');
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
// 1) `state` — anti-CSRF, single use. Checked BEFORE `error`: RFC 6749
|
|
115
|
+
// §4.1.2.1 requires `state` on an error response too, and a hostile MCP
|
|
116
|
+
// server could otherwise pass an anonymous `?error=access_denied` off as
|
|
117
|
+
// an EXPLICIT refusal by the user.
|
|
118
|
+
//
|
|
119
|
+
// What this ordering is NOT: protection against a third party denying a
|
|
120
|
+
// login in progress. Any page open in the browser that knows the port —
|
|
121
|
+
// and the port is in `redirect_uri` — burns the attempt identically with
|
|
122
|
+
// or without it; only the LABEL changes, and the real callback that
|
|
123
|
+
// follows gets a 400 "already used" either way. Continuing to listen
|
|
124
|
+
// after a suspicious request would widen the window rather than close it:
|
|
125
|
+
// an unknown `state` on this ephemeral port is already either a mistake
|
|
126
|
+
// or a probe, never something to give a second chance. What the ordering
|
|
127
|
+
// does buy is a CORRECT diagnosis (`state-mismatch`, not `denied`) when
|
|
128
|
+
// the request that burned the attempt never knew the state.
|
|
129
|
+
const state = url.searchParams.get('state');
|
|
130
|
+
if (!state || state !== expectedState) {
|
|
131
|
+
settled = true;
|
|
132
|
+
res.writeHead(400, { 'Content-Type': 'text/plain' }).end('Invalid or already-consumed state.');
|
|
133
|
+
rejectRedirect(new AuthorizationRedirectError('state-mismatch', 'authorization response carried an unknown or already-consumed state'));
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
const error = url.searchParams.get('error');
|
|
137
|
+
if (error) {
|
|
138
|
+
settled = true;
|
|
139
|
+
res.writeHead(400, { 'Content-Type': 'text/plain' }).end('Authorization was not granted.');
|
|
140
|
+
rejectRedirect(new AuthorizationRedirectError('denied', 'the user denied authorization, or the authorization server refused'));
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
// 2) `iss` (RFC 9207) — see the module header. An `iss` that is PRESENT
|
|
144
|
+
// and does not match is ALWAYS a rejection; an `iss` that is ABSENT is
|
|
145
|
+
// one only when the AS announced it would always send one. Compared on
|
|
146
|
+
// the NORMALISED form: a CONFORMING RFC 9207 issuer ending in `/` (the
|
|
147
|
+
// Auth0 pattern, `https://tenant.eu.auth0.com/`) was refused by an exact
|
|
148
|
+
// comparison, reproducing on this axis the flaw that condemned the origin
|
|
149
|
+
// check — refusing conforming deployments. The CASE stays compared as is:
|
|
150
|
+
// RFC 9207 §2.4 mandates simple string comparison, and `issuerId` in
|
|
151
|
+
// `discovery.ts` does not normalise it either.
|
|
152
|
+
const iss = url.searchParams.get('iss');
|
|
153
|
+
if (iss !== null) {
|
|
154
|
+
if (stripTrailingSlash(iss) !== stripTrailingSlash(expectedIssuer)) {
|
|
155
|
+
settled = true;
|
|
156
|
+
res.writeHead(400, { 'Content-Type': 'text/plain' }).end('Issuer mismatch.');
|
|
157
|
+
rejectRedirect(new AuthorizationRedirectError('issuer-mismatch', 'authorization response `iss` does not match the issuer discovered for this MCP server'));
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
else if (issParameterRequired) {
|
|
162
|
+
settled = true;
|
|
163
|
+
res
|
|
164
|
+
.writeHead(400, { 'Content-Type': 'text/plain' })
|
|
165
|
+
.end('Missing required issuer parameter.');
|
|
166
|
+
rejectRedirect(new AuthorizationRedirectError('issuer-mismatch', 'authorization server declares RFC 9207 support but the response is missing `iss`'));
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
const code = url.searchParams.get('code');
|
|
170
|
+
if (!code) {
|
|
171
|
+
settled = true;
|
|
172
|
+
res.writeHead(400, { 'Content-Type': 'text/plain' }).end('Missing authorization code.');
|
|
173
|
+
rejectRedirect(new AuthorizationRedirectError('missing-code', 'authorization response is missing the authorization code'));
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
settled = true;
|
|
177
|
+
res.writeHead(200, { 'Content-Type': 'text/html' }).end(SUCCESS_PAGE);
|
|
178
|
+
resolveRedirect({ code, iss });
|
|
179
|
+
});
|
|
180
|
+
const port = options.port ?? 0;
|
|
181
|
+
await new Promise((resolve, reject) => {
|
|
182
|
+
server.once('error', (error) => {
|
|
183
|
+
reject(port === 0
|
|
184
|
+
? error
|
|
185
|
+
: new Error(`cannot listen on 127.0.0.1:${port} for the authorization callback: ${error.message}`));
|
|
186
|
+
});
|
|
187
|
+
server.listen(port, '127.0.0.1', resolve);
|
|
188
|
+
});
|
|
189
|
+
const addr = server.address();
|
|
190
|
+
const stop = () => new Promise((resolve) => {
|
|
191
|
+
server.closeAllConnections?.();
|
|
192
|
+
server.close(() => resolve());
|
|
193
|
+
});
|
|
194
|
+
return { port: addr.port, redirectPromise, stop };
|
|
195
|
+
}
|
|
196
|
+
/** The `redirect_uri` for a callback server listening on `port`. */
|
|
197
|
+
export function callbackRedirectUri(port) {
|
|
198
|
+
return `http://127.0.0.1:${port}${CALLBACK_PATH}`;
|
|
199
|
+
}
|
|
200
|
+
//# sourceMappingURL=callback.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"callback.js","sourceRoot":"","sources":["../../../src/mcp/auth/callback.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoDG;AACH,OAAO,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AAoCzC;;;;GAIG;AACH,MAAM,OAAO,0BAA2B,SAAQ,KAAK;IAExC;IADX,YACW,IAAsE,EAC/E,OAAe;QAEf,KAAK,CAAC,OAAO,CAAC,CAAC;QAHN,SAAI,GAAJ,IAAI,CAAkE;QAI/E,IAAI,CAAC,IAAI,GAAG,4BAA4B,CAAC;IAC3C,CAAC;CACF;AAED,MAAM,aAAa,GAAG,qBAAqB,CAAC;AAE5C;;;;;;GAMG;AACH,SAAS,kBAAkB,CAAC,KAAa;IACvC,OAAO,KAAK,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;AACnC,CAAC;AAED,MAAM,YAAY,GAChB,mFAAmF;IACnF,sFAAsF;IACtF,oCAAoC;IACpC,8EAA8E;IAC9E,gBAAgB,CAAC;AAEnB;;;;;;;;;GASG;AACH,MAAM,CAAC,KAAK,UAAU,mCAAmC,CACvD,aAAqB,EACrB,cAAsB,EACtB,oBAA6B,EAC7B,UAAiC,EAAE;IAEnC,IAAI,OAAO,GAAG,KAAK,CAAC;IACpB,IAAI,eAAoD,CAAC;IACzD,IAAI,cAAmC,CAAC;IACxC,MAAM,eAAe,GAAG,IAAI,OAAO,CAAwB,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE;QACtE,eAAe,GAAG,GAAG,CAAC;QACtB,cAAc,GAAG,GAAG,CAAC;IACvB,CAAC,CAAC,CAAC;IAEH,MAAM,MAAM,GAAG,YAAY,CAAC,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE;QACvC,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,GAAG,IAAI,GAAG,EAAE,kBAAkB,CAAC,CAAC;QACxD,IAAI,GAAG,CAAC,QAAQ,KAAK,aAAa,EAAE,CAAC;YACnC,GAAG,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,CAAC;YACzB,OAAO;QACT,CAAC;QACD,IAAI,OAAO,EAAE,CAAC;YACZ,GAAG;iBACA,SAAS,CAAC,GAAG,EAAE,EAAE,cAAc,EAAE,YAAY,EAAE,CAAC;iBAChD,GAAG,CAAC,+CAA+C,CAAC,CAAC;YACxD,OAAO;QACT,CAAC;QAED,uEAAuE;QACvE,wEAAwE;QACxE,yEAAyE;QACzE,mCAAmC;QACnC,EAAE;QACF,wEAAwE;QACxE,wEAAwE;QACxE,yEAAyE;QACzE,oEAAoE;QACpE,qEAAqE;QACrE,0EAA0E;QAC1E,wEAAwE;QACxE,yEAAyE;QACzE,wEAAwE;QACxE,4DAA4D;QAC5D,MAAM,KAAK,GAAG,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QAC5C,IAAI,CAAC,KAAK,IAAI,KAAK,KAAK,aAAa,EAAE,CAAC;YACtC,OAAO,GAAG,IAAI,CAAC;YACf,GAAG,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,cAAc,EAAE,YAAY,EAAE,CAAC,CAAC,GAAG,CAAC,oCAAoC,CAAC,CAAC;YAC/F,cAAc,CACZ,IAAI,0BAA0B,CAC5B,gBAAgB,EAChB,qEAAqE,CACtE,CACF,CAAC;YACF,OAAO;QACT,CAAC;QAED,MAAM,KAAK,GAAG,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QAC5C,IAAI,KAAK,EAAE,CAAC;YACV,OAAO,GAAG,IAAI,CAAC;YACf,GAAG,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,cAAc,EAAE,YAAY,EAAE,CAAC,CAAC,GAAG,CAAC,gCAAgC,CAAC,CAAC;YAC3F,cAAc,CACZ,IAAI,0BAA0B,CAC5B,QAAQ,EACR,oEAAoE,CACrE,CACF,CAAC;YACF,OAAO;QACT,CAAC;QAED,wEAAwE;QACxE,uEAAuE;QACvE,uEAAuE;QACvE,uEAAuE;QACvE,yEAAyE;QACzE,0EAA0E;QAC1E,0EAA0E;QAC1E,qEAAqE;QACrE,+CAA+C;QAC/C,MAAM,GAAG,GAAG,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QACxC,IAAI,GAAG,KAAK,IAAI,EAAE,CAAC;YACjB,IAAI,kBAAkB,CAAC,GAAG,CAAC,KAAK,kBAAkB,CAAC,cAAc,CAAC,EAAE,CAAC;gBACnE,OAAO,GAAG,IAAI,CAAC;gBACf,GAAG,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,cAAc,EAAE,YAAY,EAAE,CAAC,CAAC,GAAG,CAAC,kBAAkB,CAAC,CAAC;gBAC7E,cAAc,CACZ,IAAI,0BAA0B,CAC5B,iBAAiB,EACjB,uFAAuF,CACxF,CACF,CAAC;gBACF,OAAO;YACT,CAAC;QACH,CAAC;aAAM,IAAI,oBAAoB,EAAE,CAAC;YAChC,OAAO,GAAG,IAAI,CAAC;YACf,GAAG;iBACA,SAAS,CAAC,GAAG,EAAE,EAAE,cAAc,EAAE,YAAY,EAAE,CAAC;iBAChD,GAAG,CAAC,oCAAoC,CAAC,CAAC;YAC7C,cAAc,CACZ,IAAI,0BAA0B,CAC5B,iBAAiB,EACjB,kFAAkF,CACnF,CACF,CAAC;YACF,OAAO;QACT,CAAC;QAED,MAAM,IAAI,GAAG,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QAC1C,IAAI,CAAC,IAAI,EAAE,CAAC;YACV,OAAO,GAAG,IAAI,CAAC;YACf,GAAG,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,cAAc,EAAE,YAAY,EAAE,CAAC,CAAC,GAAG,CAAC,6BAA6B,CAAC,CAAC;YACxF,cAAc,CACZ,IAAI,0BAA0B,CAC5B,cAAc,EACd,0DAA0D,CAC3D,CACF,CAAC;YACF,OAAO;QACT,CAAC;QAED,OAAO,GAAG,IAAI,CAAC;QACf,GAAG,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,cAAc,EAAE,WAAW,EAAE,CAAC,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC;QACtE,eAAe,CAAC,EAAE,IAAI,EAAE,GAAG,EAAE,CAAC,CAAC;IACjC,CAAC,CAAC,CAAC;IAEH,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,IAAI,CAAC,CAAC;IAC/B,MAAM,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QAC1C,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,KAAY,EAAE,EAAE;YACpC,MAAM,CACJ,IAAI,KAAK,CAAC;gBACR,CAAC,CAAC,KAAK;gBACP,CAAC,CAAC,IAAI,KAAK,CAAC,8BAA8B,IAAI,oCAAoC,KAAK,CAAC,OAAO,EAAE,CAAC,CACrG,CAAC;QACJ,CAAC,CAAC,CAAC;QACH,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,WAAW,EAAE,OAAO,CAAC,CAAC;IAC5C,CAAC,CAAC,CAAC;IAEH,MAAM,IAAI,GAAG,MAAM,CAAC,OAAO,EAAsB,CAAC;IAClD,MAAM,IAAI,GAAG,GAAkB,EAAE,CAC/B,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;QACtB,MAAM,CAAC,mBAAmB,EAAE,EAAE,CAAC;QAC/B,MAAM,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,OAAO,EAAE,CAAC,CAAC;IAChC,CAAC,CAAC,CAAC;IAEL,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,eAAe,EAAE,IAAI,EAAE,CAAC;AACpD,CAAC;AAED,oEAAoE;AACpE,MAAM,UAAU,mBAAmB,CAAC,IAAY;IAC9C,OAAO,oBAAoB,IAAI,GAAG,aAAa,EAAE,CAAC;AACpD,CAAC"}
|