mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-10-21 14:36:31 +00:00
8e5fdaa877
Permissions cached an empty map before it had loaded, so a DB error during the load cached permissive defaults and every later check answered from that cache - fail-open, and it never expired. The rate limiter grew its per-IP map forever; the GC the legacy version had was dropped in the fold. Idempotency did a check-then-insert, so two concurrent duplicates both ran the handler. Audit rows recorded whatever X-Forwarded-For said, which the client writes, and the request-body debug logger redacted by exact key match - it missed refresh tokens, api keys and PKCE verifiers. FORCE_HTTPS built its redirect from the client's Host header. CSP asked for unsafe-eval on top of the wasm-unsafe-eval it actually needs. The OAuth deny path redirected to an unvalidated URI, ws-token minting had no rate limit at all, and PKCE was skipped when code_verifier was absent rather than refused. An MCP session resumed with a token whose scopes had since been reduced kept the capability set it was created with, and the session cap was checked before the async registration that increments it. Plugin frame containment was lexical, so a symlink out of the directory was never noticed, and instance config accepted keys the manifest never declared.
76 lines
2.9 KiB
TypeScript
76 lines
2.9 KiB
TypeScript
import { describe, it, expect, afterEach } from 'vitest';
|
|
import express from 'express';
|
|
import request from 'supertest';
|
|
import { applyGlobalMiddleware } from '../../../src/middleware/globalMiddleware';
|
|
|
|
async function directiveSources(name: string): Promise<string[]> {
|
|
const app = express();
|
|
applyGlobalMiddleware(app);
|
|
app.get('/probe', (_req, res) => res.json({ ok: true }));
|
|
|
|
const res = await request(app).get('/probe');
|
|
const csp = String(res.headers['content-security-policy'] || '');
|
|
const directive = csp
|
|
.split(';')
|
|
.map(d => d.trim())
|
|
.find(d => d.startsWith(name));
|
|
|
|
return directive ? directive.split(/\s+/).slice(1) : [];
|
|
}
|
|
|
|
const connectSrcSources = () => directiveSources('connect-src');
|
|
|
|
describe('global CSP: OpenStreetMap tile hosts (#1733)', () => {
|
|
it('allows the bare tile.openstreetmap.org host', async () => {
|
|
// A CSP wildcard host never matches the apex, so `*.tile.openstreetmap.org`
|
|
// alone would block the tile prefetcher's fetch() against the single host
|
|
// OSM has served from since it retired a/b/c/d sharding.
|
|
expect(await connectSrcSources()).toContain('https://tile.openstreetmap.org');
|
|
});
|
|
|
|
it('still allows the sharded hosts for templates saved earlier', async () => {
|
|
expect(await connectSrcSources()).toContain('https://*.tile.openstreetmap.org');
|
|
});
|
|
});
|
|
|
|
describe('global CSP: script-src', () => {
|
|
it("allows 'wasm-unsafe-eval' so the WASM decoders keep running", async () => {
|
|
expect(await directiveSources('script-src')).toContain("'wasm-unsafe-eval'");
|
|
});
|
|
|
|
it("does not allow 'unsafe-eval' — the policy the wiki documents, and nothing first-party evals", async () => {
|
|
expect(await directiveSources('script-src')).not.toContain("'unsafe-eval'");
|
|
});
|
|
});
|
|
|
|
describe('forced-HTTPS redirect', () => {
|
|
const saved = { FORCE_HTTPS: process.env.FORCE_HTTPS, APP_URL: process.env.APP_URL };
|
|
|
|
afterEach(() => {
|
|
process.env.FORCE_HTTPS = saved.FORCE_HTTPS;
|
|
process.env.APP_URL = saved.APP_URL;
|
|
if (saved.FORCE_HTTPS === undefined) delete process.env.FORCE_HTTPS;
|
|
if (saved.APP_URL === undefined) delete process.env.APP_URL;
|
|
});
|
|
|
|
async function redirectLocation(): Promise<string> {
|
|
const app = express();
|
|
applyGlobalMiddleware(app);
|
|
app.get('/trips', (_req, res) => res.json({ ok: true }));
|
|
const res = await request(app).get('/trips').set('Host', 'evil.example.com');
|
|
return String(res.headers.location || '');
|
|
}
|
|
|
|
it('redirects to the configured APP_URL host, not the Host header the caller sent', async () => {
|
|
process.env.FORCE_HTTPS = 'true';
|
|
process.env.APP_URL = 'https://trip.pakulat.org';
|
|
expect(await redirectLocation()).toBe('https://trip.pakulat.org/trips');
|
|
});
|
|
|
|
it('falls back to the request host when APP_URL is unset', async () => {
|
|
process.env.FORCE_HTTPS = 'true';
|
|
delete process.env.APP_URL;
|
|
expect(await redirectLocation()).toBe('https://evil.example.com/trips');
|
|
});
|
|
});
|