Skip to content

fix(csp): allow gethinode.com to embed the demo site#2008

Merged
markdumay merged 3 commits into
mainfrom
fix/showcase-preview-csp
Jul 13, 2026
Merged

fix(csp): allow gethinode.com to embed the demo site#2008
markdumay merged 3 commits into
mainfrom
fix/showcase-preview-csp

Conversation

@markdumay

Copy link
Copy Markdown
Collaborator

Problem

The live previews on gethinode.com/showcases are broken for
the Hinode (classic), Agency and gethinode.com showcases. Only the AdaptiQ preview
renders; the others fall back to a static screenshot.

The previews are iframes, so both sides of the embed must permit it. This repo owns one of those
sides: demo.gethinode.com (the exampleSite deployment) currently responds with

Content-Security-Policy: ... frame-ancestors 'self'; ...

so the browser refuses to let gethinode.com frame it. AdaptiQ works precisely because it allows
gethinode.com *.gethinode.com in its own frame-ancestors.

config/development/params.toml already lists gethinode.com (commented "Production embedding"),
so the intent was there — it was simply never mirrored to production.

Change

  • exampleSite/config/production/params.toml (new) — override frame-ancestors to
    'self' gethinode.com for the demo deployment only. The theme default in
    config/production/params.toml stays 'self', so downstream Hinode sites are unaffected
    and do not silently inherit a policy allowing gethinode.com to frame them.
  • package.json — generate the production headers from the exampleSite instead of the main
    site. Netlify deploys exampleSite/public, so the exampleSite config is the correct source.
  • netlify.toml — regenerated.

Regenerating from the exampleSite also restores entries the main-site-derived file was missing,
and which the deployed demo actually serves: the cookieyes CSP entries (*.cookieyes.com,
cdn-cookieyes.com) and the localized 404 redirects.

Verification

Regenerated netlify.toml now emits:

frame-ancestors 'self' gethinode.com;

[build], publish and the plugin config are unchanged.

Not sufficient on its own

This restores only the demo's side of the embed. gethinode.com must also add the demo to its own
CSP frame-src — its current policy blocks the iframe before the request is even made (verified
via a live securitypolicyviolation event with effectiveDirective: "frame-src"). Companion PRs:

  • gethinode/gethinode.com — add 'self' and *.gethinode.com to frame-src
  • gethinode/theme-agency — same frame-ancestors fix for the Agency showcase

🤖 Generated with Claude Code

The showcase pages on gethinode.com embed demo.gethinode.com in a live preview iframe, but the
demo refused to render: it inherited the theme default `frame-ancestors 'self'`, so the browser
blocked the cross-origin frame.

- Add exampleSite/config/production/params.toml, overriding frame-ancestors for the demo
  deployment only. The theme default stays 'self', so downstream Hinode sites are unaffected.
- Generate the production headers from the exampleSite rather than the main site. Netlify
  deploys exampleSite/public, so its config is the correct source; the previous netlify.toml
  was missing the exampleSite CSP entries (cookieyes) and the localized 404 redirects.
- Regenerate netlify.toml.

Note that this alone does not restore the preview: gethinode.com must also list the demo in its
own CSP frame-src directive.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@netlify

netlify Bot commented Jul 13, 2026

Copy link
Copy Markdown

Deploy Preview for gethinode-demo ready!

Name Link
🔨 Latest commit c7df389
🔍 Latest deploy log https://app.netlify.com/projects/gethinode-demo/deploys/6a54cbd2a8a92a00086b795d
😎 Deploy Preview https://deploy-preview-2008--gethinode-demo.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

The block reference from mod-docs, published at demo.gethinode.com/docs/blocks/preview, previews
a Wikipedia article and an OpenStreetMap map. Neither host was listed in the demo's own CSP
frame-src directive, so the browser blocked both iframes and the page showed two empty boxes.

- Add the two hosts to frame-src, under the docs module so they union with the theme's own
  sources rather than replacing them.
- Upgrade mod-blocks to v2.0.3, which fixes the preview component's embedding detection. The same
  page previews google.com on purpose, to demonstrate a site that refuses to be framed; it sends
  X-Frame-Options: SAMEORIGIN, and before v2.0.3 that was rendered as a live iframe, so the
  "blocked" example was itself an empty box rather than the intended screenshot fallback.
- Regenerate netlify.toml.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@markdumay

Copy link
Copy Markdown
Collaborator Author

Added: the demo renders previews too

I originally scoped this PR to frame-ancestors only, on the assumption that the exampleSite does
not use the preview component. That was wrong — the block reference from mod-docs is published
at demo.gethinode.com/docs/blocks/preview,
and it renders three previews. Two of them are broken on the live demo today.

Preview Sends Needs Status before
en.wikipedia.org no framing restriction frame-src entry ❌ empty box — not in frame-src
www.openstreetmap.org no framing restriction frame-src entry ❌ empty box — not in frame-src
google.com X-Frame-Options: SAMEORIGIN nothing — cannot be framed ❌ empty box — should be a screenshot

The google.com preview is deliberate: the docs use it to demonstrate a site that refuses to be
framed. It was rendering as an empty box rather than the intended screenshot fallback, so the
"blocked preview" example was itself broken.

Changes

  • exampleSite/config/production/params.toml — add *.wikipedia.org and
    www.openstreetmap.org to frame-src. They are declared under [modules.docs.csp] rather than
    [modules.hinode.csp], because a site-level array under the same module key replaces the
    theme's array; a separate module key unions with it instead, so the existing youtube / cloudinary
    / vimeo sources are preserved.
  • exampleSite/go.mod — upgrade mod-blocks to v2.0.3, which fixes the preview
    component's embedding detection (fix(preview): honour frame-ancestors when detecting embedding restrictions mod-blocks#151). This is what turns the google.com
    example back into a screenshot.
  • netlify.toml — regenerated.

Verification

Built the exampleSite and cross-checked every rendered preview against the regenerated frame-src:

HOST                    RENDERED   frame-src ALLOWS         VERDICT
en.wikipedia.org        live       *.wikipedia.org          OK - renders
www.openstreetmap.org   live       www.openstreetmap.org    OK - renders
google.com              fallback   -                        OK - screenshot fallback

frame-ancestors 'self' gethinode.com is unchanged, so the showcase embedding fix still holds.

Follow-up worth considering

These two sources arguably belong in mod-docs itself — the module that embeds Wikipedia and
OpenStreetMap should declare its own CSP needs, the way [modules.vimeo.csp] declares
player.vimeo.com. Every site that ships the block reference needs the same two entries, and
gethinode.com is currently hand-maintaining them (gethinode/gethinode.com#154). Happy to move them
upstream if you prefer; I kept them site-level here to avoid a mod-docs release in the critical
path.

mod-docs v1.15.0 declares the frame-src sources its preview block reference needs, so the demo no
longer has to hand-maintain them. Drop the site-level entries and upgrade the modules:

- mod-docs v1.14.1 => v1.15.0, which declares *.wikipedia.org and www.openstreetmap.org.
- mod-blocks v2.0.3 => v2.1.0, whose preview component now also checks a URL against the frame-src
  the site itself publishes, and falls back to the screenshot when it is not covered.

The generated netlify.toml is unchanged: the two sources are now inherited rather than declared
locally, so the published policy is identical.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@markdumay

Copy link
Copy Markdown
Collaborator Author

Updated: inherit the CSP sources from mod-docs

Both follow-ups are released, so the site-level entries I added earlier are gone:

The generated netlify.toml is byte-for-byte the same as before the change, which is the point:
the two sources are now inherited rather than declared locally, so the published policy is
identical.

Verified

Built the exampleSite with the released modules:

/docs/blocks/preview
  en.wikipedia.org        live
  www.openstreetmap.org   live
  google.com              fallback   (refuses framing, as the docs intend)

No preview warnings, and frame-ancestors 'self' gethinode.com is unchanged, so the showcase
embedding fix still holds.

Net effect of this PR

  • frame-ancestors now allows gethinode.com, so the Hinode (classic) showcase can render.
  • The demo's own block reference renders its previews instead of three empty boxes.
  • Production headers are generated from the exampleSite, which is what Netlify actually deploys.

@markdumay markdumay merged commit 771fa09 into main Jul 13, 2026
16 checks passed
@markdumay

Copy link
Copy Markdown
Collaborator Author

🎉 This PR is included in version 3.0.2 🎉

The release is available on GitHub release

Your semantic-release bot 📦🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant