mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-06 03:14:00 +00:00
fix(overpass): keyless gate, loopback admission headroom and per-phase arrival budgets
Address gekh's review of 69a87fe2:
- P1: /api/overpass now uses admitSameSiteKeyless, like /api/terrain/heights,
so an instance behind the README's reverse proxy keeps its Overpass-backed
layers; cross-site shapes are still refused and forwarding headers no
longer launder them. SECURITY.md, the gate header and CURRENT-STATE say so.
- P2: admission keys IPv6 peers by /64 and folds IPv4-mapped peers
(clientNetworkKey), and loopback has its own 300/min window outside the
900/min global, so a few LAN peers cannot lock the owner's browser out.
- P2 (track): the settle and reveal frames each get their own 500 ms budget,
measured from moveEnd and from the settle frame respectively, so a ~300 ms
SwiftShader frame no longer trips the reveal deadline while both stay
inside the 1 s idle refresh.
- Smaller: drop the dead settings-block check and stale probe comment in
query.js, clamp whitespace-tolerant [ timeout : N ], and add tests for the
global window, the /64 keying, loopback headroom, proxied admission and
cache hits spending admission but not upstream budget.
Co-Authored-By: Claude Fable 5.1 <[email protected]>
This commit is contained in:
1 parent
f3bb697ed4
commit
55f979c128
14 files changed
+357
-49
No files matched your search
@@ -1,6 +1,11 @@
|
||||
import { makeRateLimiter, clientKey } from './common/rate-limit.js';
|
||||
import {
|
||||
makeRateLimiter,
|
||||
clientKey,
|
||||
clientNetworkKey,
|
||||
isLoopbackPeer,
|
||||
} from './common/rate-limit.js';
|
||||
import { readRequestBodyCapped } from './common/request.js';
|
||||
import { admitSameSite } from './common/same-site.js';
|
||||
import { admitSameSiteKeyless } from './common/same-site.js';
|
||||
import {
|
||||
OVERPASS_MAX_BODY_BYTES,
|
||||
OVERPASS_MAX_CONCURRENT,
|
||||
@@ -29,10 +34,16 @@ const _overpassInFlight = new Map();
|
||||
let _overpassConcurrent = 0;
|
||||
|
||||
// Bound body-reading and validation work, including malformed requests and
|
||||
// cache hits. Keep the tighter upstream budget below independent of this gate.
|
||||
const _overpassRequestLimiter = makeRateLimiter({
|
||||
windowMs: 60_000,
|
||||
max: 300,
|
||||
// cache hits, before a byte is read. Non-loopback peers share one global
|
||||
// window so address rotation cannot mint admission without bound (IPv6 peers
|
||||
// are keyed by /64 for the same reason); loopback — the owner's own browser
|
||||
// and the repo QA harnesses — has its own per-peer window outside that global,
|
||||
// so a few busy LAN peers cannot lock the owner out of the map. Keep the
|
||||
// tighter upstream budget below independent of this gate.
|
||||
const OVERPASS_ADMISSION_WINDOW = { windowMs: 60_000, max: 300 };
|
||||
const _overpassLoopbackAdmission = makeRateLimiter(OVERPASS_ADMISSION_WINDOW);
|
||||
const _overpassPeerAdmission = makeRateLimiter({
|
||||
...OVERPASS_ADMISSION_WINDOW,
|
||||
globalMax: 900,
|
||||
});
|
||||
|
||||
@@ -42,6 +53,20 @@ const _overpassRateLimiter = makeRateLimiter({
|
||||
globalMax: 300,
|
||||
});
|
||||
|
||||
/**
|
||||
* Spend one admission slot for this request's peer; false when it is over its
|
||||
* window (or, for a non-loopback peer, when the shared global window is full).
|
||||
* @param {import('http').IncomingMessage} req
|
||||
* @param {string} peer - `clientNetworkKey(req)`.
|
||||
* @returns {boolean}
|
||||
*/
|
||||
function admitOverpassRequest(req, peer) {
|
||||
const admit = isLoopbackPeer(clientKey(req))
|
||||
? _overpassLoopbackAdmission
|
||||
: _overpassPeerAdmission;
|
||||
return admit(peer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write a completed Overpass payload to the HTTP response.
|
||||
*
|
||||
@@ -103,8 +128,13 @@ function overpassProxy({ routing = {} } = {}) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (admitSameSite(req, res)) return;
|
||||
if (!_overpassRequestLimiter(clientKey(req))) {
|
||||
// Keyless gate: refuses cross-site browser requests but, unlike the
|
||||
// cost-bearing routes, admits the forwarding headers a reverse proxy
|
||||
// adds — the layers that depend on Overpass must keep working for an
|
||||
// instance deployed the way the README documents.
|
||||
if (admitSameSiteKeyless(req, res)) return;
|
||||
const peer = clientNetworkKey(req);
|
||||
if (!admitOverpassRequest(req, peer)) {
|
||||
res.writeHead(429, {
|
||||
'Content-Type': 'application/json',
|
||||
'Cache-Control': 'no-store',
|
||||
@@ -163,7 +193,7 @@ function overpassProxy({ routing = {} } = {}) {
|
||||
// requests; boundary-class queries keep their month-long TTL.
|
||||
readDisk: () =>
|
||||
readOverpassDisk(cacheKey, overpassDiskTtlMs(cacheKey)),
|
||||
allowUpstream: () => _overpassRateLimiter(clientKey(req)),
|
||||
allowUpstream: () => _overpassRateLimiter(peer),
|
||||
});
|
||||
if (preflight.source === 'RATE_LIMITED') {
|
||||
res.writeHead(429, {
|
||||
|
||||
Reference in new issue
Block a user