Comparing version 4.5.0 to 4.6.0
# Changelog | ||
## 4.6.0 - 2021-05-01 | ||
### Added | ||
- `helmet.contentSecurityPolicy`: the `useDefaults` option, defaulting to `false`, lets you selectively override defaults more easily | ||
- Explicitly define TypeScript types in `package.json`. See [#303](https://github.com/helmetjs/helmet/pull/303) | ||
## 4.5.0 - 2021-04-17 | ||
@@ -4,0 +11,0 @@ |
@@ -8,3 +8,4 @@ /// <reference types="node" /> | ||
export interface ContentSecurityPolicyOptions { | ||
directives?: Record<string, Iterable<ContentSecurityPolicyDirectiveValue> | typeof dangerouslyDisableDefaultSrc> | ||
useDefaults?: boolean | ||
directives?: Record<string, null | Iterable<ContentSecurityPolicyDirectiveValue> | typeof dangerouslyDisableDefaultSrc> | ||
reportOnly?: boolean | ||
@@ -11,0 +12,0 @@ } |
@@ -25,5 +25,7 @@ "use strict" | ||
function normalizeDirectives(options) { | ||
const { directives: rawDirectives = getDefaultDirectives() } = options | ||
const result = [] | ||
const defaultDirectives = getDefaultDirectives() | ||
const { useDefaults = false, directives: rawDirectives = defaultDirectives } = options | ||
const result = new Map() | ||
const directiveNamesSeen = new Set() | ||
const directivesExplicitlyDisabled = new Set() | ||
for (const rawDirectiveName in rawDirectives) { | ||
@@ -43,3 +45,9 @@ if (!has(rawDirectives, rawDirectiveName)) { | ||
let directiveValue | ||
if (typeof rawDirectiveValue === "string") { | ||
if (rawDirectiveValue === null) { | ||
if (directiveName === "default-src") { | ||
throw new Error("Content-Security-Policy needs a default-src but it was set to `null`. If you really want to disable it, set it to `contentSecurityPolicy.dangerouslyDisableDefaultSrc`.") | ||
} | ||
directivesExplicitlyDisabled.add(directiveName) | ||
continue | ||
} else if (typeof rawDirectiveValue === "string") { | ||
directiveValue = [rawDirectiveValue] | ||
@@ -50,2 +58,3 @@ } else if (!rawDirectiveValue) { | ||
if (directiveName === "default-src") { | ||
directivesExplicitlyDisabled.add("default-src") | ||
continue | ||
@@ -63,9 +72,16 @@ } else { | ||
} | ||
result.push({ directiveName, directiveValue }) | ||
result.set(directiveName, directiveValue) | ||
} | ||
if (!result.length) { | ||
if (useDefaults) { | ||
Object.entries(defaultDirectives).forEach(([defaultDirectiveName, defaultDirectiveValue]) => { | ||
if (!result.has(defaultDirectiveName) && !directivesExplicitlyDisabled.has(defaultDirectiveName)) { | ||
result.set(defaultDirectiveName, defaultDirectiveValue) | ||
} | ||
}) | ||
} | ||
if (!result.size) { | ||
throw new Error("Content-Security-Policy has no directives. Either set some or disable the header") | ||
} | ||
if (!directiveNamesSeen.has("default-src")) { | ||
throw new Error("Content-Security-Policy needs a default-src but none was provided") | ||
if (!result.has("default-src") && !directivesExplicitlyDisabled.has("default-src")) { | ||
throw new Error("Content-Security-Policy needs a default-src but none was provided. If you really want to disable it, set it to `contentSecurityPolicy.dangerouslyDisableDefaultSrc`.") | ||
} | ||
@@ -75,4 +91,5 @@ return result | ||
function getHeaderValue(req, res, normalizedDirectives) { | ||
let err | ||
const result = [] | ||
for (const { directiveName, directiveValue: rawDirectiveValue } of normalizedDirectives) { | ||
normalizedDirectives.forEach((rawDirectiveValue, directiveName) => { | ||
let directiveValue = "" | ||
@@ -85,8 +102,8 @@ for (const element of rawDirectiveValue) { | ||
} else if (isDirectiveValueInvalid(directiveValue)) { | ||
return new Error(`Content-Security-Policy received an invalid directive value for ${JSON.stringify(directiveName)}`) | ||
err = new Error(`Content-Security-Policy received an invalid directive value for ${JSON.stringify(directiveName)}`) | ||
} else { | ||
result.push(`${directiveName}${directiveValue}`) | ||
} | ||
} | ||
return result.join(";") | ||
}) | ||
return err ? err : result.join(";") | ||
} | ||
@@ -93,0 +110,0 @@ const contentSecurityPolicy = function contentSecurityPolicy(options = {}) { |
@@ -9,3 +9,3 @@ { | ||
"description": "help secure Express/Connect apps with various HTTP headers", | ||
"version": "4.5.0", | ||
"version": "4.6.0", | ||
"keywords": [ | ||
@@ -55,3 +55,4 @@ "express", | ||
"license": "MIT", | ||
"types": "dist/index.d.ts", | ||
"main": "dist/index" | ||
} |
# Helmet | ||
[![npm version](https://badge.fury.io/js/helmet.svg)](http://badge.fury.io/js/helmet) | ||
[![npm version](https://badge.fury.io/js/helmet.svg)](https://badge.fury.io/js/helmet) | ||
[![npm dependency status](https://david-dm.org/helmetjs/helmet.svg)](https://david-dm.org/helmetjs/helmet) | ||
@@ -139,26 +139,16 @@ [![FOSSA Status](https://app.fossa.io/api/projects/git%2Bhttps%3A%2F%2Fgithub.com%2Fhelmetjs%2Fhelmet.svg?type=shield)](https://app.fossa.io/projects/git%2Bhttps%3A%2F%2Fgithub.com%2Fhelmetjs%2Fhelmet?ref=badge_shield) | ||
You can fetch this default with `helmet.contentSecurityPolicy.getDefaultDirectives()`. | ||
You can use this default with the `options.useDefaults` option. `options.useDefaults` is `false` by default, but will be `true` in the next major version of Helmet. | ||
You can also get the default directives object with `helmet.contentSecurityPolicy.getDefaultDirectives()`. | ||
Examples: | ||
```js | ||
// Sets "Content-Security-Policy: default-src 'self';script-src 'self' example.com;object-src 'none';upgrade-insecure-requests" | ||
// Sets all of the defaults, but overrides `script-src` and disables the default `style-src` | ||
app.use( | ||
helmet.contentSecurityPolicy({ | ||
useDefaults: true, | ||
directives: { | ||
defaultSrc: ["'self'"], | ||
scriptSrc: ["'self'", "example.com"], | ||
objectSrc: ["'none'"], | ||
upgradeInsecureRequests: [], | ||
}, | ||
}) | ||
); | ||
// Sets "Content-Security-Policy: default-src 'self';script-src 'self' example.com;object-src 'none'" | ||
app.use( | ||
helmet.contentSecurityPolicy({ | ||
directives: { | ||
"default-src": ["'self'"], | ||
"script-src": ["'self'", "example.com"], | ||
"object-src": ["'none'"], | ||
"style-src": null, | ||
}, | ||
@@ -168,8 +158,11 @@ }) | ||
// Sets all of the defaults, but overrides script-src | ||
// Sets "Content-Security-Policy: default-src 'self';script-src 'self' example.com;object-src 'none';upgrade-insecure-requests" | ||
app.use( | ||
helmet.contentSecurityPolicy({ | ||
useDefaults: false, | ||
directives: { | ||
...helmet.contentSecurityPolicy.getDefaultDirectives(), | ||
"script-src": ["'self'", "example.com"], | ||
defaultSrc: ["'self'"], | ||
scriptSrc: ["'self'", "example.com"], | ||
objectSrc: ["'none'"], | ||
upgradeInsecureRequests: [], | ||
}, | ||
@@ -182,2 +175,3 @@ }) | ||
helmet.contentSecurityPolicy({ | ||
useDefaults: true, | ||
directives: { | ||
@@ -190,3 +184,3 @@ /* ... */ | ||
// Sets "Content-Security-Policy: default-src 'self';script-src 'self' 'nonce-e33ccde670f149c1789b1e1e113b0916'" | ||
// Sets the `script-src` directive to "'self' 'nonce-e33ccde670f149c1789b1e1e113b0916'" (or similar) | ||
app.use((req, res, next) => { | ||
@@ -198,4 +192,4 @@ res.locals.cspNonce = crypto.randomBytes(16).toString("hex"); | ||
helmet.contentSecurityPolicy({ | ||
useDefaults: true, | ||
directives: { | ||
defaultSrc: ["'self'"], | ||
scriptSrc: ["'self'", (req, res) => `'nonce-${res.locals.cspNonce}'`], | ||
@@ -209,2 +203,3 @@ }, | ||
helmet.contentSecurityPolicy({ | ||
useDefaults: false, | ||
directives: { | ||
@@ -318,3 +313,3 @@ "default-src": helmet.contentSecurityPolicy.dangerouslyDisableDefaultSrc, | ||
You can't install this module separately. | ||
You can install this module separately as `cross-origin-resource-policy`. | ||
@@ -321,0 +316,0 @@ </details> |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
73751
705
603