@adobe/helix-html-pipeline 6.23.0 → 6.24.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +7 -0
- package/package.json +1 -1
- package/src/steps/csp.js +42 -15
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
# [6.24.0](https://github.com/adobe/helix-html-pipeline/compare/v6.23.0...v6.24.0) (2025-03-27)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Features
|
|
5
|
+
|
|
6
|
+
* Enable CSP with Nonce in the Report Only Header ([#840](https://github.com/adobe/helix-html-pipeline/issues/840)) ([33ab7e1](https://github.com/adobe/helix-html-pipeline/commit/33ab7e17ca4f63bd0ddf550200a8f0e982451317))
|
|
7
|
+
|
|
1
8
|
# [6.23.0](https://github.com/adobe/helix-html-pipeline/compare/v6.22.0...v6.23.0) (2025-03-20)
|
|
2
9
|
|
|
3
10
|
|
package/package.json
CHANGED
package/src/steps/csp.js
CHANGED
|
@@ -40,17 +40,22 @@ function parseCSP(csp) {
|
|
|
40
40
|
/**
|
|
41
41
|
* Computes where nonces should be applied
|
|
42
42
|
* @param {string | null | undefined} metaCSPText The actual CSP value from the meta tag
|
|
43
|
-
* @param {string | null | undefined}
|
|
43
|
+
* @param {string | null | undefined} headerCSPText The actual CSP value from the header
|
|
44
|
+
* @param {string | null | undefined} headerCSPROText The actual CSP value from report-only header
|
|
44
45
|
* @returns {scriptNonce: boolean, styleNonce: boolean}
|
|
45
46
|
*/
|
|
46
|
-
function shouldApplyNonce(metaCSPText,
|
|
47
|
+
function shouldApplyNonce(metaCSPText, headerCSPText, headerCSPROText) {
|
|
47
48
|
const metaBased = parseCSP(metaCSPText);
|
|
48
|
-
const
|
|
49
|
+
const headerBased = parseCSP(headerCSPText);
|
|
50
|
+
const headerROBased = parseCSP(headerCSPROText);
|
|
51
|
+
|
|
49
52
|
return {
|
|
50
53
|
scriptNonce: metaBased['script-src']?.includes(NONCE_AEM)
|
|
51
|
-
||
|
|
54
|
+
|| headerBased['script-src']?.includes(NONCE_AEM)
|
|
55
|
+
|| headerROBased['script-src']?.includes(NONCE_AEM),
|
|
52
56
|
styleNonce: metaBased['style-src']?.includes(NONCE_AEM)
|
|
53
|
-
||
|
|
57
|
+
|| headerBased['style-src']?.includes(NONCE_AEM)
|
|
58
|
+
|| headerROBased['style-src']?.includes(NONCE_AEM),
|
|
54
59
|
};
|
|
55
60
|
}
|
|
56
61
|
|
|
@@ -73,23 +78,36 @@ export function getHeaderCSP(res) {
|
|
|
73
78
|
return res.headers?.get('content-security-policy');
|
|
74
79
|
}
|
|
75
80
|
|
|
81
|
+
export function getHeaderCSPRO(res) {
|
|
82
|
+
return res.headers?.get('content-security-policy-report-only');
|
|
83
|
+
}
|
|
84
|
+
|
|
76
85
|
/**
|
|
77
86
|
* Apply CSP with nonces on an AST
|
|
78
87
|
* @param {PipelineResponse} res
|
|
79
88
|
* @param {Object} tree
|
|
80
89
|
* @param {Object} metaCSP
|
|
81
|
-
* @param {string}
|
|
90
|
+
* @param {string} headerCSP
|
|
91
|
+
* @param {string} headerCSPRO
|
|
82
92
|
*/
|
|
83
|
-
function createAndApplyNonceOnAST(res, tree, metaCSP,
|
|
93
|
+
function createAndApplyNonceOnAST(res, tree, metaCSP, headerCSP, headerCSPRO) {
|
|
84
94
|
const nonce = createNonce();
|
|
85
|
-
const { scriptNonce, styleNonce } = shouldApplyNonce(
|
|
95
|
+
const { scriptNonce, styleNonce } = shouldApplyNonce(
|
|
96
|
+
metaCSP?.properties.content,
|
|
97
|
+
headerCSP,
|
|
98
|
+
headerCSPRO,
|
|
99
|
+
);
|
|
86
100
|
|
|
87
101
|
if (metaCSP) {
|
|
88
102
|
metaCSP.properties.content = metaCSP.properties.content.replaceAll(NONCE_AEM, `'nonce-${nonce}'`);
|
|
89
103
|
}
|
|
90
104
|
|
|
91
|
-
if (
|
|
92
|
-
res.headers.set('content-security-policy',
|
|
105
|
+
if (headerCSP) {
|
|
106
|
+
res.headers.set('content-security-policy', headerCSP.replaceAll(NONCE_AEM, `'nonce-${nonce}'`));
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
if (headerCSPRO) {
|
|
110
|
+
res.headers.set('content-security-policy-report-only', headerCSPRO.replaceAll(NONCE_AEM, `'nonce-${nonce}'`));
|
|
93
111
|
}
|
|
94
112
|
|
|
95
113
|
visit(tree, (node) => {
|
|
@@ -130,15 +148,18 @@ export function getMetaCSP(tree) {
|
|
|
130
148
|
export function contentSecurityPolicyOnAST(res, tree) {
|
|
131
149
|
const metaCSP = getMetaCSP(tree);
|
|
132
150
|
const headersCSP = getHeaderCSP(res);
|
|
133
|
-
|
|
134
|
-
if (!metaCSP && !headersCSP) {
|
|
151
|
+
const headersCSPRO = getHeaderCSPRO(res);
|
|
152
|
+
if (!metaCSP && !headersCSP && !headersCSPRO) {
|
|
135
153
|
// No CSP defined
|
|
136
154
|
return;
|
|
137
155
|
}
|
|
138
156
|
|
|
139
157
|
// CSP with nonce
|
|
140
|
-
if (metaCSP?.properties.content.includes(NONCE_AEM)
|
|
141
|
-
|
|
158
|
+
if (metaCSP?.properties.content.includes(NONCE_AEM)
|
|
159
|
+
|| headersCSP?.includes(NONCE_AEM)
|
|
160
|
+
|| headersCSPRO?.includes(NONCE_AEM)
|
|
161
|
+
) {
|
|
162
|
+
createAndApplyNonceOnAST(res, tree, metaCSP, headersCSP, headersCSPRO);
|
|
142
163
|
}
|
|
143
164
|
|
|
144
165
|
if (metaCSP?.properties['move-as-header'] === 'true') {
|
|
@@ -159,15 +180,17 @@ export function contentSecurityPolicyOnCode(state, res) {
|
|
|
159
180
|
}
|
|
160
181
|
|
|
161
182
|
const cspHeader = getHeaderCSP(res);
|
|
183
|
+
const cspROHeader = getHeaderCSPRO(res);
|
|
162
184
|
if (!(
|
|
163
185
|
cspHeader?.includes(NONCE_AEM)
|
|
186
|
+
|| cspROHeader?.includes(NONCE_AEM)
|
|
164
187
|
|| (checkResponseBodyForMetaBasedCSP(res) && checkResponseBodyForAEMNonce(res))
|
|
165
188
|
)) {
|
|
166
189
|
return;
|
|
167
190
|
}
|
|
168
191
|
|
|
169
192
|
const nonce = createNonce();
|
|
170
|
-
let { scriptNonce, styleNonce } = shouldApplyNonce(null, cspHeader);
|
|
193
|
+
let { scriptNonce, styleNonce } = shouldApplyNonce(null, cspHeader, cspROHeader);
|
|
171
194
|
|
|
172
195
|
const html = res.body;
|
|
173
196
|
const chunks = [];
|
|
@@ -232,4 +255,8 @@ export function contentSecurityPolicyOnCode(state, res) {
|
|
|
232
255
|
if (cspHeader) {
|
|
233
256
|
res.headers.set('content-security-policy', cspHeader.replaceAll(NONCE_AEM, `'nonce-${nonce}'`));
|
|
234
257
|
}
|
|
258
|
+
|
|
259
|
+
if (cspROHeader) {
|
|
260
|
+
res.headers.set('content-security-policy-report-only', cspROHeader.replaceAll(NONCE_AEM, `'nonce-${nonce}'`));
|
|
261
|
+
}
|
|
235
262
|
}
|