Skip to content

feat(security): add route-scoped frame-defense guard against clickjacking#1988

Draft
HARISHWAR-T wants to merge 1 commit into
runbox:masterfrom
HARISHWAR-T:frame-defense-hardening
Draft

feat(security): add route-scoped frame-defense guard against clickjacking#1988
HARISHWAR-T wants to merge 1 commit into
runbox:masterfrom
HARISHWAR-T:frame-defense-hardening

Conversation

@HARISHWAR-T

Copy link
Copy Markdown

What

Defence-in-depth hardening against clickjacking / UI-redress. The app currently
ships no client-side frame defence and index.html carries no CSP /
X-Frame-Options. This PR adds:

  • FrameBustingService.isFramed() — frame detection (injectable window for tests).
  • FrameDefenseGuard (CanActivate) wired onto the authenticated /calendar
    and /contacts routes: when window.self !== window.top it refuses to
    activate the route, so those flows are not rendered inside a frame.
  • A conservative <meta> CSP in index.html (object-src 'none'; base-uri 'self'),
    both directives honoured in meta and chosen to not constrain Angular's
    script/style/worker/connect needs.
  • SECURITY.md recommending the authoritative server headers
    (Content-Security-Policy: frame-ancestors 'none' + X-Frame-Options: DENY).

Why route-scoped, not global

Every framing usage in the repo is the app acting as a parent (sandboxed
mail-content iframe, print frame, logout iframe) — none bootstrap the app as a
framed child. But a legitimate external embedder can't be ruled out, so a
global break-out / display:none could break real usage. Scoping the refusal
to specific sensitive routes closes the vector while leaving any whole-app
embedding intact. frame-ancestors is deliberately omitted from <meta>
because browsers ignore it there — it must be a response header.

Tests

  • frame-busting.service.spec.ts — framed / not-framed / cross-origin-throws.
  • frame-defense.guard.spec.tscanActivate() is true when top === self,
    false when top !== self.

Run: npm run ci-tests.

Notes

  • Changelog (node src/build/build-changelog.js) not regenerated yet; happy to
    add it if preferred.
  • Opened as a draft pending a local npm run ci-tests run.

AI disclosure

Drafted with assistance from Claude (Anthropic); all code reviewed and verified
by the author, per CONTRIBUTING's AI-use policy.

…king

Add FrameBustingService (frame detection) and FrameDefenseGuard (CanActivate),
wired onto the authenticated /calendar and /contacts routes so the app refuses
to render those flows inside a frame. Add a conservative meta CSP
(object-src 'none'; base-uri 'self') and a SECURITY.md documenting the
authoritative frame-ancestors / X-Frame-Options response headers to set at the
edge. Scoped per-route rather than globally so legitimate full-app embedding is
not broken.

Developed with assistance from Claude (Anthropic), per the AI-use policy in
CONTRIBUTING.md; all code reviewed by the author.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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.

1 participant