Skip to content

Commit

Permalink
Sanitize unknown attribute names for SSR
Browse files Browse the repository at this point in the history
  • Loading branch information
gaearon committed Aug 1, 2018
1 parent 9725065 commit 5b19684
Show file tree
Hide file tree
Showing 2 changed files with 152 additions and 16 deletions.
165 changes: 150 additions & 15 deletions packages/react-dom/src/__tests__/ReactDOMComponent-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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');
const beforeUpdate = React.createElement('x-foo-component', {}, null);
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>`',
]);
});

const afterUpdate = React.createElement(
'x-foo-component',
{'blah" onclick="beevil" noise="hi': 'selected'},
null,
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);
ReactDOM.render(
React.createElement(
'x-foo-component',
{'blah" onclick="beevil" noise="hi': 'selected'},
null,
),
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,
);
ReactDOM.render(afterUpdate, 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', () => {
Expand Down
3 changes: 2 additions & 1 deletion packages/react-dom/src/server/DOMMarkupOperations.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 '';
}

/**
Expand Down

0 comments on commit 5b19684

Please sign in to comment.