fix(osm): gate place search like /api/overpass after #1308

#1308 moved /api/overpass to the keyless cross-site gate: foreign origins
and cross-site fetches are still refused, but forwarding headers are not,
so the map works behind a reverse proxy. Voice place search spends the same
operator Overpass, so it now uses the same gate; with the stricter one,
place search failed behind a proxy while the map kept working. The prompt
lint note records the merged size, 55,370 with an operator Overpass.
This commit is contained in:
Manjunath committed 2026-11-03 22:18:06 +05:30
1 parent 1e12d5ba26
commit 582bfce660
3 files changed
+14 -4

No files matched your search

@@ -1,6 +1,6 @@
import path from 'node:path';
import { makeRateLimiter, clientKey } from '../common/rate-limit.js';
import { admitSameSite } from '../common/same-site.js';
import { admitSameSiteKeyless } from '../common/same-site.js';
import { createBoundedCache } from '../common/boundedCache.js';
import {
compileOsmFeatureQuery,
@@ -201,8 +201,9 @@ export function installOsmFeaturesRoute(
middlewares.use('/api/osm/features', async (req, res) => {
const send = sender(res);
if (req.method !== 'GET') return send(jsonError(405, 'Method Not Allowed'));
// Quota-bearing: a page elsewhere must not spend the operator's Overpass.
if (admitSameSite(req, res)) return undefined;
// A page elsewhere must not spend the operator's Overpass. The keyless
// gate, as on /api/overpass: voice works behind a reverse proxy too.
if (admitSameSiteKeyless(req, res)) return undefined;
if (!configured())
return send(
jsonError(503, 'Place search needs a configured Overpass server.', {
+8
View File
@@ -380,6 +380,14 @@ test('a cross-site page cannot spend the operator Overpass', async () => {
'sec-fetch-site': 'same-origin',
});
assert.equal(own.status, 200);
// Like /api/overpass, it works behind a reverse proxy: forwarding headers
// alone are not a refusal.
const proxied = await request('preset=school&bbox=0,0,2,2', undefined, {
host: 'localhost:5173',
'sec-fetch-site': 'same-origin',
'x-forwarded-for': '203.0.113.7',
});
assert.equal(proxied.status, 200);
});
test('an upstream refusal is not an empty answer', async () => {
+2 -1
View File
@@ -346,7 +346,8 @@ test('deixis prompt lint: one short rule, and every sentinel it names exists in
// it to 51,646. Voice geometry (resolve_area, find_imagery, osm_query and
// the analyst area scopes) raised the ceiling to 55,500; tightening the
// AREAS rule and the area tool descriptions left it at 55,263 with an
// operator Overpass, a deliberate ~240-character margin. A new tool or rule
// operator Overpass; main's Sea Marks and Ski Pistes layers bring it to
// 55,370, ~130 under. A new tool or rule
// has to fit under this ceiling.
const size = instructions.length + JSON.stringify(GEV_REALTIME_TOOLS).length;
assert.ok(size <= 55_500, `prompt size ${size}`);