fix(search): bound and sanitize the geocode proxy

- reject blank viewport-bias components before Number() reads them as 0
- give the upstream call a 10s deadline and a 256 KiB capped body read
- return fixed public text on failure instead of the upstream message
This commit is contained in:
Niccolò Saggese committed 2026-09-21 11:56:33 +02:00
1 parent c5b1d41cba
commit 771c9e5632
2 files changed
+88 -19

No files matched your search

+42 -5
View File
@@ -73,6 +73,10 @@ test('geocoder query and viewport bias are validated before Google is paid', ()
'north,-97.9|30.4,-97.6',
'91,-97.9|30.4,-97.6',
'30.1,-181|30.4,-97.6',
// A blank component must not read as 0 — that is the equator, not a corner.
'30.1,-97.9|30.4,',
'30.1,|30.4,-97.6',
'30.1,-97.9| ,-97.6',
])
assert.equal(geocodeBounds(new URLSearchParams({ bounds })), null);
});
@@ -82,10 +86,7 @@ test('the geocode routes keep the key server-side and answer in Google shape', a
const routes = installRoutes({
fetchImpl: async (url) => {
upstream.push(new URL(String(url)));
return {
ok: true,
json: async () => ({ status: 'OK', results: [{ place_id: 'fixture' }] }),
};
return Response.json({ status: 'OK', results: [{ place_id: 'fixture' }] });
},
});
const forward = routes.get('/api/google/geocode');
@@ -123,7 +124,7 @@ test('a bad or keyless geocode request never reaches Google', async () => {
const upstream = [];
const fetchImpl = async (url) => {
upstream.push(String(url));
return { ok: true, json: async () => ({ status: 'OK', results: [] }) };
return Response.json({ status: 'OK', results: [] });
};
const routes = installRoutes({ fetchImpl });
@@ -165,3 +166,39 @@ test('a bad or keyless geocode request never reaches Google', async () => {
assert.deepEqual(upstream, []);
});
test('an upstream failure reaches the browser as fixed public text', async () => {
const routes = installRoutes({
fetchImpl: async () => {
throw new Error('connect ECONNREFUSED 10.0.0.7:443');
},
});
for (const path of ['/api/google/geocode', '/api/google/reverse-geocode']) {
const response = await invokeRoute(routes.get(path), {
url: '/?address=Austin&lat=30&lon=-97',
});
assert.equal(response.statusCode, 502);
assert.deepEqual(response.body, {
status: 'UNKNOWN_ERROR',
results: [],
error: 'Google Geocoding request failed',
});
}
});
test('an oversized upstream body is dropped rather than buffered', async () => {
const routes = installRoutes({
// Two hundred times the 256 KiB ceiling, declared honestly.
fetchImpl: async () =>
Response.json({ status: 'OK', results: [{ pad: 'x'.repeat(52_428_800) }] }),
});
const response = await invokeRoute(routes.get('/api/google/geocode'), {
url: '/?address=Austin',
});
assert.equal(response.statusCode, 502);
assert.deepEqual(response.body, {
status: 'UNKNOWN_ERROR',
results: [],
error: 'Google Geocoding request failed',
});
});