Skip to content

Commit

Permalink
✨ add applyDefault option to createContentSecurityPolicy
Browse files Browse the repository at this point in the history
  • Loading branch information
michenly committed Dec 21, 2023
1 parent f0415c9 commit f6ab545
Show file tree
Hide file tree
Showing 2 changed files with 45 additions and 1 deletion.
18 changes: 18 additions & 0 deletions .changeset/heavy-coins-tickle.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
---
'@shopify/hydrogen': patch
---

✨ add applyDefault option to createContentSecurityPolicy which allow use to add policy in front of the existing rules instead of overriding them. The default value of applyDefault option is false which is the current behaviour.

Example usage:

```diff
const {nonce, header, NonceProvider} = createContentSecurityPolicy(
{connectSrc: 'wss://public-domain:*'},
+ {applyDefault: true},
);
```

Result of connect-src when `applyDefault=false` is "wss://public-domain:\*"

Result of connect-src when `applyDefault=true` is "wss://public-domain:\* 'self' 'https://cdn.shopify.com' 'https://shopify.com'"
28 changes: 27 additions & 1 deletion packages/hydrogen/src/csp/csp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,10 @@ type ContentSecurityPolicy = {
*/
export function createContentSecurityPolicy(
directives: Record<string, string[] | string | boolean> = {},
options: {applyDefault?: boolean} = {},
): ContentSecurityPolicy {
const nonce = generateNonce();
const header = createCSPHeader(nonce, directives);
const header = createCSPHeader(nonce, directives, options?.applyDefault);

const Provider = ({children}: {children: ReactNode}) => {
return createElement(NonceProvider, {value: nonce}, children);
Expand All @@ -44,6 +45,7 @@ export function createContentSecurityPolicy(
function createCSPHeader(
nonce: string,
directives: Record<string, string[] | string | boolean> = {},
applyDefault = false,
): string {
const nonceString = `'nonce-${nonce}'`;
const styleSrc = ["'self'", "'unsafe-inline'", 'https://cdn.shopify.com'];
Expand Down Expand Up @@ -78,6 +80,14 @@ function createCSPHeader(
}

const combinedDirectives = Object.assign({}, defaultDirectives, directives);
if (applyDefault) {
for (const key in defaultDirectives) {
combinedDirectives[key] = addCspDirective(
directives[key],
defaultDirectives[key],
);
}
}

// Make sure that at least script-src includes a nonce directive.
// If someone doesn't want a nonce in their CSP, they probably
Expand All @@ -98,3 +108,19 @@ function createCSPHeader(
directives: combinedDirectives,
});
}

function addCspDirective(
currentValue: string[] | string | boolean,
value: string[] | string | boolean,
): boolean | string[] {
const normalizedValue = typeof value === 'string' ? [value] : value;
const normalizedCurrentValue = Array.isArray(currentValue)
? currentValue
: [String(currentValue)];

const newValue = Array.isArray(normalizedValue)
? [...normalizedCurrentValue, ...normalizedValue]
: normalizedValue;

return newValue;
}

0 comments on commit f6ab545

Please sign in to comment.