P7 r8 fix: forward_auth trust boundary (XFF/XFH honored only from trusted proxy; direct/spoofed requests use socket identity) + adversarial tests

This commit is contained in:
Hermes committed 2026-10-03 23:25:23 -07:00
1 parent 1e606bf0f3
commit d4973f3af3
3 files changed
+90 -8

No files matched your search

@@ -66,10 +66,15 @@ function buildDeps(overrides = {}) {
return deps;
}
function buildApp(deps) {
function buildApp(deps, opts = {}) {
const app = express();
const service = createForwardAuthService(deps);
app.use('/network/forward-auth', forwardAuthRoutes({ service }));
app.use('/network/forward-auth', forwardAuthRoutes({
service,
// supertest connects over loopback: that IS the trusted reverse proxy
// in the test harness (mirrors Caddy on-host in production).
trustedProxies: opts.trustedProxies !== undefined ? opts.trustedProxies : ['127.0.0.1', '::1'],
}));
return app;
}
@@ -326,6 +331,46 @@ describe('DC-154 route (GET/POST /network/forward-auth/)', () => {
// ── X-Forwarded-For parsing ─────────────────────────────────────────────────
describe('DC-154 trust boundary (forwarded headers only from trusted proxy)', () => {
test('DIRECT request with spoofed X-Forwarded-For (no trusted proxy) → identity NOT inherited → 401 unknown-device', async () => {
const app = buildApp(buildDeps(), { trustedProxies: [] }); // trust disabled
const res = await request(app).get('/network/forward-auth/')
.set('X-Forwarded-For', '10.53.0.7') // spoofed: a KNOWN allowed device
.set('X-Forwarded-Host', 'plex.sami');
expect(res.status).toBe(401);
expect(res.body).toEqual({ error: 'unknown-device' });
});
test('Spoofed X-Forwarded-Host from an untrusted peer is ignored (socket identity used instead)', async () => {
const app = buildApp(buildDeps(), { trustedProxies: [] });
const res = await request(app).get('/network/forward-auth/')
.set('X-Forwarded-For', '10.53.0.7')
.set('X-Forwarded-Host', 'vault.sami');
// socket addr (127.0.0.1) is not a known device → unknown-device, NOT
// a vault authorization decision based on the spoofed host+ip pair.
expect(res.status).toBe(401);
expect(res.body).toEqual({ error: 'unknown-device' });
});
test('Trusted proxy present → headers honored as before (regression)', async () => {
const app = buildApp(buildDeps()); // loopback trusted
const res = await request(app).get('/network/forward-auth/')
.set('X-Forwarded-For', '10.53.0.7, 127.0.0.1')
.set('X-Forwarded-Host', 'plex.sami');
expect(res.status).toBe(200);
expect(res.body.personId).toBe(PERSON);
});
test('XFF chain from trusted proxy picks the first entry (original client)', async () => {
const app = buildApp(buildDeps());
const res = await request(app).get('/network/forward-auth/')
.set('X-Forwarded-For', '10.53.0.9, 127.0.0.1') // first = other person's device
.set('X-Forwarded-Host', 'plex.sami');
expect(res.status).toBe(200);
expect(res.body.personId).toBe(OTHER_PERSON);
});
});
describe('DC-154 parseForwardedFor', () => {
test('single entry', () => {
expect(parseForwardedFor('203.0.113.7')).toEqual(['203.0.113.7']);