Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Security

- Pages no longer permit stylesheets or fonts from Google Fonts, which nothing
requests.

- The `linux/arm64` release image is scanned for vulnerabilities before it is
published, the same as `linux/amd64`.

Expand Down
27 changes: 27 additions & 0 deletions api/test/nginx-config.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,33 @@ test('every policy in the config states a frame-ancestors', () => {
}
});

/* ── the hosts a page may reach ───────────────────────────────────────────── */

/* Google Fonts was allowed by three policies for a widget that no longer
exists, so every page could pull a stylesheet and a font from a host nothing
asked for. The app uses system fonts and ships its own assets. */
test('no policy allows an outside font or stylesheet host', () => {
const all = [...dashboard.matchAll(/add_header Content-Security-Policy "([^"]+)"/g)].map(m => m[1]);
all.push(policy);
for (const p of all) {
const directives = Object.fromEntries(
p
.split(';')
.map(d => d.trim())
.filter(Boolean)
.map(d => {
const [name, ...values] = d.split(/\s+/);
return [name, values];
}),
);
for (const name of ['font-src', 'style-src']) {
const values = directives[name] || [];
const hosts = values.filter(v => v.startsWith('http') || v.includes('.'));
assert.deepEqual(hosts, [], `${name} allows an outside host: ${hosts.join(' ')}`);
}
}
});

/* ── P16-5 and P16-6: version disclosure and compression ──────────────────── */

test('the nginx version is not advertised', () => {
Expand Down
4 changes: 2 additions & 2 deletions nginx/csp-default.conf
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
# this file.
#
# Two locations set their own policy inline instead:
# /admin no Google Fonts, no frame-src
# /admin no frame-src
# /widgets/ allows 'unsafe-inline' scripts
#
# frame-ancestors says who may frame us, frame-src what we may frame.
Expand All @@ -12,4 +12,4 @@
#
# Keep this to the one header. Anything that differs per call site belongs in
# the location.
add_header Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; img-src 'self' data: https://images.unsplash.com https://cdn.jsdelivr.net; frame-src 'self'; frame-ancestors 'self'; connect-src 'self' https://api.unsplash.com https://cdn.jsdelivr.net; font-src 'self' https://fonts.gstatic.com;" always;
add_header Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https://images.unsplash.com https://cdn.jsdelivr.net; frame-src 'self'; frame-ancestors 'self'; connect-src 'self' https://api.unsplash.com https://cdn.jsdelivr.net; font-src 'self';" always;
2 changes: 1 addition & 1 deletion nginx/dashboard.conf
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ server {
include /etc/nginx/http.d/security-headers.conf;
add_header Cache-Control "no-cache, no-store, must-revalidate";
add_header X-Frame-Options "" always;
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; img-src 'self' data: https://cdn.jsdelivr.net; connect-src 'self'; font-src 'self' https://fonts.gstatic.com https://fonts.googleapis.com; frame-ancestors 'self';" always;
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https://cdn.jsdelivr.net; connect-src 'self'; font-src 'self'; frame-ancestors 'self';" always;
}

# ── API proxy ──
Expand Down
Loading