mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-05 02:44:02 +00:00
fix(server): gate cost/log endpoints against cross-site requests and ship a full CSP
The cost-bearing endpoints (/api/realtime/token, /api/openai/hud-summary, /api/google/nearby-places) and the voice debug log refuse cross-site browser requests: a foreign or opaque Origin, a Sec-Fetch-Site other than same-origin/none, or proxy forwarding headers. Loopback tools without an Origin and the LAN opt-in keep working. The dev and preview servers send one shared Content-Security-Policy, and embed-mode framing rewrites only the frame-ancestors directive of it. Salvaged from #242 onto current main. Co-authored-by: Sameh Khamis <[email protected]> Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
This commit is contained in:
1 parent
42faed3aea
commit
7aa9bbd3be
11 files changed
+402
-15
No files matched your search
@@ -0,0 +1,43 @@
|
||||
import { admitSameSiteRequest } from '../../../src/localRequestGate.mjs';
|
||||
|
||||
/**
|
||||
* Cross-site request gate for cost-bearing and log endpoints. Feeds the
|
||||
* request to the pure, unit-tested `admitSameSiteRequest` and answers 403
|
||||
* itself when the request is refused. Returns true when it has already
|
||||
* responded (the caller returns); false when the handler should continue.
|
||||
* Refuses cross-site browser requests (foreign or opaque Origin, a
|
||||
* Sec-Fetch-Site other than same-origin/none, or reverse-proxy headers) while
|
||||
* keeping loopback non-browser tools and the HOST=0.0.0.0 LAN opt-in working.
|
||||
* @param {import('node:http').IncomingMessage} req
|
||||
* @param {import('node:http').ServerResponse} res
|
||||
* @returns {boolean} True when a 403 was sent.
|
||||
*/
|
||||
export function admitSameSite(req, res) {
|
||||
const verdict = admitSameSiteRequest({
|
||||
hostHeader: req.headers?.host,
|
||||
protocol: req.socket?.encrypted ? 'https:' : 'http:',
|
||||
origin: req.headers?.origin,
|
||||
secFetchSite: req.headers?.['sec-fetch-site'],
|
||||
proxyHeaders: req.headers || {},
|
||||
});
|
||||
if (verdict.ok) return false;
|
||||
res.statusCode = verdict.status;
|
||||
res.setHeader('Content-Type', 'application/json');
|
||||
res.setHeader('Cache-Control', 'no-store');
|
||||
res.end(JSON.stringify({ error: verdict.error }));
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrap a middleware handler so cross-site requests are refused with 403
|
||||
* before the handler runs. Loopback tools and same-origin browser requests
|
||||
* pass straight through.
|
||||
* @param {(req: import('node:http').IncomingMessage, res: import('node:http').ServerResponse, next?: Function) => unknown} handler
|
||||
* @returns {(req: import('node:http').IncomingMessage, res: import('node:http').ServerResponse, next?: Function) => unknown}
|
||||
*/
|
||||
export function sameSiteGated(handler) {
|
||||
return (req, res, next) => {
|
||||
if (admitSameSite(req, res)) return undefined;
|
||||
return handler(req, res, next);
|
||||
};
|
||||
}
|
||||
Reference in new issue
Block a user