mirror of
https://github.com/facebook/react.git
synced 2026-02-27 03:07:57 +00:00
Sanitize unknown attribute names for SSR (#13302)
This commit is contained in:
@@ -554,39 +554,174 @@ describe('ReactDOMComponent', () => {
|
||||
expect(stubStyle.color).toEqual('green');
|
||||
});
|
||||
|
||||
it('should reject attribute key injection attack on markup', () => {
|
||||
it('should reject attribute key injection attack on markup for regular DOM (SSR)', () => {
|
||||
expect(() => {
|
||||
for (let i = 0; i < 3; i++) {
|
||||
const container = document.createElement('div');
|
||||
const element = React.createElement(
|
||||
const element1 = React.createElement(
|
||||
'div',
|
||||
{'blah" onclick="beevil" noise="hi': 'selected'},
|
||||
null,
|
||||
);
|
||||
const element2 = React.createElement(
|
||||
'div',
|
||||
{'></div><script>alert("hi")</script>': 'selected'},
|
||||
null,
|
||||
);
|
||||
let result1 = ReactDOMServer.renderToString(element1);
|
||||
let result2 = ReactDOMServer.renderToString(element2);
|
||||
expect(result1.toLowerCase()).not.toContain('onclick');
|
||||
expect(result2.toLowerCase()).not.toContain('script');
|
||||
}
|
||||
}).toWarnDev([
|
||||
'Warning: Invalid attribute name: `blah" onclick="beevil" noise="hi`',
|
||||
'Warning: Invalid attribute name: `></div><script>alert("hi")</script>`',
|
||||
]);
|
||||
});
|
||||
|
||||
it('should reject attribute key injection attack on markup for custom elements (SSR)', () => {
|
||||
expect(() => {
|
||||
for (let i = 0; i < 3; i++) {
|
||||
const element1 = React.createElement(
|
||||
'x-foo-component',
|
||||
{'blah" onclick="beevil" noise="hi': 'selected'},
|
||||
null,
|
||||
);
|
||||
ReactDOM.render(element, container);
|
||||
const element2 = React.createElement(
|
||||
'x-foo-component',
|
||||
{'></x-foo-component><script>alert("hi")</script>': 'selected'},
|
||||
null,
|
||||
);
|
||||
let result1 = ReactDOMServer.renderToString(element1);
|
||||
let result2 = ReactDOMServer.renderToString(element2);
|
||||
expect(result1.toLowerCase()).not.toContain('onclick');
|
||||
expect(result2.toLowerCase()).not.toContain('script');
|
||||
}
|
||||
}).toWarnDev(
|
||||
}).toWarnDev([
|
||||
'Warning: Invalid attribute name: `blah" onclick="beevil" noise="hi`',
|
||||
);
|
||||
'Warning: Invalid attribute name: `></x-foo-component><script>alert("hi")</script>`',
|
||||
]);
|
||||
});
|
||||
|
||||
it('should reject attribute key injection attack on update', () => {
|
||||
it('should reject attribute key injection attack on mount for regular DOM', () => {
|
||||
expect(() => {
|
||||
for (let i = 0; i < 3; i++) {
|
||||
const container = document.createElement('div');
|
||||
ReactDOM.render(
|
||||
React.createElement(
|
||||
'div',
|
||||
{'blah" onclick="beevil" noise="hi': 'selected'},
|
||||
null,
|
||||
),
|
||||
container,
|
||||
);
|
||||
expect(container.firstChild.attributes.length).toBe(0);
|
||||
ReactDOM.unmountComponentAtNode(container);
|
||||
ReactDOM.render(
|
||||
React.createElement(
|
||||
'div',
|
||||
{'></div><script>alert("hi")</script>': 'selected'},
|
||||
null,
|
||||
),
|
||||
container,
|
||||
);
|
||||
expect(container.firstChild.attributes.length).toBe(0);
|
||||
}
|
||||
}).toWarnDev([
|
||||
'Warning: Invalid attribute name: `blah" onclick="beevil" noise="hi`',
|
||||
'Warning: Invalid attribute name: `></div><script>alert("hi")</script>`',
|
||||
]);
|
||||
});
|
||||
|
||||
it('should reject attribute key injection attack on mount for custom elements', () => {
|
||||
expect(() => {
|
||||
for (let i = 0; i < 3; i++) {
|
||||
const container = document.createElement('div');
|
||||
ReactDOM.render(
|
||||
React.createElement(
|
||||
'x-foo-component',
|
||||
{'blah" onclick="beevil" noise="hi': 'selected'},
|
||||
null,
|
||||
),
|
||||
container,
|
||||
);
|
||||
expect(container.firstChild.attributes.length).toBe(0);
|
||||
ReactDOM.unmountComponentAtNode(container);
|
||||
ReactDOM.render(
|
||||
React.createElement(
|
||||
'x-foo-component',
|
||||
{'></x-foo-component><script>alert("hi")</script>': 'selected'},
|
||||
null,
|
||||
),
|
||||
container,
|
||||
);
|
||||
expect(container.firstChild.attributes.length).toBe(0);
|
||||
}
|
||||
}).toWarnDev([
|
||||
'Warning: Invalid attribute name: `blah" onclick="beevil" noise="hi`',
|
||||
'Warning: Invalid attribute name: `></x-foo-component><script>alert("hi")</script>`',
|
||||
]);
|
||||
});
|
||||
|
||||
it('should reject attribute key injection attack on update for regular DOM', () => {
|
||||
expect(() => {
|
||||
for (let i = 0; i < 3; i++) {
|
||||
const container = document.createElement('div');
|
||||
const beforeUpdate = React.createElement('div', {}, null);
|
||||
ReactDOM.render(beforeUpdate, container);
|
||||
ReactDOM.render(
|
||||
React.createElement(
|
||||
'div',
|
||||
{'blah" onclick="beevil" noise="hi': 'selected'},
|
||||
null,
|
||||
),
|
||||
container,
|
||||
);
|
||||
expect(container.firstChild.attributes.length).toBe(0);
|
||||
ReactDOM.render(
|
||||
React.createElement(
|
||||
'div',
|
||||
{'></div><script>alert("hi")</script>': 'selected'},
|
||||
null,
|
||||
),
|
||||
container,
|
||||
);
|
||||
expect(container.firstChild.attributes.length).toBe(0);
|
||||
}
|
||||
}).toWarnDev([
|
||||
'Warning: Invalid attribute name: `blah" onclick="beevil" noise="hi`',
|
||||
'Warning: Invalid attribute name: `></div><script>alert("hi")</script>`',
|
||||
]);
|
||||
});
|
||||
|
||||
it('should reject attribute key injection attack on update for custom elements', () => {
|
||||
expect(() => {
|
||||
for (let i = 0; i < 3; i++) {
|
||||
const container = document.createElement('div');
|
||||
const beforeUpdate = React.createElement('x-foo-component', {}, null);
|
||||
ReactDOM.render(beforeUpdate, container);
|
||||
|
||||
const afterUpdate = React.createElement(
|
||||
'x-foo-component',
|
||||
{'blah" onclick="beevil" noise="hi': 'selected'},
|
||||
null,
|
||||
ReactDOM.render(
|
||||
React.createElement(
|
||||
'x-foo-component',
|
||||
{'blah" onclick="beevil" noise="hi': 'selected'},
|
||||
null,
|
||||
),
|
||||
container,
|
||||
);
|
||||
ReactDOM.render(afterUpdate, container);
|
||||
expect(container.firstChild.attributes.length).toBe(0);
|
||||
ReactDOM.render(
|
||||
React.createElement(
|
||||
'x-foo-component',
|
||||
{'></x-foo-component><script>alert("hi")</script>': 'selected'},
|
||||
null,
|
||||
),
|
||||
container,
|
||||
);
|
||||
expect(container.firstChild.attributes.length).toBe(0);
|
||||
}
|
||||
}).toWarnDev(
|
||||
}).toWarnDev([
|
||||
'Warning: Invalid attribute name: `blah" onclick="beevil" noise="hi`',
|
||||
);
|
||||
'Warning: Invalid attribute name: `></x-foo-component><script>alert("hi")</script>`',
|
||||
]);
|
||||
});
|
||||
|
||||
it('should update arbitrary attributes for tags containing dashes', () => {
|
||||
|
||||
@@ -60,9 +60,10 @@ export function createMarkupForProperty(name: string, value: mixed): string {
|
||||
} else {
|
||||
return attributeName + '=' + quoteAttributeValueForBrowser(value);
|
||||
}
|
||||
} else {
|
||||
} else if (isAttributeNameSafe(name)) {
|
||||
return name + '=' + quoteAttributeValueForBrowser(value);
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user