mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-05 10:53:59 +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
@@ -1,7 +1,7 @@
|
||||
import test from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import { readFileSync } from 'node:fs';
|
||||
import { createBrowserViteConfig } from '../../build/vite.js';
|
||||
import { BROWSER_CSP, createBrowserViteConfig } from '../../build/vite.js';
|
||||
import standaloneConfig, * as compatibility from '../../vite.config.js';
|
||||
import * as providers from '../../server/providers/local.js';
|
||||
|
||||
@@ -23,10 +23,18 @@ test('explicit build inputs preserve browser-only defines, plugin order and loop
|
||||
assert.ok(config.server.fs.deny.includes('**/ENVIRONMENT'));
|
||||
assert.ok(config.server.fs.deny.includes('.env.*'));
|
||||
assert.equal(config.server.headers['X-Frame-Options'], 'DENY');
|
||||
assert.equal(
|
||||
config.server.headers['Content-Security-Policy'],
|
||||
const csp = config.server.headers['Content-Security-Policy'];
|
||||
assert.equal(csp, BROWSER_CSP);
|
||||
for (const directive of [
|
||||
"script-src 'self' 'unsafe-eval' blob:",
|
||||
"object-src 'none'",
|
||||
"base-uri 'self'",
|
||||
"frame-ancestors 'none'",
|
||||
);
|
||||
]) {
|
||||
assert.ok(csp.includes(directive), directive);
|
||||
}
|
||||
assert.ok(!csp.includes("script-src 'self' 'unsafe-inline'"));
|
||||
assert.deepEqual(config.preview.headers, config.server.headers);
|
||||
assert.deepEqual(config.define, {
|
||||
'import.meta.env.GOOGLE_MAPS_API_KEY': '"browser-fixture"',
|
||||
'import.meta.env.CESIUM_ION_TOKEN': '"ion-fixture"',
|
||||
@@ -127,6 +135,22 @@ test('only embed-mode documents may be framed, and only by the allowed ancestors
|
||||
anywhere.setHeader('X-Frame-Options', 'DENY');
|
||||
anywhere.setHeader('Content-Security-Policy', "frame-ancestors 'none'");
|
||||
assert.deepEqual(Object.fromEntries(anywhere.headers), {});
|
||||
// A full policy keeps its other directives; only the framing rule changes.
|
||||
const full = "default-src 'self'; script-src 'self'; frame-ancestors 'none'";
|
||||
const embeddedFull = response();
|
||||
middleware({ url: '/?embed=1' }, embeddedFull, () => {});
|
||||
embeddedFull.setHeader('Content-Security-Policy', full);
|
||||
assert.equal(
|
||||
embeddedFull.headers.get('content-security-policy'),
|
||||
"default-src 'self'; script-src 'self'; frame-ancestors https://a.example",
|
||||
);
|
||||
const anywhereFull = response();
|
||||
open({ url: '/?embed=1' }, anywhereFull, () => {});
|
||||
anywhereFull.setHeader('Content-Security-Policy', full);
|
||||
assert.equal(
|
||||
anywhereFull.headers.get('content-security-policy'),
|
||||
"default-src 'self'; script-src 'self'",
|
||||
);
|
||||
const normal = response();
|
||||
middleware({ url: '/' }, normal, () => {});
|
||||
normal.setHeader('X-Frame-Options', 'DENY');
|
||||
|
||||
Reference in new issue
Block a user