Skip to content

Commit b872635

Browse files
authoredApr 18, 2024··
fix(cspNonce): don't overwrite existing nonce values (#16415)
1 parent 6cccef7 commit b872635

File tree

3 files changed

+38
-9
lines changed

3 files changed

+38
-9
lines changed
 

‎packages/vite/src/node/plugins/html.ts

+14-9
Original file line numberDiff line numberDiff line change
@@ -1180,24 +1180,29 @@ export function injectNonceAttributeTagHook(
11801180
return
11811181
}
11821182

1183+
const { nodeName, attrs, sourceCodeLocation } = node
1184+
11831185
if (
1184-
node.nodeName === 'script' ||
1185-
(node.nodeName === 'link' &&
1186-
node.attrs.some(
1186+
nodeName === 'script' ||
1187+
(nodeName === 'link' &&
1188+
attrs.some(
11871189
(attr) =>
11881190
attr.name === 'rel' &&
11891191
parseRelAttr(attr.value).some((a) => processRelType.has(a)),
11901192
))
11911193
) {
1194+
// If we already have a nonce attribute, we don't need to add another one
1195+
if (attrs.some(({ name }) => name === 'nonce')) {
1196+
return
1197+
}
1198+
1199+
const startTagEndOffset = sourceCodeLocation!.startTag!.endOffset
1200+
11921201
// if the closing of the start tag includes a `/`, the offset should be 2 so the nonce
11931202
// is appended prior to the `/`
1194-
const appendOffset =
1195-
html[node.sourceCodeLocation!.startTag!.endOffset - 2] === '/' ? 2 : 1
1203+
const appendOffset = html[startTagEndOffset - 2] === '/' ? 2 : 1
11961204

1197-
s.appendRight(
1198-
node.sourceCodeLocation!.startTag!.endOffset - appendOffset,
1199-
` nonce="${nonce}"`,
1200-
)
1205+
s.appendRight(startTagEndOffset - appendOffset, ` nonce="${nonce}"`)
12011206
}
12021207
})
12031208

‎playground/csp/__tests__/csp.spec.ts

+14
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,20 @@ test('dynamic js', async () => {
2727
)
2828
})
2929

30+
test('inline js', async () => {
31+
await expectWithRetry(() => page.textContent('.inline-js')).toBe(
32+
'inline-js: ok',
33+
)
34+
})
35+
36+
test('nonce attributes are not repeated', async () => {
37+
const htmlSource = await page.content()
38+
expect(htmlSource).not.toContain(/nonce=""[^>]*nonce=""/)
39+
await expectWithRetry(() => page.textContent('.double-nonce-js')).toBe(
40+
'double-nonce-js: ok',
41+
)
42+
})
43+
3044
test('meta[property=csp-nonce] is injected', async () => {
3145
const meta = await page.$('meta[property=csp-nonce]')
3246
expect(await (await meta.getProperty('nonce')).jsonValue()).not.toBe('')

‎playground/csp/index.html

+10
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,13 @@
1111
<p class="dynamic">dynamic</p>
1212
<p class="js">js: error</p>
1313
<p class="dynamic-js">dynamic-js: error</p>
14+
<p class="inline-js">inline-js: error</p>
15+
<p class="double-nonce-js">double-nonce-js: error</p>
16+
<script>
17+
document.querySelector('.inline-js').textContent = 'inline-js: ok'
18+
</script>
19+
<script nonce="#$NONCE$#">
20+
// this test case is to ensure that the nonce isn't being
21+
// double-applied if an existing attribute is present.
22+
document.querySelector('.double-nonce-js').textContent = 'double-nonce-js: ok'
23+
</script>

0 commit comments

Comments
 (0)
Please sign in to comment.