@cybedefend/vibedefend 1.1.1
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/LICENSE +77 -0
- package/README.md +120 -0
- package/bin/vibedefend.js +19 -0
- package/dist/auth/auth-store.js +170 -0
- package/dist/auth/auth-store.js.map +1 -0
- package/dist/auth/auth.js +125 -0
- package/dist/auth/auth.js.map +1 -0
- package/dist/auth/callback-server.js +216 -0
- package/dist/auth/callback-server.js.map +1 -0
- package/dist/auth/pkce.js +31 -0
- package/dist/auth/pkce.js.map +1 -0
- package/dist/auth/token-exchange.js +83 -0
- package/dist/auth/token-exchange.js.map +1 -0
- package/dist/clients/claude-code.js +170 -0
- package/dist/clients/claude-code.js.map +1 -0
- package/dist/clients/codex.js +378 -0
- package/dist/clients/codex.js.map +1 -0
- package/dist/clients/cursor-guards-rules.js +94 -0
- package/dist/clients/cursor-guards-rules.js.map +1 -0
- package/dist/clients/cursor.js +172 -0
- package/dist/clients/cursor.js.map +1 -0
- package/dist/clients/detect.js +86 -0
- package/dist/clients/detect.js.map +1 -0
- package/dist/clients/registry.js +41 -0
- package/dist/clients/registry.js.map +1 -0
- package/dist/clients/types.js +2 -0
- package/dist/clients/types.js.map +1 -0
- package/dist/clients/vscode.js +187 -0
- package/dist/clients/vscode.js.map +1 -0
- package/dist/clients/windsurf.js +151 -0
- package/dist/clients/windsurf.js.map +1 -0
- package/dist/config.js +32 -0
- package/dist/config.js.map +1 -0
- package/dist/custom-regions.js +112 -0
- package/dist/custom-regions.js.map +1 -0
- package/dist/diagnostics.js +122 -0
- package/dist/diagnostics.js.map +1 -0
- package/dist/doctor.js +125 -0
- package/dist/doctor.js.map +1 -0
- package/dist/guards-evaluator/bucketing.js +83 -0
- package/dist/guards-evaluator/bucketing.js.map +1 -0
- package/dist/guards-evaluator/evaluate.js +272 -0
- package/dist/guards-evaluator/evaluate.js.map +1 -0
- package/dist/guards-evaluator/glob.js +148 -0
- package/dist/guards-evaluator/glob.js.map +1 -0
- package/dist/guards-evaluator/index.js +9 -0
- package/dist/guards-evaluator/index.js.map +1 -0
- package/dist/guards-evaluator/preprocess.js +174 -0
- package/dist/guards-evaluator/preprocess.js.map +1 -0
- package/dist/guards-evaluator/redact.js +111 -0
- package/dist/guards-evaluator/redact.js.map +1 -0
- package/dist/guards-evaluator/regex.js +125 -0
- package/dist/guards-evaluator/regex.js.map +1 -0
- package/dist/guards-evaluator/types.js +2 -0
- package/dist/guards-evaluator/types.js.map +1 -0
- package/dist/guards-evaluator/validation.js +115 -0
- package/dist/guards-evaluator/validation.js.map +1 -0
- package/dist/hook-runner.js +6680 -0
- package/dist/hooks/install.js +169 -0
- package/dist/hooks/install.js.map +1 -0
- package/dist/hooks/runtime/api.js +167 -0
- package/dist/hooks/runtime/api.js.map +1 -0
- package/dist/hooks/runtime/config.js +60 -0
- package/dist/hooks/runtime/config.js.map +1 -0
- package/dist/hooks/runtime/emit.js +45 -0
- package/dist/hooks/runtime/emit.js.map +1 -0
- package/dist/hooks/runtime/fetch-rules.js +154 -0
- package/dist/hooks/runtime/fetch-rules.js.map +1 -0
- package/dist/hooks/runtime/guard-rules-cache.js +217 -0
- package/dist/hooks/runtime/guard-rules-cache.js.map +1 -0
- package/dist/hooks/runtime/guard-violations-buffer.js +105 -0
- package/dist/hooks/runtime/guard-violations-buffer.js.map +1 -0
- package/dist/hooks/runtime/pre-compact.js +41 -0
- package/dist/hooks/runtime/pre-compact.js.map +1 -0
- package/dist/hooks/runtime/resolve.js +206 -0
- package/dist/hooks/runtime/resolve.js.map +1 -0
- package/dist/hooks/runtime/session-review.js +198 -0
- package/dist/hooks/runtime/session-review.js.map +1 -0
- package/dist/hooks/runtime/session-start.js +101 -0
- package/dist/hooks/runtime/session-start.js.map +1 -0
- package/dist/hooks/runtime/sniff.js +112 -0
- package/dist/hooks/runtime/sniff.js.map +1 -0
- package/dist/hooks/runtime/types.js +22 -0
- package/dist/hooks/runtime/types.js.map +1 -0
- package/dist/hooks/runtime/user-prompt-submit.js +154 -0
- package/dist/hooks/runtime/user-prompt-submit.js.map +1 -0
- package/dist/index.js +129 -0
- package/dist/index.js.map +1 -0
- package/dist/install.js +183 -0
- package/dist/install.js.map +1 -0
- package/dist/login.js +335 -0
- package/dist/login.js.map +1 -0
- package/dist/prompts.js +134 -0
- package/dist/prompts.js.map +1 -0
- package/dist/self-update.js +177 -0
- package/dist/self-update.js.map +1 -0
- package/dist/status.js +58 -0
- package/dist/status.js.map +1 -0
- package/dist/utils.js +84 -0
- package/dist/utils.js.map +1 -0
- package/dist/version.js +23 -0
- package/dist/version.js.map +1 -0
- package/package.json +73 -0
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Local HTTP callback server for the OAuth 2.0 Authorization Code flow.
|
|
3
|
+
*
|
|
4
|
+
* Binds to 127.0.0.1 (localhost only, never 0.0.0.0) on a random unprivileged
|
|
5
|
+
* port (port 0 → OS assigns a free port). After a successful callback the
|
|
6
|
+
* server is closed immediately.
|
|
7
|
+
*
|
|
8
|
+
* Security:
|
|
9
|
+
* - Binds to 127.0.0.1 only (not reachable from the network).
|
|
10
|
+
* - Validates the `state` parameter on every callback to prevent CSRF.
|
|
11
|
+
* - Closes as soon as the callback is received (no lingering listener).
|
|
12
|
+
*/
|
|
13
|
+
import { createServer } from 'node:http';
|
|
14
|
+
/**
|
|
15
|
+
* Start a one-shot local HTTP server on a random port. Returns the port,
|
|
16
|
+
* a promise that resolves with the OAuth code, and a stop function.
|
|
17
|
+
*
|
|
18
|
+
* @param expectedState The CSRF state value the caller generated. Every
|
|
19
|
+
* incoming callback is validated against this; mismatches are rejected.
|
|
20
|
+
*/
|
|
21
|
+
/**
|
|
22
|
+
* Default port for production. Matches the loopback redirect URIs registered
|
|
23
|
+
* on the Logto "CybeDefend CLI" Native application
|
|
24
|
+
* (http://localhost:9877/callback and http://127.0.0.1:9877/callback).
|
|
25
|
+
* Tests override this with port 0 to avoid sequential-test collisions.
|
|
26
|
+
*/
|
|
27
|
+
export const DEFAULT_CALLBACK_PORT = 9877;
|
|
28
|
+
export async function startCallbackServer(expectedState, options = {}) {
|
|
29
|
+
let resolveCode;
|
|
30
|
+
let rejectCode;
|
|
31
|
+
const codePromise = new Promise((res, rej) => {
|
|
32
|
+
resolveCode = res;
|
|
33
|
+
rejectCode = rej;
|
|
34
|
+
});
|
|
35
|
+
const server = createServer((req, res) => {
|
|
36
|
+
const url = new URL(req.url ?? '/', 'http://localhost');
|
|
37
|
+
if (url.pathname !== '/callback') {
|
|
38
|
+
res.writeHead(404).end();
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
const code = url.searchParams.get('code');
|
|
42
|
+
const state = url.searchParams.get('state');
|
|
43
|
+
const error = url.searchParams.get('error');
|
|
44
|
+
const errorDescription = url.searchParams.get('error_description') ?? '';
|
|
45
|
+
if (error) {
|
|
46
|
+
res.writeHead(400, { 'Content-Type': 'text/html' });
|
|
47
|
+
res.end(prettyErrorPage(error, errorDescription));
|
|
48
|
+
rejectCode(new Error(`OAuth error: ${error}${errorDescription ? ` — ${errorDescription}` : ''}`));
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
if (!code || state !== expectedState) {
|
|
52
|
+
const reason = !code ? 'missing authorization code' : 'state mismatch';
|
|
53
|
+
res.writeHead(400, { 'Content-Type': 'text/html' });
|
|
54
|
+
res.end(prettyErrorPage('invalid_request', reason));
|
|
55
|
+
rejectCode(new Error(`Invalid OAuth callback: ${reason}.`));
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
res.writeHead(200, { 'Content-Type': 'text/html' });
|
|
59
|
+
res.end(prettySuccessPage());
|
|
60
|
+
resolveCode(code);
|
|
61
|
+
});
|
|
62
|
+
// Bind to localhost on the chosen port. Production uses DEFAULT_CALLBACK_PORT
|
|
63
|
+
// (9877) because Logto enforces strict redirect_uri matching against the URIs
|
|
64
|
+
// registered for the "CybeDefend CLI" Native application
|
|
65
|
+
// (http://localhost:9877/callback and http://127.0.0.1:9877/callback). RFC
|
|
66
|
+
// 8252 recommends random loopback ports, but Logto does not honor wildcards.
|
|
67
|
+
// Tests pass port=0 to let the OS assign a free port — required to avoid
|
|
68
|
+
// EADDRINUSE collisions across sequentially-run test cases.
|
|
69
|
+
const requestedPort = options.port ?? DEFAULT_CALLBACK_PORT;
|
|
70
|
+
await new Promise((resolve, reject) => {
|
|
71
|
+
server.once('error', (err) => {
|
|
72
|
+
if (err.code === 'EADDRINUSE') {
|
|
73
|
+
reject(new Error(`Port ${requestedPort} is already in use. Another vibedefend login flow may be running, or another process is bound to this port. ` +
|
|
74
|
+
`Kill the other process (lsof -i :${requestedPort}) and try again.`));
|
|
75
|
+
}
|
|
76
|
+
else {
|
|
77
|
+
reject(err);
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
server.listen(requestedPort, '127.0.0.1', resolve);
|
|
81
|
+
});
|
|
82
|
+
const addr = server.address();
|
|
83
|
+
if (!addr)
|
|
84
|
+
throw new Error('Failed to bind callback server.');
|
|
85
|
+
// When the caller passed port=0 the OS assigned a free port; reflect the
|
|
86
|
+
// ACTUAL bound port back to the caller so they can build the redirect_uri.
|
|
87
|
+
const port = addr.port;
|
|
88
|
+
const stopServer = () => new Promise((resolve) => {
|
|
89
|
+
// server.close() alone only refuses NEW connections — existing keep-alive
|
|
90
|
+
// connections (the browser's callback request) remain in the event loop
|
|
91
|
+
// until they idle out, which hangs `vibedefend login` for ~5 s after
|
|
92
|
+
// success. closeAllConnections() forcefully closes those so the process
|
|
93
|
+
// can exit immediately. Available since Node 18.2; we require >=18.17.
|
|
94
|
+
server.closeAllConnections?.();
|
|
95
|
+
server.close(() => resolve());
|
|
96
|
+
});
|
|
97
|
+
return { port, codePromise, stopServer };
|
|
98
|
+
}
|
|
99
|
+
// ─── HTML pages ──────────────────────────────────────────────────────────────
|
|
100
|
+
/**
|
|
101
|
+
* Shared HTML shell for the OAuth callback success / error pages.
|
|
102
|
+
*
|
|
103
|
+
* Mirrors the branded layout used by the Go `cybedefend-cli` (pkg/auth/oauth.go:
|
|
104
|
+
* `callbackPageBase`) so users see the same authentication-complete experience
|
|
105
|
+
* regardless of which CLI initiated the flow. The single shared template keeps
|
|
106
|
+
* the look-and-feel — radial-gradient background, white card, CybeDefend logo,
|
|
107
|
+
* "CybeDefend CLI" tag — consistent across both CLIs.
|
|
108
|
+
*
|
|
109
|
+
* Caller injects `body` as the inner content (icon + title + optional detail).
|
|
110
|
+
* The caller is responsible for HTML-escaping any user-controlled string before
|
|
111
|
+
* embedding it into `body`.
|
|
112
|
+
*/
|
|
113
|
+
function renderCallbackPage(body) {
|
|
114
|
+
return `<!DOCTYPE html>
|
|
115
|
+
<html lang="en">
|
|
116
|
+
<head>
|
|
117
|
+
<meta charset="UTF-8" />
|
|
118
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
119
|
+
<title>CybeDefend — Authentication</title>
|
|
120
|
+
<style>
|
|
121
|
+
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
|
122
|
+
body {
|
|
123
|
+
min-height: 100vh;
|
|
124
|
+
display: flex;
|
|
125
|
+
align-items: center;
|
|
126
|
+
justify-content: center;
|
|
127
|
+
background: radial-gradient(ellipse at 60% 10%, #2d1045 0%, #14082a 50%, #0a0414 100%);
|
|
128
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
129
|
+
color: #e8d8ff;
|
|
130
|
+
}
|
|
131
|
+
.card {
|
|
132
|
+
background: #ffffff;
|
|
133
|
+
border: none;
|
|
134
|
+
border-radius: 20px;
|
|
135
|
+
padding: 48px 52px;
|
|
136
|
+
max-width: 480px;
|
|
137
|
+
width: 100%;
|
|
138
|
+
text-align: center;
|
|
139
|
+
box-shadow: 0 8px 48px rgba(0,0,0,0.5);
|
|
140
|
+
}
|
|
141
|
+
h1 { color: #1a0a2e; font-size: 22px; font-weight: 600; margin-bottom: 10px; }
|
|
142
|
+
p { color: rgba(40,20,60,0.6); font-size: 14px; line-height: 1.6; }
|
|
143
|
+
.logo { height: 44px; margin-bottom: 36px; }
|
|
144
|
+
.icon {
|
|
145
|
+
width: 72px; height: 72px;
|
|
146
|
+
border-radius: 50%;
|
|
147
|
+
display: flex; align-items: center; justify-content: center;
|
|
148
|
+
margin: 0 auto 24px;
|
|
149
|
+
font-size: 32px;
|
|
150
|
+
}
|
|
151
|
+
.icon.success { background: #e6f9ee; border: 1px solid #4caf7d; }
|
|
152
|
+
.icon.error { background: rgba(255,80,80,0.08); border: 1px solid rgba(255,80,80,0.25); }
|
|
153
|
+
.detail {
|
|
154
|
+
margin-top: 16px;
|
|
155
|
+
padding: 12px 16px;
|
|
156
|
+
background: rgba(255,80,80,0.06);
|
|
157
|
+
border: 1px solid rgba(255,80,80,0.18);
|
|
158
|
+
border-radius: 10px;
|
|
159
|
+
font-size: 13px;
|
|
160
|
+
color: rgba(200,40,40,0.85);
|
|
161
|
+
word-break: break-word;
|
|
162
|
+
}
|
|
163
|
+
.tag {
|
|
164
|
+
display: inline-block;
|
|
165
|
+
margin-top: 28px;
|
|
166
|
+
padding: 5px 14px;
|
|
167
|
+
border-radius: 99px;
|
|
168
|
+
font-size: 11px;
|
|
169
|
+
letter-spacing: 0.05em;
|
|
170
|
+
text-transform: uppercase;
|
|
171
|
+
border: 1px solid rgba(100,50,180,0.25);
|
|
172
|
+
color: rgba(100,50,180,0.5);
|
|
173
|
+
}
|
|
174
|
+
</style>
|
|
175
|
+
</head>
|
|
176
|
+
<body>
|
|
177
|
+
<div class="card">
|
|
178
|
+
<img class="logo" src="https://eu.cybedefend.com/logo.webp" alt="CybeDefend" />
|
|
179
|
+
${body}
|
|
180
|
+
<span class="tag">VibeDefend</span>
|
|
181
|
+
</div>
|
|
182
|
+
</body>
|
|
183
|
+
</html>`;
|
|
184
|
+
}
|
|
185
|
+
function prettySuccessPage() {
|
|
186
|
+
const body = `
|
|
187
|
+
<div class="icon success">
|
|
188
|
+
<svg width="34" height="34" viewBox="0 0 24 24" fill="none" stroke="#1a7a40" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"/></svg>
|
|
189
|
+
</div>
|
|
190
|
+
<h1>Authentication successful</h1>
|
|
191
|
+
<p>You're now logged in. You can close this tab and return to your terminal.</p>
|
|
192
|
+
<script>
|
|
193
|
+
// Best-effort auto-close — works in most browsers unless blocked by policy.
|
|
194
|
+
setTimeout(() => { try { window.close(); } catch (e) {} }, 1500);
|
|
195
|
+
</script>`;
|
|
196
|
+
return renderCallbackPage(body);
|
|
197
|
+
}
|
|
198
|
+
function prettyErrorPage(error, description) {
|
|
199
|
+
const safeError = htmlEscape(error || 'Authentication error');
|
|
200
|
+
const safeDesc = htmlEscape(description || '');
|
|
201
|
+
const body = `
|
|
202
|
+
<div class="icon error">✕</div>
|
|
203
|
+
<h1>${safeError}</h1>
|
|
204
|
+
<p>Authentication could not be completed.</p>
|
|
205
|
+
${safeDesc ? `<div class="detail">${safeDesc}</div>` : ''}`;
|
|
206
|
+
return renderCallbackPage(body);
|
|
207
|
+
}
|
|
208
|
+
function htmlEscape(s) {
|
|
209
|
+
return s
|
|
210
|
+
.replace(/&/g, '&')
|
|
211
|
+
.replace(/</g, '<')
|
|
212
|
+
.replace(/>/g, '>')
|
|
213
|
+
.replace(/"/g, '"')
|
|
214
|
+
.replace(/'/g, ''');
|
|
215
|
+
}
|
|
216
|
+
//# sourceMappingURL=callback-server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"callback-server.js","sourceRoot":"","sources":["../../src/auth/callback-server.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PKCE (Proof Key for Code Exchange) helpers for the OAuth 2.0 Authorization
|
|
3
|
+
* Code flow. Used by `vibedefend login` to authenticate against Logto without
|
|
4
|
+
* a client_secret (Public/Native client model).
|
|
5
|
+
*
|
|
6
|
+
* RFC 7636 — PKCE spec.
|
|
7
|
+
*/
|
|
8
|
+
import { createHash, randomBytes } from 'node:crypto';
|
|
9
|
+
/**
|
|
10
|
+
* Generate a cryptographically random, URL-safe base64 string of the given
|
|
11
|
+
* byte length. Used both for the PKCE code_verifier and the state CSRF token.
|
|
12
|
+
*
|
|
13
|
+
* PKCE spec requires 43-128 characters for code_verifier. A 64-byte random
|
|
14
|
+
* value produces an 86-character base64url string (well within the range).
|
|
15
|
+
*/
|
|
16
|
+
export function randomBase64url(byteLength) {
|
|
17
|
+
return randomBytes(byteLength).toString('base64url');
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Compute the PKCE code_challenge from a code_verifier using the S256 method:
|
|
21
|
+
* BASE64URL(SHA256(ASCII(code_verifier)))
|
|
22
|
+
*
|
|
23
|
+
* The resulting challenge is sent in the authorization request; the verifier
|
|
24
|
+
* is sent in the token exchange. The server re-derives the challenge and
|
|
25
|
+
* verifies they match, proving the entity that started the flow is the same
|
|
26
|
+
* one completing it.
|
|
27
|
+
*/
|
|
28
|
+
export function pkceChallenge(verifier) {
|
|
29
|
+
return createHash('sha256').update(verifier).digest('base64url');
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=pkce.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pkce.js","sourceRoot":"","sources":["../../src/auth/pkce.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAEtD;;;;;;GAMG;AACH,MAAM,UAAU,eAAe,CAAC,UAAkB;IAChD,OAAO,WAAW,CAAC,UAAU,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;AACvD,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,aAAa,CAAC,QAAgB;IAC5C,OAAO,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC;AACnE,CAAC"}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OAuth 2.0 token exchange and refresh helpers.
|
|
3
|
+
*
|
|
4
|
+
* Handles two grant types against Logto's /oidc/token endpoint:
|
|
5
|
+
* - authorization_code (initial login, includes PKCE code_verifier)
|
|
6
|
+
* - refresh_token (transparent refresh before API calls)
|
|
7
|
+
*
|
|
8
|
+
* Both functions accept an optional `fetchImpl` so they can be injected
|
|
9
|
+
* in unit tests without touching globalThis.fetch.
|
|
10
|
+
*/
|
|
11
|
+
// ─── Errors ───────────────────────────────────────────────────────────────────
|
|
12
|
+
/**
|
|
13
|
+
* Thrown when the /oidc/token endpoint rejects the refresh_token (e.g. the
|
|
14
|
+
* token was revoked, expired beyond the inactivity window, or the user
|
|
15
|
+
* changed their password). The caller should clear local credentials and
|
|
16
|
+
* ask the user to `vibedefend login` again.
|
|
17
|
+
*/
|
|
18
|
+
export class RefreshTokenInvalidError extends Error {
|
|
19
|
+
constructor(msg) {
|
|
20
|
+
super(msg);
|
|
21
|
+
this.name = 'RefreshTokenInvalidError';
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Exchange an authorization code + PKCE code_verifier for a full token bundle.
|
|
26
|
+
*
|
|
27
|
+
* Called once at the end of the `vibedefend login` browser flow. The resulting
|
|
28
|
+
* tokens (access, refresh, id) are persisted to the OS keychain / file store.
|
|
29
|
+
*/
|
|
30
|
+
export async function exchangeCodeForTokens(params) {
|
|
31
|
+
const f = params.fetchImpl ?? globalThis.fetch;
|
|
32
|
+
const body = new URLSearchParams({
|
|
33
|
+
grant_type: 'authorization_code',
|
|
34
|
+
code: params.code,
|
|
35
|
+
code_verifier: params.codeVerifier,
|
|
36
|
+
client_id: params.clientId,
|
|
37
|
+
redirect_uri: params.redirectUri,
|
|
38
|
+
resource: params.resource,
|
|
39
|
+
});
|
|
40
|
+
const res = await f(`${params.logtoEndpoint}/oidc/token`, {
|
|
41
|
+
method: 'POST',
|
|
42
|
+
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
|
43
|
+
body,
|
|
44
|
+
signal: AbortSignal.timeout(10_000),
|
|
45
|
+
});
|
|
46
|
+
if (!res.ok) {
|
|
47
|
+
const text = await res.text().catch(() => '');
|
|
48
|
+
throw new Error(`Token exchange failed: HTTP ${res.status}${text ? ` — ${text}` : ''}`);
|
|
49
|
+
}
|
|
50
|
+
return res.json();
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Exchange a refresh_token for a new access_token (and a rotated
|
|
54
|
+
* refresh_token, if Logto is configured for rolling refresh).
|
|
55
|
+
*
|
|
56
|
+
* Throws `RefreshTokenInvalidError` on 400/401 so the caller can purge
|
|
57
|
+
* local credentials and prompt for re-login.
|
|
58
|
+
*/
|
|
59
|
+
export async function refreshAccessToken(params) {
|
|
60
|
+
const f = params.fetchImpl ?? globalThis.fetch;
|
|
61
|
+
const body = new URLSearchParams({
|
|
62
|
+
grant_type: 'refresh_token',
|
|
63
|
+
refresh_token: params.refreshToken,
|
|
64
|
+
client_id: params.clientId,
|
|
65
|
+
resource: params.resource,
|
|
66
|
+
});
|
|
67
|
+
const res = await f(`${params.logtoEndpoint}/oidc/token`, {
|
|
68
|
+
method: 'POST',
|
|
69
|
+
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
|
70
|
+
body,
|
|
71
|
+
signal: AbortSignal.timeout(10_000),
|
|
72
|
+
});
|
|
73
|
+
if (!res.ok) {
|
|
74
|
+
const text = await res.text().catch(() => '');
|
|
75
|
+
// 400 (invalid_grant) and 401 both indicate a dead refresh token.
|
|
76
|
+
if (res.status === 400 || res.status === 401) {
|
|
77
|
+
throw new RefreshTokenInvalidError(`Refresh token rejected: HTTP ${res.status}${text ? ` — ${text}` : ''}`);
|
|
78
|
+
}
|
|
79
|
+
throw new Error(`Token refresh failed: HTTP ${res.status}${text ? ` — ${text}` : ''}`);
|
|
80
|
+
}
|
|
81
|
+
return res.json();
|
|
82
|
+
}
|
|
83
|
+
//# sourceMappingURL=token-exchange.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"token-exchange.js","sourceRoot":"","sources":["../../src/auth/token-exchange.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Claude Code adapter.
|
|
3
|
+
*
|
|
4
|
+
* Wires the Node-based hook runner into `~/.claude/settings.json` and
|
|
5
|
+
* registers the MCP server via `claude mcp add`.
|
|
6
|
+
*
|
|
7
|
+
* Hook entries point at `node <path>/hook-runner.js <subcommand>` — the
|
|
8
|
+
* universal bundled runner installed at `~/.cybedefend/hook-runner.js`.
|
|
9
|
+
* No bash, no jq — runs the same way on Linux / macOS / Windows.
|
|
10
|
+
*/
|
|
11
|
+
import { execFileSync } from 'node:child_process';
|
|
12
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
13
|
+
import { home, log, readJson, run, which, writeJson } from '../utils.js';
|
|
14
|
+
import { hookCommand, VIBEDEFEND_PATH_MARKER, isVibedefendOwnedHookCommand, } from '../hooks/install.js';
|
|
15
|
+
import { probeBinaryVersion } from './detect.js';
|
|
16
|
+
const CLAUDE_SETTINGS = home('.claude', 'settings.json');
|
|
17
|
+
function detect() {
|
|
18
|
+
if (which('claude') === null) {
|
|
19
|
+
return {
|
|
20
|
+
installed: false,
|
|
21
|
+
supportsHooks: false,
|
|
22
|
+
reason: '`claude` CLI not on PATH. Install Claude Code from https://claude.com/claude-code',
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
const version = probeBinaryVersion('claude') ?? undefined;
|
|
26
|
+
return { installed: true, version, supportsHooks: true };
|
|
27
|
+
}
|
|
28
|
+
function supports(event) {
|
|
29
|
+
return [
|
|
30
|
+
'fetch-rules',
|
|
31
|
+
'session-start',
|
|
32
|
+
'stop',
|
|
33
|
+
'pre-compact',
|
|
34
|
+
// Claude Code is the ONLY adapter that opts into UserPromptSubmit
|
|
35
|
+
// today — see user-prompt-submit.ts header for the rationale (the
|
|
36
|
+
// brainstorming skill hijack we need to backstop). Codex follows the
|
|
37
|
+
// doctrine directly via MCP Server.instructions; the other clients
|
|
38
|
+
// either don't have an equivalent event or haven't shown the same
|
|
39
|
+
// hijack behaviour.
|
|
40
|
+
'user-prompt-submit',
|
|
41
|
+
].includes(event);
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Drop any prior entry that belongs to vibedefend (V0 manual template
|
|
45
|
+
* `tools/cybedefend-claude-hooks/hook-*.sh`, V1 bash hooks under
|
|
46
|
+
* `~/.cybedefend/hooks/*.sh`, and current V2 entries
|
|
47
|
+
* `node ~/.cybedefend/hook-runner.js ...`). Preserves unrelated user
|
|
48
|
+
* hooks. See `isVibedefendOwnedHookCommand` for the exact match set.
|
|
49
|
+
*/
|
|
50
|
+
function dropOurs(entries) {
|
|
51
|
+
return entries.filter((e) => !(e.hooks ?? []).some((h) => isVibedefendOwnedHookCommand(h.command)));
|
|
52
|
+
}
|
|
53
|
+
function writeSettings(opts) {
|
|
54
|
+
const settings = readJson(CLAUDE_SETTINGS, {});
|
|
55
|
+
settings.hooks ??= {};
|
|
56
|
+
settings.hooks.PreToolUse ??= [];
|
|
57
|
+
settings.hooks.Stop ??= [];
|
|
58
|
+
settings.hooks.SessionStart ??= [];
|
|
59
|
+
settings.hooks.PreCompact ??= [];
|
|
60
|
+
settings.hooks.UserPromptSubmit ??= [];
|
|
61
|
+
settings.hooks.PreToolUse = dropOurs(settings.hooks.PreToolUse);
|
|
62
|
+
settings.hooks.SessionStart = dropOurs(settings.hooks.SessionStart);
|
|
63
|
+
settings.hooks.Stop = dropOurs(settings.hooks.Stop);
|
|
64
|
+
settings.hooks.PreCompact = dropOurs(settings.hooks.PreCompact);
|
|
65
|
+
settings.hooks.UserPromptSubmit = dropOurs(settings.hooks.UserPromptSubmit);
|
|
66
|
+
settings.hooks.PreToolUse.push({
|
|
67
|
+
matcher: 'Edit|Write|MultiEdit',
|
|
68
|
+
hooks: [{ type: 'command', command: hookCommand('fetch-rules') }],
|
|
69
|
+
});
|
|
70
|
+
// Action Guards: hard-block hook. Runs on every tool call (no matcher so
|
|
71
|
+
// it fires for Read/Write/Edit/Bash/WebFetch etc.). Exit code 2 → Claude
|
|
72
|
+
// Code blocks the tool call and surfaces the message to the model.
|
|
73
|
+
settings.hooks.PreToolUse.push({
|
|
74
|
+
hooks: [{ type: 'command', command: hookCommand('guard-check') }],
|
|
75
|
+
});
|
|
76
|
+
settings.hooks.SessionStart.push({
|
|
77
|
+
hooks: [{ type: 'command', command: hookCommand('session-start') }],
|
|
78
|
+
});
|
|
79
|
+
// UserPromptSubmit fires before the agent processes each user prompt.
|
|
80
|
+
// We use it to backstop the CybeDefend doctrine when a skill (notably
|
|
81
|
+
// superpowers:brainstorming) auto-activates and would otherwise
|
|
82
|
+
// hijack the flow before step 0 / step 1 fire. See
|
|
83
|
+
// src/hooks/runtime/user-prompt-submit.ts for the full rationale.
|
|
84
|
+
settings.hooks.UserPromptSubmit.push({
|
|
85
|
+
hooks: [{ type: 'command', command: hookCommand('user-prompt-submit') }],
|
|
86
|
+
});
|
|
87
|
+
if (opts.enableSessionReview) {
|
|
88
|
+
settings.hooks.Stop.push({
|
|
89
|
+
matcher: 'Stop',
|
|
90
|
+
hooks: [{ type: 'command', command: hookCommand('session-review') }],
|
|
91
|
+
});
|
|
92
|
+
settings.hooks.PreCompact.push({
|
|
93
|
+
hooks: [{ type: 'command', command: hookCommand('pre-compact') }],
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
writeJson(CLAUDE_SETTINGS, settings);
|
|
97
|
+
log.ok(`Claude Code hooks wired into ${CLAUDE_SETTINGS}`);
|
|
98
|
+
}
|
|
99
|
+
function registerMcp(opts) {
|
|
100
|
+
const { region } = opts;
|
|
101
|
+
log.step(`Registering MCP "${region.mcpName}" in Claude Code`);
|
|
102
|
+
log.hint(`URL: ${region.mcpUrl}`);
|
|
103
|
+
const code = run('claude', [
|
|
104
|
+
'mcp',
|
|
105
|
+
'add',
|
|
106
|
+
region.mcpName,
|
|
107
|
+
'--transport',
|
|
108
|
+
'http',
|
|
109
|
+
region.mcpUrl,
|
|
110
|
+
]);
|
|
111
|
+
if (code === 0) {
|
|
112
|
+
log.ok(`MCP "${region.mcpName}" registered.`);
|
|
113
|
+
return true;
|
|
114
|
+
}
|
|
115
|
+
log.warn(`\`claude mcp add\` returned exit ${code}. It may already be registered — run \`claude mcp list\` to verify.`);
|
|
116
|
+
return false;
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Pure introspection — never throws. Reports whether our hooks are wired
|
|
120
|
+
* into `~/.claude/settings.json` and whether the MCP server is registered.
|
|
121
|
+
*
|
|
122
|
+
* `mcpRegistered` shells out to `claude mcp list` (the MCP registry lives
|
|
123
|
+
* in Claude Code's internal store, not a file we can read directly). If
|
|
124
|
+
* the `claude` binary is absent or the command fails, we degrade to
|
|
125
|
+
* `false` rather than throwing.
|
|
126
|
+
*/
|
|
127
|
+
function inspect(region) {
|
|
128
|
+
let hooksWired = false;
|
|
129
|
+
try {
|
|
130
|
+
if (existsSync(CLAUDE_SETTINGS)) {
|
|
131
|
+
hooksWired = readFileSync(CLAUDE_SETTINGS, 'utf8').includes(VIBEDEFEND_PATH_MARKER);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
catch {
|
|
135
|
+
/* missing/unreadable → false */
|
|
136
|
+
}
|
|
137
|
+
let mcpRegistered = false;
|
|
138
|
+
try {
|
|
139
|
+
const stdout = execFileSync('claude', ['mcp', 'list'], {
|
|
140
|
+
encoding: 'utf8',
|
|
141
|
+
stdio: ['ignore', 'pipe', 'ignore'],
|
|
142
|
+
});
|
|
143
|
+
mcpRegistered = stdout.includes(region.mcpName);
|
|
144
|
+
}
|
|
145
|
+
catch {
|
|
146
|
+
/* `claude` binary absent or command failed → false */
|
|
147
|
+
}
|
|
148
|
+
return { hooksWired, mcpRegistered };
|
|
149
|
+
}
|
|
150
|
+
export const claudeCodeAdapter = {
|
|
151
|
+
id: 'claude-code',
|
|
152
|
+
label: 'Claude Code',
|
|
153
|
+
detect,
|
|
154
|
+
supports,
|
|
155
|
+
writeSettings,
|
|
156
|
+
registerMcp,
|
|
157
|
+
inspect,
|
|
158
|
+
};
|
|
159
|
+
/**
|
|
160
|
+
* Cross-platform sanity gate (used by the installer banner).
|
|
161
|
+
*
|
|
162
|
+
* With the Node-based hook runtime (V2), Windows is now SUPPORTED — no
|
|
163
|
+
* Git Bash / WSL needed. Linux and macOS were already fine. We keep
|
|
164
|
+
* this function as the central place to gate hooks support in case a
|
|
165
|
+
* future platform (e.g. some embedded shell) needs special handling.
|
|
166
|
+
*/
|
|
167
|
+
export function checkPlatformSupport() {
|
|
168
|
+
return { hooksSupported: true };
|
|
169
|
+
}
|
|
170
|
+
//# sourceMappingURL=claude-code.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"claude-code.js","sourceRoot":"","sources":["../../src/clients/claude-code.ts"],"names":[],"mappings":"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"}
|