Skip to content

Commit

Permalink
Add a content security policy implementation and update templates (#1235
Browse files Browse the repository at this point in the history
)
  • Loading branch information
blittle authored Aug 30, 2023
1 parent ed50823 commit 1f8acd7
Show file tree
Hide file tree
Showing 29 changed files with 911 additions and 36 deletions.
7 changes: 7 additions & 0 deletions .changeset/two-carrots-relax.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
'@shopify/cli-hydrogen': patch
'@shopify/create-hydrogen': patch
'@shopify/hydrogen': patch
---

Add functionality for creating a Content Security Policy. See the [guide on Content Security Policies](https://shopify.dev/docs/custom-storefronts/hydrogen/content-security-policy) for more details.
9 changes: 8 additions & 1 deletion examples/customer-api/app/entry.server.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,22 @@ import type {EntryContext} from '@shopify/remix-oxygen';
import {RemixServer} from '@remix-run/react';
import isbot from 'isbot';
import {renderToReadableStream} from 'react-dom/server';
import {createContentSecurityPolicy} from '@shopify/hydrogen';

export default async function handleRequest(
request: Request,
responseStatusCode: number,
responseHeaders: Headers,
remixContext: EntryContext,
) {
const {nonce, header, NonceProvider} = createContentSecurityPolicy();

const body = await renderToReadableStream(
<RemixServer context={remixContext} url={request.url} />,
<NonceProvider>
<RemixServer context={remixContext} url={request.url} />
</NonceProvider>,
{
nonce,
signal: request.signal,
onError(error) {
// eslint-disable-next-line no-console
Expand All @@ -26,6 +32,7 @@ export default async function handleRequest(
}

responseHeaders.set('Content-Type', 'text/html');
responseHeaders.set('Content-Security-Policy', header);
return new Response(body, {
headers: responseHeaders,
status: responseStatusCode,
Expand Down
8 changes: 5 additions & 3 deletions examples/customer-api/app/root.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
import type {Shop} from '@shopify/hydrogen/storefront-api-types';
import styles from './styles/app.css';
import favicon from '../public/favicon.svg';
import {useNonce} from '@shopify/hydrogen';

export const links: LinksFunction = () => {
return [
Expand All @@ -34,6 +35,7 @@ export async function loader({context}: LoaderArgs) {

export default function App() {
const data = useLoaderData<typeof loader>();
const nonce = useNonce();

const {name} = data.layout.shop;

Expand All @@ -51,9 +53,9 @@ export default function App() {
This is an example of Hydrogen using the Customer API
</p>
<Outlet />
<ScrollRestoration />
<Scripts />
<LiveReload />
<ScrollRestoration nonce={nonce} />
<Scripts nonce={nonce} />
<LiveReload nonce={nonce} />
</body>
</html>
);
Expand Down
33 changes: 23 additions & 10 deletions examples/express/app/entry.server.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {Response} from '@remix-run/node';
import {RemixServer} from '@remix-run/react';
import isbot from 'isbot';
import {renderToPipeableStream} from 'react-dom/server';
import {createContentSecurityPolicy} from '@shopify/hydrogen';

const ABORT_DELAY = 5_000;

Expand Down Expand Up @@ -43,17 +44,23 @@ function handleBotRequest(
remixContext: EntryContext,
) {
return new Promise((resolve, reject) => {
const {nonce, header, NonceProvider} = createContentSecurityPolicy();

const {pipe, abort} = renderToPipeableStream(
<RemixServer
context={remixContext}
url={request.url}
abortDelay={ABORT_DELAY}
/>,
<NonceProvider>
<RemixServer
context={remixContext}
url={request.url}
abortDelay={ABORT_DELAY}
/>
</NonceProvider>,
{
nonce,
onAllReady() {
const body = new PassThrough();

responseHeaders.set('Content-Type', 'text/html');
responseHeaders.set('Content-Security-Policy', header);

resolve(
new Response(body, {
Expand Down Expand Up @@ -84,18 +91,24 @@ function handleBrowserRequest(
responseHeaders: Headers,
remixContext: EntryContext,
) {
const {nonce, header, NonceProvider} = createContentSecurityPolicy();

return new Promise((resolve, reject) => {
const {pipe, abort} = renderToPipeableStream(
<RemixServer
context={remixContext}
url={request.url}
abortDelay={ABORT_DELAY}
/>,
<NonceProvider>
<RemixServer
context={remixContext}
url={request.url}
abortDelay={ABORT_DELAY}
/>
</NonceProvider>,
{
nonce,
onShellReady() {
const body = new PassThrough();

responseHeaders.set('Content-Type', 'text/html');
responseHeaders.set('Content-Security-Policy', header);

resolve(
new Response(body, {
Expand Down
8 changes: 5 additions & 3 deletions examples/express/app/root.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import type {Cart, Shop} from '@shopify/hydrogen/storefront-api-types';
import {Layout} from '~/components/Layout';
import styles from './styles/app.css';
import favicon from '../public/favicon.svg';
import {useNonce} from '@shopify/hydrogen';

export const links: LinksFunction = () => {
return [
Expand Down Expand Up @@ -64,6 +65,7 @@ export async function loader({context}: LoaderArgs) {

export default function App() {
const data = useLoaderData<typeof loader>();
const nonce = useNonce();

const {name, description} = data.layout.shop;

Expand All @@ -79,9 +81,9 @@ export default function App() {
<Layout description={description} title={name}>
<Outlet />
</Layout>
<ScrollRestoration />
<Scripts />
<LiveReload />
<ScrollRestoration nonce={nonce} />
<Scripts nonce={nonce} />
<LiveReload nonce={nonce} />
</body>
</html>
);
Expand Down
15 changes: 15 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit 1f8acd7

Please sign in to comment.