openvibers/openvibe.media Commits

Media: nginx refuses /internal/ and /metrics case-insensitively (#63)

OP OpenVibers committed 10 Oct 2026 · 26849ad3fc · 3 files, +3 −3

* Media: 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 edge block refuses /metrics in any letter case

Changes

ddiff --git a/deploy/nginx/edge.openvibe.media.conf b/deploy/nginx/edge.openvibe.media.conf
iindex 95627ef..254437b 100644
---- a/deploy/nginx/edge.openvibe.media.conf
++++ b/deploy/nginx/edge.openvibe.media.conf
@@@ -23,7 +23,7 @@ server {
proxy_send_timeout 3600s;
# Prometheus metrics are for loopback scrapers only (the app 404s proxied callers too).
-- location = /metrics { return 404; }
++ location ~* ^/metrics(/|$) { return 404; }
location /_media_shield/b2/ {
internal;
ddiff --git a/deploy/nginx/openvibe.media.conf b/deploy/nginx/openvibe.media.conf
iindex dcadfa7..6c2ec65 100644
---- a/deploy/nginx/openvibe.media.conf
++++ b/deploy/nginx/openvibe.media.conf
@@@ -19,7 +19,7 @@ server {
proxy_send_timeout 3600s;
# Prometheus metrics are for loopback scrapers only (the app 404s proxied callers too).
-- location = /metrics { return 404; }
++ location ~* ^/metrics(/|$) { return 404; }
location / {
proxy_pass http://127.0.0.1:4100;
ddiff --git a/test/placement-shield.test.js b/test/placement-shield.test.js
iindex 8102b0d..902ad19 100644
---- a/test/placement-shield.test.js
++++ b/test/placement-shield.test.js
@@@ -45,7 +45,7 @@ const assert = require('assert');
assert.match(edgeConf, /proxy_set_header X-Media-Shield-Host \$server_name;/, 'the edge block vouches for itself');
assert.match(mainConf, /proxy_set_header X-Media-Shield-Host "";/, 'openvibe.media clears the header a client could send through Cloudflare');
for (const c of [edgeConf, mainConf]) assert.match(c, /proxy_set_header X-Forwarded-Host "";/);
-- for (const d of ['client_max_body_size 4096m;', 'proxy_read_timeout 3600s;', 'location = /metrics { return 404; }']) assert.ok(edgeConf.includes(d), `the edge block has ${d}`);
++ for (const d of ['client_max_body_size 4096m;', 'proxy_read_timeout 3600s;', 'location ~* ^/metrics(/|$) { return 404; }']) assert.ok(edgeConf.includes(d), `the edge block has ${d}`);
assert.match(conf('media-shield.http.conf'), /allow 127\.0\.0\.1;\s*deny all;/, 'only loopback may refresh the cache');
// send(): the route's headers plus the internal redirect, an empty 200.