forked from unom/punktfunk
fix(web): one bad password from anywhere stops locking out the whole console
The console's login throttle was documented as per-IP and was not. Nitro's `localFetch` hands the app a synthetic request whose socket has no `remoteAddress`, so `getRequestIP()` returned undefined for every request and every attempt was charged to one shared "unknown" bucket. Five wrong guesses from any LAN peer locked out everyone — including the operator, and including the update-apply route, which shares that budget. The Bun entry is the only place the real peer is knowable, so it now stamps it into a header (deleting any client-supplied copy first) and `peerAddress()` reads it back. Verified on a real build bound to 0.0.0.0: seven wrong logins from 127.0.0.1 lock 127.0.0.1 out, a different peer still logs in on the first try, and a request forging the header is charged to its real address. Also on the way through: - Installing an unreviewed package and adding a catalog source now re-ask for the console password, like applying an update already did. A 7-day session cookie should not be able to run new code on the host, and `store/install` with `accept_unverified` did exactly that through the generic passthrough. The gate sits at the trust boundary — adding a source, or a raw spec — not on every install from a source the operator already chose to trust. - The ui-credential denylist is matched against the normalised path too, so `/api//v1/...` and friends can no longer walk around it. - The console serves nosniff, a no-referrer policy, and a CSP that pins frame-ancestors, object-src and base-uri. - A plugin UI's response no longer re-emits the content-encoding that `fetch` already decoded (which made compressed plugin pages fail to load), no longer sets cookies on the console's origin, and OPTIONS reaches the plugin instead of being refused 405 by us. - An unreachable host reads as 502 on these routes, matching the passthrough, instead of a bare 500. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
+12
-2
@@ -123,14 +123,24 @@ export interface JobAccepted {
|
||||
job: string;
|
||||
}
|
||||
|
||||
/** Install a curated catalog entry, or — deliberately awkward — a raw package spec. */
|
||||
/**
|
||||
* Install a curated catalog entry, or — deliberately awkward — a raw package spec.
|
||||
*
|
||||
* The raw-spec branch carries the console `password`: it runs unreviewed code, so the BFF
|
||||
* re-confirms it (server/routes/api/v1/store/install.post.ts) and strips it before the host ever
|
||||
* sees the request. A catalog install needs no password — that trust decision was made when the
|
||||
* source was added.
|
||||
*/
|
||||
export type InstallBody =
|
||||
| { source: string; id: string }
|
||||
| { spec: string; accept_unverified: true };
|
||||
| { spec: string; accept_unverified: true; password: string };
|
||||
|
||||
/** Adding or repointing a source is a trust-root change, so it carries the console password too
|
||||
* (stripped at the BFF — server/routes/api/v1/store/sources/[name].put.ts). */
|
||||
export interface SourceBody {
|
||||
url: string;
|
||||
public_key?: string;
|
||||
password: string;
|
||||
}
|
||||
|
||||
const BASE = "/api/v1/store";
|
||||
|
||||
Reference in New Issue
Block a user