openvibers/openvibe.community Commits
The CSP allows Cloudflare Web Analytics (browser check, WS-Q task 3): script-src https://static.cloudflareinsights.com loads the beacon Cloudflare injects at the edge, connect-src https://cloudflareinsights.com is where it reports. The network's privacy text already says Cloudflare may measure performance, and Live and Tools allow it; here the policy blocked it and every page view logged a CSP error. Test: seo.test.js
Changes
@@@ -79,12 +79,14 @@ function createApp(opts = {}) {directives: {defaultSrc: ["'self'"],// Shared chrome (theme-loader, navbar, footer, history, ov-mark) comes from the Network.-- scriptSrc: ["'self'", "'unsafe-inline'", 'https://openvibe.network', 'https://cdnjs.cloudflare.com'],++ // Cloudflare Web Analytics: Cloudflare injects its beacon at the edge and the privacy text says it may++ // measure performance; script-src loads the beacon, connect-src is where it reports.++ scriptSrc: ["'self'", "'unsafe-inline'", 'https://openvibe.network', 'https://cdnjs.cloudflare.com', 'https://static.cloudflareinsights.com'],styleSrc: ["'self'", "'unsafe-inline'", 'https://cdnjs.cloudflare.com', 'https://fonts.googleapis.com', 'https://openvibe.network'],fontSrc: ["'self'", 'https://cdnjs.cloudflare.com', 'https://fonts.gstatic.com', 'data:'],// Screenshots serve from openvibe.media (which may 302 to object storage); avatars from Live/Network.imgSrc: ["'self'", 'data:', 'blob:', 'https:'],-- connectSrc: ["'self'", 'https://openvibe.network', 'https://openvibe.live', 'https://openvibe.media', 'https://events.openvibe.network'],++ connectSrc: ["'self'", 'https://openvibe.network', 'https://openvibe.live', 'https://openvibe.media', 'https://events.openvibe.network', 'https://cloudflareinsights.com'],// The Network's hidden /sso/check frame: how a visitor who is signed in elsewhere gets signed in here.frameSrc: ["'self'", 'https://openvibe.network'],frameAncestors: ["'self'"],@@@ -52,7 +52,11 @@ const { boot, check, done } = require('./helpers/app');await check('security headers are present on pages', async () => {const r = await t.get('/');-- assert.ok(r.headers.get('content-security-policy').includes("script-src 'self' 'unsafe-inline' https://openvibe.network"));++ const csp = r.headers.get('content-security-policy');++ assert.ok(csp.includes("script-src 'self' 'unsafe-inline' https://openvibe.network"));++ // Cloudflare Web Analytics (injected at the edge, disclosed in the privacy text) may load and report.++ assert.match(csp, /script-src [^;]*https:\/\/static\.cloudflareinsights\.com/);++ assert.match(csp, /connect-src [^;]*https:\/\/cloudflareinsights\.com/);assert.strictEqual(r.headers.get('x-content-type-options'), 'nosniff');assert.strictEqual(r.headers.get('x-powered-by'), null);});
@@ -79,12 +79,14 @@ function createApp(opts = {}) {
@@ -79,12 +79,14 @@ function createApp(opts = {}) {
directives: {
directives: {
defaultSrc: ["'self'"],
defaultSrc: ["'self'"],
// Shared chrome (theme-loader, navbar, footer, history, ov-mark) comes from the Network.
// Shared chrome (theme-loader, navbar, footer, history, ov-mark) comes from the Network.
- scriptSrc: ["'self'", "'unsafe-inline'", 'https://openvibe.network', 'https://cdnjs.cloudflare.com'],
+ // Cloudflare Web Analytics: Cloudflare injects its beacon at the edge and the privacy text says it may
+ // measure performance; script-src loads the beacon, connect-src is where it reports.
+ scriptSrc: ["'self'", "'unsafe-inline'", 'https://openvibe.network', 'https://cdnjs.cloudflare.com', 'https://static.cloudflareinsights.com'],
styleSrc: ["'self'", "'unsafe-inline'", 'https://cdnjs.cloudflare.com', 'https://fonts.googleapis.com', 'https://openvibe.network'],
styleSrc: ["'self'", "'unsafe-inline'", 'https://cdnjs.cloudflare.com', 'https://fonts.googleapis.com', 'https://openvibe.network'],
fontSrc: ["'self'", 'https://cdnjs.cloudflare.com', 'https://fonts.gstatic.com', 'data:'],
fontSrc: ["'self'", 'https://cdnjs.cloudflare.com', 'https://fonts.gstatic.com', 'data:'],
// Screenshots serve from openvibe.media (which may 302 to object storage); avatars from Live/Network.
// Screenshots serve from openvibe.media (which may 302 to object storage); avatars from Live/Network.
imgSrc: ["'self'", 'data:', 'blob:', 'https:'],
imgSrc: ["'self'", 'data:', 'blob:', 'https:'],
- connectSrc: ["'self'", 'https://openvibe.network', 'https://openvibe.live', 'https://openvibe.media', 'https://events.openvibe.network'],
+ connectSrc: ["'self'", 'https://openvibe.network', 'https://openvibe.live', 'https://openvibe.media', 'https://events.openvibe.network', 'https://cloudflareinsights.com'],
// The Network's hidden /sso/check frame: how a visitor who is signed in elsewhere gets signed in here.
// The Network's hidden /sso/check frame: how a visitor who is signed in elsewhere gets signed in here.
frameSrc: ["'self'", 'https://openvibe.network'],
frameSrc: ["'self'", 'https://openvibe.network'],
frameAncestors: ["'self'"],
frameAncestors: ["'self'"],
@@ -52,7 +52,11 @@ const { boot, check, done } = require('./helpers/app');
@@ -52,7 +52,11 @@ const { boot, check, done } = require('./helpers/app');
await check('security headers are present on pages', async () => {
await check('security headers are present on pages', async () => {
const r = await t.get('/');
const r = await t.get('/');
- assert.ok(r.headers.get('content-security-policy').includes("script-src 'self' 'unsafe-inline' https://openvibe.network"));
+ const csp = r.headers.get('content-security-policy');
+ assert.ok(csp.includes("script-src 'self' 'unsafe-inline' https://openvibe.network"));
+ // Cloudflare Web Analytics (injected at the edge, disclosed in the privacy text) may load and report.
+ assert.match(csp, /script-src [^;]*https:\/\/static\.cloudflareinsights\.com/);
+ assert.match(csp, /connect-src [^;]*https:\/\/cloudflareinsights\.com/);
assert.strictEqual(r.headers.get('x-content-type-options'), 'nosniff');
assert.strictEqual(r.headers.get('x-content-type-options'), 'nosniff');
assert.strictEqual(r.headers.get('x-powered-by'), null);
assert.strictEqual(r.headers.get('x-powered-by'), null);
});
});