lazyclaw 3.99.17 → 3.99.19
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/cli.mjs +7 -0
- package/daemon.mjs +15 -2
- package/package.json +1 -1
package/cli.mjs
CHANGED
|
@@ -2541,6 +2541,13 @@ async function cmdDashboard(flags = {}) {
|
|
|
2541
2541
|
// LAZYCLAW_AUTH_TOKEN + --allow-origin via the daemon command.
|
|
2542
2542
|
authToken: undefined,
|
|
2543
2543
|
allowedOrigins: [],
|
|
2544
|
+
// The dashboard's browser tab posts back to the same loopback URL
|
|
2545
|
+
// it was served from (e.g. `http://127.0.0.1:19600`). Without this
|
|
2546
|
+
// opt-in every chat send / mutation tripped the daemon's CSRF gate
|
|
2547
|
+
// with `403 forbidden origin`. Safe — the daemon binds 127.0.0.1
|
|
2548
|
+
// only, so an attacker can't reach it with a loopback origin
|
|
2549
|
+
// unless they're already on the machine.
|
|
2550
|
+
allowLoopbackOrigin: true,
|
|
2544
2551
|
rateLimit: null,
|
|
2545
2552
|
responseCache: null,
|
|
2546
2553
|
logger: null,
|
package/daemon.mjs
CHANGED
|
@@ -231,13 +231,22 @@ function isAuthorized(req, expectedToken) {
|
|
|
231
231
|
* - `Origin` set → must be in `allowedOrigins`. Empty allowlist
|
|
232
232
|
* means "reject all browser-originated requests" — the default,
|
|
233
233
|
* because the daemon is designed for CLI/script callers.
|
|
234
|
+
* - `allowLoopback: true` (set by `lazyclaw dashboard`) additionally
|
|
235
|
+
* accepts any `Origin` that looks like loopback (`http://127.0.0.1:*`,
|
|
236
|
+
* `http://localhost:*`, `http://[::1]:*`). Safe because the daemon
|
|
237
|
+
* binds only to 127.0.0.1, so an attacker can't reach us with a
|
|
238
|
+
* loopback Origin unless they're already on the box. DNS rebinding
|
|
239
|
+
* can't forge `127.0.0.1` as a hostname — that resolves before
|
|
240
|
+
* `fetch()` ever issues the request.
|
|
234
241
|
*
|
|
235
242
|
* Returns true when the request should proceed, false when it should
|
|
236
243
|
* be rejected with 403.
|
|
237
244
|
*/
|
|
238
|
-
|
|
245
|
+
const LOOPBACK_ORIGIN_RE = /^https?:\/\/(127\.0\.0\.1|localhost|\[::1\])(:\d+)?$/i;
|
|
246
|
+
function isOriginAllowed(req, allowedOrigins, allowLoopback) {
|
|
239
247
|
const origin = req.headers['origin'];
|
|
240
248
|
if (!origin) return true;
|
|
249
|
+
if (allowLoopback && LOOPBACK_ORIGIN_RE.test(origin)) return true;
|
|
241
250
|
if (!allowedOrigins || allowedOrigins.length === 0) return false;
|
|
242
251
|
return allowedOrigins.includes(origin);
|
|
243
252
|
}
|
|
@@ -267,6 +276,10 @@ function isOriginAllowed(req, allowedOrigins) {
|
|
|
267
276
|
export function makeHandler(ctx) {
|
|
268
277
|
const authToken = ctx.authToken || null;
|
|
269
278
|
const allowedOrigins = Array.isArray(ctx.allowedOrigins) ? ctx.allowedOrigins : [];
|
|
279
|
+
// dashboard subcommand opts in so the browser tab it just opened can
|
|
280
|
+
// actually call its own daemon. Bare `lazyclaw daemon` leaves this off
|
|
281
|
+
// and the explicit allowlist (or no-browser default) stays in force.
|
|
282
|
+
const allowLoopback = !!ctx.allowLoopbackOrigin;
|
|
270
283
|
// Default state dir matches the CLI's default. Callers can override
|
|
271
284
|
// via ctx.workflowStateDir or LAZYCLAW_WORKFLOW_STATE_DIR env var.
|
|
272
285
|
const workflowStateDir = ctx.workflowStateDir
|
|
@@ -345,7 +358,7 @@ export function makeHandler(ctx) {
|
|
|
345
358
|
try {
|
|
346
359
|
// Origin gate runs *before* auth so a browser-originated request
|
|
347
360
|
// can't even probe whether a token is required.
|
|
348
|
-
if (!isOriginAllowed(req, allowedOrigins)) {
|
|
361
|
+
if (!isOriginAllowed(req, allowedOrigins, allowLoopback)) {
|
|
349
362
|
return writeJson(res, 403, { error: 'forbidden origin' });
|
|
350
363
|
}
|
|
351
364
|
// Authentication gate — when authToken is set, every request must
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lazyclaw",
|
|
3
|
-
"version": "3.99.
|
|
3
|
+
"version": "3.99.19",
|
|
4
4
|
"description": "Lazy, elegant terminal CLI for chatting with Claude / OpenAI / Gemini / Ollama and orchestrating multi-step LLM workflows. Banner-on-launch, slash-command ghost autocomplete, persistent sessions, local HTTP gateway.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"claude",
|