openvibers/openvibe.events Commits

Events: nginx refuses /internal/ and /metrics case-insensitively (#36)

OP OpenVibers committed 10 Oct 2026 · 4a2e71f13b · 2 files, +3 −3

* Events: nginx refuses /internal/ and /metrics case-insensitively

nginx config only. Express routes case-insensitively, so a case variant such as `/Internal/events` or `/METRICS` slipped past the case-sensitive `location /internal/ { return 404; }` and `location = /metrics { return 404; }` blocks and reached the app. Those paths were still guarded in the app (the Events signature, the loopback check on metrics). Both blocks are now `location ~* ^/internal(/|$)` and `location ~* ^/metrics(/|$)`. The independent security reviews of 2026-10-09/10 found this in several services; this applies it network-wide.

* test: the vhost refuses /metrics and /internal/ in any letter case

Changes

ddiff --git a/deploy/nginx/openvibe.events.conf b/deploy/nginx/openvibe.events.conf
iindex 013445a..ad4ad96 100644
---- a/deploy/nginx/openvibe.events.conf
++++ b/deploy/nginx/openvibe.events.conf
@@@ -109,10 +109,10 @@ server {
location = /limits.json { proxy_pass http://127.0.0.1:4300; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $remote_addr; proxy_set_header CF-Connecting-IP $remote_addr; }
# Metrics are for host-local scrapers only (http://127.0.0.1:4300/metrics).
-- location = /metrics { return 404; }
++ location ~* ^/metrics(/|$) { return 404; }
# Internal service endpoints (Live's /internal/media-events, ...) are host-local.
-- location /internal/ { return 404; }
++ location ~* ^/internal(/|$) { return 404; }
# ── Token-guarded API for developer apps (see header) ──
# Per-project quotas are enforced by the service; this only caps per-IP request rates.
ddiff --git a/test/home.test.js b/test/home.test.js
iindex 72582cc..bcd811b 100644
---- a/test/home.test.js
++++ b/test/home.test.js
@@@ -158,7 +158,7 @@ t('deploy/nginx/openvibe.events.conf is the API and product origin, with no redi
assert.ok(conf.includes('location /api/ {'), 'the /api/ refusal block');
assert.ok(conf.includes('proxy_buffering off;'), 'SSE is not buffered');
assert.ok(conf.includes('proxy_read_timeout 1h;'), 'SSE keeps its long read timeout');
-- const refusals = [conf.indexOf('location = /metrics { return 404; }'), conf.indexOf('location /internal/ { return 404; }')];
++ const refusals = [conf.indexOf('location ~* ^/metrics(/|$) { return 404; }'), conf.indexOf('location ~* ^/internal(/|$) { return 404; }')];
assert.ok(refusals.every((i) => i >= 0), 'metrics and /internal/ are refused');
assert.ok(!conf.includes('events.openvibe.network$request_uri'), 'no catch-all redirect to the old API host');
const old = fs.readFileSync(path.join(__dirname, '..', 'deploy', 'nginx', 'events.openvibe.network.conf'), 'utf8')