Fix __proto__ bug in ReactDOMSelect (#9536)
https://twitter.com/rauschma/status/857307345231249409
Ben Alpert committed
Apr 29, 2017 at 08:23 UTC
f677bfc964574053a41539fffae7891968c910a3
4 files changed
+61
-4
scripts/fiber/tests-passing.txt
+2
@@ -1529,8 +1529,10 @@ src/renderers/dom/shared/wrappers/__tests__/ReactDOMSelect-test.js
1529
* should not control when using `defaultValue`
1530
* should allow setting `defaultValue` with multiple
1531
* should allow setting `value`
1532
+* should allow setting `value` to __proto__
1533
* should not throw with `value` and without children
1534
* should allow setting `value` with multiple
1535
+* should allow setting `value` to __proto__ with multiple
1536
* should not select other options automatically
1537
* should reset child options selected when they are changed and `value` is set
1538
* should allow setting `value` with `objectToString`
src/renderers/dom/fiber/wrappers/ReactDOMFiberSelect.js
+3
-2
@@ -89,10 +89,11 @@ function updateOptions(
89
let selectedValues = (propValue: Array<string>);
90
let selectedValue = {};
91
for (let i = 0; i < selectedValues.length; i++) {
92
- selectedValue['' + selectedValues[i]] = true;
92
+ // Prefix to avoid chaos with special keys.
93
+ selectedValue['$' + selectedValues[i]] = true;
94
}
95
for (let i = 0; i < options.length; i++) {
95
- var selected = selectedValue.hasOwnProperty(options[i].value);
96
+ var selected = selectedValue.hasOwnProperty('$' + options[i].value);
97
if (options[i].selected !== selected) {
98
options[i].selected = selected;
99
}
src/renderers/dom/shared/wrappers/__tests__/ReactDOMSelect-test.js
+53
@@ -128,6 +128,29 @@ describe('ReactDOMSelect', () => {
128
expect(node.value).toEqual('gorilla');
129
});
130
131
+ it('should allow setting `value` to __proto__', () => {
132
+ var stub = (
133
+ <select value="__proto__" onChange={noop}>
134
+ <option value="monkey">A monkey!</option>
135
+ <option value="__proto__">A giraffe!</option>
136
+ <option value="gorilla">A gorilla!</option>
137
+ </select>
138
+ );
139
+ var options = stub.props.children;
140
+ var container = document.createElement('div');
141
+ stub = ReactDOM.render(stub, container);
142
+ var node = ReactDOM.findDOMNode(stub);
143
+
144
+ expect(node.value).toBe('__proto__');
145
+
146
+ // Changing the `value` prop should change the selected option.
147
+ ReactDOM.render(
148
+ <select value="gorilla" onChange={noop}>{options}</select>,
149
+ container,
150
+ );
151
+ expect(node.value).toEqual('gorilla');
152
+ });
153
+
154
it('should not throw with `value` and without children', () => {
155
var stub = <select value="dummy" onChange={noop} />;
156
@@ -166,6 +189,36 @@ describe('ReactDOMSelect', () => {
189
expect(node.options[2].selected).toBe(false); // gorilla
190
});
191
192
+ it('should allow setting `value` to __proto__ with multiple', () => {
193
+ var stub = (
194
+ <select multiple={true} value={['__proto__', 'gorilla']} onChange={noop}>
195
+ <option value="monkey">A monkey!</option>
196
+ <option value="__proto__">A __proto__!</option>
197
+ <option value="gorilla">A gorilla!</option>
198
+ </select>
199
+ );
200
+ var options = stub.props.children;
201
+ var container = document.createElement('div');
202
+ stub = ReactDOM.render(stub, container);
203
+ var node = ReactDOM.findDOMNode(stub);
204
+
205
+ expect(node.options[0].selected).toBe(false); // monkey
206
+ expect(node.options[1].selected).toBe(true); // __proto__
207
+ expect(node.options[2].selected).toBe(true); // gorilla
208
+
209
+ // Changing the `value` prop should change the selected options.
210
+ ReactDOM.render(
211
+ <select multiple={true} value={['monkey']} onChange={noop}>
212
+ {options}
213
+ </select>,
214
+ container,
215
+ );
216
+
217
+ expect(node.options[0].selected).toBe(true); // monkey
218
+ expect(node.options[1].selected).toBe(false); // __proto__
219
+ expect(node.options[2].selected).toBe(false); // gorilla
220
+ });
221
+
222
it('should not select other options automatically', () => {
223
var stub = (
224
<select multiple={true} value={['12']} onChange={noop}>
src/renderers/dom/stack/client/wrappers/ReactDOMSelect.js
+3
-2
@@ -84,10 +84,11 @@ function updateOptions(inst, multiple, propValue) {
84
if (multiple) {
85
let selectedValue = {};
86
for (let i = 0; i < propValue.length; i++) {
87
- selectedValue['' + propValue[i]] = true;
87
+ // Prefix to avoid chaos with special keys.
88
+ selectedValue['$' + propValue[i]] = true;
89
}
90
for (let i = 0; i < options.length; i++) {
90
- var selected = selectedValue.hasOwnProperty(options[i].value);
91
+ var selected = selectedValue.hasOwnProperty('$' + options[i].value);
92
if (options[i].selected !== selected) {
93
options[i].selected = selected;
94
}