Skip to content

fix(csp): require explicit allowlist for external-pad framing (#102) - #116

Merged
Jaggob merged 1 commit into
mainfrom
security/102-csp-frame-src
Jun 2, 2026
Merged

fix(csp): require explicit allowlist for external-pad framing (#102)#116
Jaggob merged 1 commit into
mainfrom
security/102-csp-frame-src

Conversation

@Jaggob

@Jaggob Jaggob commented Jun 2, 2026

Copy link
Copy Markdown
Collaborator

Problem

CSPListener handles every AddContentSecurityPolicyEvent. When external pads were enabled (allow_external_pads=yes) with an empty external_pad_allowlist, it emitted a blanket https: into frame-src/child-src — loosening framing on all Nextcloud pages, not just pad pages.

Fix

External pads are rendered as locally fetched read-only snapshots (no live iframe to the external host), so the blanket relaxation bought nothing and only widened the site-wide framing surface. An empty allowlist now contributes no frame domains; only concrete allowlisted hosts (plus the configured local Etherpad host) are ever added to frame-src/child-src.

The server-side snapshot fetch keeps its existing "empty allowlist = allow all public HTTPS hosts" behaviour (ExternalPadExportFetcher, still guarded by the public-IP / DNS-rebinding checks). Only the browser-facing CSP is tightened — so this fixes all existing installs immediately, with no admin re-save required.

Acceptance

  • No config path emits frame-src https: globally
  • External pads still load when a host is allowlisted (concrete-host framing unchanged)
  • Test covers the empty-allowlist case

Verification

  • phpunit: 399 tests green (CSP suite covers empty-allowlist + no-local-host cases asserting no https: blanket).
  • Live on NC 33 in the exact vulnerable config (allow_external_pads=yes, empty allowlist): the served login-page CSP is now
    frame-src https://pad.jaggob.uber.space 'self' nc: https://demo.eu.collaboraonline.com
    child-src https://pad.jaggob.uber.space
    
    — concrete host only, no blanket https:.

Closes #102.

CSPListener handles every AddContentSecurityPolicyEvent, so when external
pads were enabled with an empty allowlist it emitted a blanket `https:`
frame-src/child-src on *all* Nextcloud pages, not just pad pages.

External pads are rendered as locally fetched read-only snapshots (no live
iframe to the external host), so the blanket relaxation bought nothing and
only widened the framing surface site-wide. Drop it: an empty allowlist now
contributes no frame domains, and only concrete allowlisted hosts (plus the
configured local Etherpad host) are ever added to frame-src/child-src.

The server-side snapshot fetch keeps its existing 'empty allowlist = allow
all public HTTPS hosts' behaviour (ExternalPadExportFetcher, guarded by the
public-IP / DNS-rebinding checks); only the browser-facing CSP is tightened.

Closes #102.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Tightens CSP so that enabling external pads with an empty allowlist no longer emits a blanket https: into frame-src/child-src globally. Only concrete allowlisted hosts (plus the configured local Etherpad host) contribute frame domains; the server-side snapshot fetch behavior is unchanged.

Changes:

  • Remove the $domains['https:'] = true; fallback in CSPListener::getAllowedFrameDomains() when the external pad allowlist is empty.
  • Update existing test to assert no blanket https: is emitted, and add a new test verifying no policy is added when both local host and allowlist are empty.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.

File Description
lib/Listeners/CSPListener.php Stop adding blanket https: frame source for empty external pad allowlist.
tests/phpunit/unit/CSPListenerTest.php Renamed/updated regression test and added empty-config no-policy test.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@Jaggob
Jaggob merged commit e49c0c7 into main Jun 2, 2026
11 checks passed
@Jaggob
Jaggob deleted the security/102-csp-frame-src branch June 17, 2026 14:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

CSP: stop allowing blanket frame-src https: for external pads

2 participants