openvibers/openvibe.search Commits

Search: nginx refuses /internal/ and /metrics case-insensitively (#25)

OP OpenVibers committed 10 Oct 2026 · ab0100b793 · 2 files, +2 −2

* Search: 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 in any letter case

Changes

ddiff --git a/deploy/nginx/search.openvibe.network.conf b/deploy/nginx/search.openvibe.network.conf
iindex bb75d4e..7fc7a92 100644
---- a/deploy/nginx/search.openvibe.network.conf
++++ b/deploy/nginx/search.openvibe.network.conf
@@@ -74,7 +74,7 @@ server {
location = /release.json { proxy_pass http://127.0.0.1:4710; }
# Metrics are for host-local scrapers only (http://127.0.0.1:4710/metrics).
-- location = /metrics { return 404; }
++ location ~* ^/metrics(/|$) { return 404; }
# ── Search page ─────────────────────────────────────────
location = / {
ddiff --git a/test/vhost.test.js b/test/vhost.test.js
iindex d7d1a5d..9ea6276 100644
---- a/test/vhost.test.js
++++ b/test/vhost.test.js
@@@ -26,7 +26,7 @@ t('wildcard certificate and client address from $remote_addr only', () => {
});
t('/metrics is 404; owner API, webhook and document writes are loopback-only', () => {
-- assert.match(conf, /location = \/metrics \{ return 404; \}/);
++ assert.match(conf, /location ~\* \^\/metrics\(\/\|\$\) \{ return 404; \}/, '/metrics in any letter case');
assert.match(block('location = /updates'), /limit_except GET \{ deny all; \}/, 'the update log, GET only');
assert.match(conf, /location \^~ \/shared\/ \{ limit_except GET \{ deny all; \} proxy_pass http:\/\/127\.0\.0\.1:4710; \}/, 'the site\'s own Frame files');
assert.match(block('location ^~ /internal/'), loopbackOnly);