mirror of
https://github.com/angular/angular.git
synced 2026-09-14 13:54:52 +08:00
fix(core): harden TransferState restoration against DOM clobbering
Reject non-script elements when reading the SSR transfer state payload by id. This prevents attacker-controlled elements with a clobbered id from spoofing hydration state.
This commit is contained in:
committed by
Andrew Scott
parent
7896e74222
commit
6bde84fa8e
@@ -154,7 +154,7 @@ export function retrieveTransferredState(
|
||||
// Locate the script tag with the JSON data transferred from the server.
|
||||
// The id of the script tag is set to the Angular appId + 'state'.
|
||||
const script = doc.getElementById(appId + '-state');
|
||||
if (script?.textContent) {
|
||||
if (script?.tagName === 'SCRIPT' && script.textContent) {
|
||||
try {
|
||||
// Avoid using any here as it triggers lint errors in google3 (any is not allowed).
|
||||
// Decoding of `<` is done of the box by browsers and node.js, same behaviour as G3
|
||||
|
||||
@@ -13,7 +13,11 @@ import {DOCUMENT} from '../src/document';
|
||||
import {makeStateKey, TransferState} from '../src/transfer_state';
|
||||
|
||||
function removeScriptTag(doc: Document, id: string) {
|
||||
doc.getElementById(id)?.remove();
|
||||
let node = doc.getElementById(id);
|
||||
while (node) {
|
||||
node.remove();
|
||||
node = doc.getElementById(id);
|
||||
}
|
||||
}
|
||||
|
||||
function addScriptTag(doc: Document, appId: string, data: object | string) {
|
||||
@@ -57,6 +61,24 @@ describe('TransferState', () => {
|
||||
expect(transferState.get(TEST_KEY, 0)).toBe(10);
|
||||
});
|
||||
|
||||
it('ignores non-script elements that clobber the transfer state id', () => {
|
||||
const id = APP_ID + '-state';
|
||||
|
||||
const clobberingNode = doc.createElement('div');
|
||||
clobberingNode.id = id;
|
||||
clobberingNode.textContent = '{"test":999}';
|
||||
doc.body.appendChild(clobberingNode);
|
||||
|
||||
const script = doc.createElement('script');
|
||||
script.id = id;
|
||||
script.setAttribute('type', 'application/json');
|
||||
script.textContent = '{"test":10}';
|
||||
doc.body.appendChild(script);
|
||||
|
||||
const transferState: TransferState = TestBed.inject(TransferState);
|
||||
expect(transferState.get(TEST_KEY, 0)).toBe(0);
|
||||
});
|
||||
|
||||
it('is initialized to empty state if script tag not found', () => {
|
||||
const transferState: TransferState = TestBed.inject(TransferState);
|
||||
expect(transferState.get(TEST_KEY, 0)).toBe(0);
|
||||
|
||||
Reference in New Issue
Block a user