Read API paths and forwarded requests as the router does

The router reads an absolute-form target's path without resolving dot
segments, so a target such as http://host/api/../x still reaches /api
middleware. Take that path the same way when deciding whether the API
response headers apply.

The Street View fallback now treats any forwarding header as forwarded, even
an empty one, before it spends the key.
This commit is contained in:
Sameh Khamis committed 2026-10-21 15:43:29 -07:00
1 parent afe05454d9
commit b75db874c6
5 files changed
+32 -8

No files matched your search

+3
View File
@@ -491,6 +491,9 @@ test('a forwarded same-origin request reaches every CCTV route but not the Stree
{ via: '1.1 proxy' },
{ forwarded: 'for=203.0.113.9' },
{ 'x-real-ip': '203.0.113.9' },
{ 'x-forwarded-for': '' },
{ 'x-forwarded-for': ' ' },
{ 'cf-connecting-ip': '' },
]) {
const headers = { host, 'sec-fetch-site': 'same-origin', ...forwarded };
const label = JSON.stringify(forwarded);
+8
View File
@@ -9,6 +9,7 @@ import { test } from 'node:test';
import assert from 'node:assert/strict';
import { loadTflSourcesFromOpenData } from '../../server/providers/cctv/sources.js';
import { fetchWithinHost } from '../../server/providers/cctv/media.js';
import { pinnedCameraUrl } from '../../server/providers/cctv/normalize.js';
const BUCKET = 'https://s3-eu-west-1.amazonaws.com/jamcams.tfl.gov.uk/';
@@ -104,3 +105,10 @@ test('a redirect on a pinned camera host stays inside the pinned directory', asy
);
assert.equal(await unpinned.text(), 'frame');
});
test('a pin prefix without a trailing slash still names a directory', () => {
assert.equal(
pinnedCameraUrl('https://h.example/camsX/a.jpg', 'https://h.example/cams'),
null,
);
});
+3
View File
@@ -366,6 +366,9 @@ test('every API response carries the API policy and nosniff on real dev and prev
['/api.x', 404],
[`http://localhost:${port}/api/fixture/ok`, 200],
[`http://localhost:${port}/API/nope`, 404],
// The router keeps dot segments in an absolute-form target's path.
['http://elsewhere.example/api/../x', 404],
['http://elsewhere.example/API/%2e%2e/x', 404],
['/api/fixture/ok', 403, policy, { host: 'elsewhere.example' }],
// A route's own policy is added to the API policy, never replaces it.
['/api/fixture/own-policy', 200, `${policy}, frame-ancestors 'none'`],