openvibers/openvibe.network Commits

Brand assets (/assets/*: logo, app icons, OG images) are readable cross-origin and cached for a day: helmet's Cross-Origin-Resource-Policy same-origin blocked the logo on every placeholder site

OP OpenVibers committed 24 Sept 2026 · fce0931200 · 4 files, +37 −8


Changes

ddiff --git a/package.json b/package.json
iindex 44487b3d..e7d48164 100644
---- a/package.json
++++ b/package.json
@@@ -6,7 +6,7 @@
"scripts": {
"start": "node server/index.js",
"dev": "NODE_ENV=development node server/index.js",
-- "test": "node test/registry-bootstrap.test.js && node test/nginx-generator-live.test.js && node test/admin-refresh.test.js && node test/history-sso.test.js && node test/sso-check.test.js && node test/fedcm.test.js && node test/domains.test.js && node test/frame.test.js && node test/avatar.test.js && node test/roadmap-baseline.test.js && node test/identity-subjects.test.js && node test/principals.test.js && node test/modules.test.js && node test/module-events.test.js && node test/registry-ecosystem.test.js && node test/registry-exposure.test.js && node test/registry-topics.test.js && node test/service-principal.test.js && node test/oauth-pkce.test.js && node test/developer-projects.test.js && node test/developer-defaults.test.js && node test/public-cors.test.js && node test/observability.test.js && node test/status.test.js && node test/events-consumer.test.js && node test/security-session.test.js && node test/security-redirects.test.js && node test/analytics-privacy.test.js && node test/data-ownership.test.js && node test/unknown-paths.test.js && node test/oidc-discovery.test.js && node test/refresh-tokens.test.js && node test/provider-secrets.test.js && node test/first-party-origins.test.js && node test/updates.test.js",
++ "test": "node test/registry-bootstrap.test.js && node test/nginx-generator-live.test.js && node test/admin-refresh.test.js && node test/history-sso.test.js && node test/sso-check.test.js && node test/fedcm.test.js && node test/domains.test.js && node test/frame.test.js && node test/avatar.test.js && node test/roadmap-baseline.test.js && node test/identity-subjects.test.js && node test/principals.test.js && node test/modules.test.js && node test/module-events.test.js && node test/registry-ecosystem.test.js && node test/registry-exposure.test.js && node test/registry-topics.test.js && node test/service-principal.test.js && node test/oauth-pkce.test.js && node test/developer-projects.test.js && node test/developer-defaults.test.js && node test/public-cors.test.js && node test/observability.test.js && node test/status.test.js && node test/events-consumer.test.js && node test/security-session.test.js && node test/security-redirects.test.js && node test/analytics-privacy.test.js && node test/data-ownership.test.js && node test/unknown-paths.test.js && node test/oidc-discovery.test.js && node test/refresh-tokens.test.js && node test/provider-secrets.test.js && node test/first-party-origins.test.js && node test/updates.test.js && node test/static-headers.test.js",
"reset-db": "node server/reset-db.js",
"grant-admin": "node server/grant-admin.js"
},
ddiff --git a/server/index.js b/server/index.js
iindex b6f7934c..a2eec6e9 100644
---- a/server/index.js
++++ b/server/index.js
@@@ -654,13 +654,7 @@ app.get(['/sso/fanout', '/sso/fanout.html'], (req, res) => {
res.sendFile(path.join(__dirname, '..', 'public', 'sso-fanout.html'));
});
--app.use(express.static(path.join(__dirname, '..', 'public'), {
-- setHeaders(res, filePath) {
-- if (filePath.endsWith('.js') || filePath.endsWith('.css')) {
-- res.setHeader('Cache-Control', 'no-cache');
-- }
-- },
--}));
++app.use(express.static(path.join(__dirname, '..', 'public'), { setHeaders: require('./static-headers').publicStaticHeaders }));
// openvibe-shared: the OpenVibe.Shared release package.json pins, from node_modules.
const sharedFiles = require('openvibe-shared/files');
ddiff --git a/server/static-headers.js b/server/static-headers.js
nnew file mode 100644
iindex 00000000..01de01a9
---- /dev/null
++++ b/server/static-headers.js
@@@ -0,0 +1,21 @@
++'use strict';
++/**
++ * Headers for Network's public/ files. /assets/* are the network's brand assets (logo, app icons,
++ * OG images) that every OpenVibe site and placeholder shows from openvibe.network, so they are
++ * readable cross-origin (helmet's default Cross-Origin-Resource-Policy: same-origin broke the logo
++ * on every placeholder) and cached for a day. Scripts and stylesheets revalidate every time.
++ */
++const path = require('path');
++
++function publicStaticHeaders(res, filePath) {
++ const p = String(filePath).split(path.sep).join('/');
++ if (/\/public\/assets\//.test(p)) {
++ res.setHeader('Cross-Origin-Resource-Policy', 'cross-origin');
++ res.setHeader('Access-Control-Allow-Origin', '*');
++ res.setHeader('Cache-Control', 'public, max-age=86400');
++ return;
++ }
++ if (p.endsWith('.js') || p.endsWith('.css')) res.setHeader('Cache-Control', 'no-cache');
++}
++
++module.exports = { publicStaticHeaders };
ddiff --git a/test/static-headers.test.js b/test/static-headers.test.js
nnew file mode 100644
iindex 00000000..82756705
---- /dev/null
++++ b/test/static-headers.test.js
@@@ -0,0 +1,14 @@
++'use strict';
++// Brand assets are readable from every OpenVibe site (the placeholders' logo was blocked by CORP).
++const assert = require('assert');
++const { publicStaticHeaders } = require('../server/static-headers');
++const res = () => { const h = {}; return { h, setHeader: (k, v) => { h[k] = v; } }; };
++let r = res(); publicStaticHeaders(r, '/opt/openvibe.network/public/assets/logo.svg');
++assert.strictEqual(r.h['Cross-Origin-Resource-Policy'], 'cross-origin');
++assert.strictEqual(r.h['Cache-Control'], 'public, max-age=86400');
++r = res(); publicStaticHeaders(r, '/opt/openvibe.network/public/js/app.js');
++assert.strictEqual(r.h['Cache-Control'], 'no-cache');
++assert.strictEqual(r.h['Cross-Origin-Resource-Policy'], undefined, 'only brand assets are cross-origin');
++r = res(); publicStaticHeaders(r, '/opt/openvibe.network/public/index.html');
++assert.deepStrictEqual(r.h, {});
++console.log('static headers: all checks passed');