main
js 287 lines 7.37 KB
Raw
1 /**
2 * Copyright (c) Meta Platforms, Inc. and affiliates.
3 *
4 * This source code is licensed under the MIT license found in the
5 * LICENSE file in the root directory of this source tree.
6 *
7 * @emails react-core
8 */
9
10 'use strict';
11
12 const rule = require('../safe-string-coercion');
13 const {RuleTester} = require('eslint');
14
15 RuleTester.setDefaultConfig({
16 parser: require.resolve('babel-eslint'),
17 parserOptions: {
18 ecmaVersion: 6,
19 sourceType: 'module',
20 },
21 });
22
23 const ruleTester = new RuleTester();
24
25 const missingDevCheckMessage =
26 'Missing DEV check before this string coercion.' +
27 ' Check should be in this format:\n' +
28 ' if (__DEV__) {\n' +
29 ' checkXxxxxStringCoercion(value);\n' +
30 ' }';
31 const prevStatementNotDevCheckMessage =
32 'The statement before this coercion must be a DEV check in this format:\n' +
33 ' if (__DEV__) {\n' +
34 ' checkXxxxxStringCoercion(value);\n' +
35 ' }';
36 const message =
37 "Using `'' + value` or `value + ''` is fast to coerce strings, but may throw." +
38 ' For prod code, add a DEV check from shared/CheckStringCoercion immediately' +
39 ' before this coercion.' +
40 ' For non-prod code and prod error handling, use `String(value)` instead.';
41
42 ruleTester.run('eslint-rules/safe-string-coercion', rule, {
43 valid: [
44 {
45 code: 'String(obj)',
46 options: [{isProductionUserAppCode: false}],
47 },
48 'String(obj)',
49 "'a' + obj",
50 `
51 function getValueForAttribute(
52 node,
53 name,
54 expected
55 ) {
56 if (__DEV__) {
57 var value = node.getAttribute(name);
58 if (__DEV__) {
59 checkAttributeStringCoercion(expected, name);
60 }
61 if (value === '' + expected) {
62 return expected;
63 }
64 return value;
65 }
66 }
67 `,
68 `
69 if (__DEV__) { checkFormFieldValueStringCoercion (obj) }
70 '' + obj;
71 `,
72 `
73 function f(a, index) {
74 if (typeof a === 'object' && a !== null && a.key != null) {
75 if (__DEV__) {
76 checkKeyStringCoercion(a.key);
77 }
78 return f('' + a.key);
79 }
80 return a;
81 }
82 `,
83 "'' + i++",
84 "'' + +i",
85 "'' + +i",
86 "+i + ''",
87 "if (typeof obj === 'string') { '' + obj }",
88 "if (typeof obj === 'string' || typeof obj === 'number') { '' + obj }",
89 "if (typeof obj === 'string' && somethingElse) { '' + obj }",
90 "if (typeof obj === 'number' && somethingElse) { '' + obj }",
91 "if (typeof obj === 'bigint' && somethingElse) { '' + obj }",
92 "if (typeof obj === 'undefined' && somethingElse) { '' + obj }",
93 "if (typeof nextProp === 'number') { setTextContent(domElement, '' + nextProp); }",
94 // These twe below are sneaky. The inner `if` is unsafe, but the outer `if`
95 // ensures that the unsafe code will never be run. It's bad code, but
96 // doesn't violate this rule.
97 "if (typeof obj === 'string') { if (typeof obj === 'string' && obj.length) {} else {'' + obj} }",
98 "if (typeof obj === 'string') if (typeof obj === 'string' && obj.length) {} else {'' + obj}",
99 "'' + ''",
100 "'' + '' + ''",
101 "`test${foo}` + ''",
102 ],
103 invalid: [
104 {
105 code: "'' + obj",
106 errors: [
107 {
108 message: missingDevCheckMessage + '\n' + message,
109 },
110 ],
111 },
112 {
113 code: "obj + ''",
114 errors: [
115 {
116 message: missingDevCheckMessage + '\n' + message,
117 },
118 ],
119 },
120 {
121 code: 'String(obj)',
122 options: [{isProductionUserAppCode: true}],
123 errors: [
124 {
125 message:
126 "For perf-sensitive coercion, avoid `String(value)`. Instead, use `'' + value`." +
127 ' Precede it with a DEV check from shared/CheckStringCoercion' +
128 ' unless Symbol and Temporal.* values are impossible.' +
129 ' For non-prod code and prod error handling, use `String(value)` and disable this rule.',
130 },
131 ],
132 },
133 {
134 code: "if (typeof obj === 'object') { '' + obj }",
135 errors: [
136 {
137 message: missingDevCheckMessage + '\n' + message,
138 },
139 ],
140 },
141 {
142 code: "if (typeof obj === 'string') { } else if (typeof obj === 'object') {'' + obj}",
143 errors: [
144 {
145 message: missingDevCheckMessage + '\n' + message,
146 },
147 ],
148 },
149 {
150 code: "if (typeof obj === 'string' && obj.length) {} else {'' + obj}",
151 errors: [
152 {
153 message: missingDevCheckMessage + '\n' + message,
154 },
155 ],
156 },
157 {
158 code: `
159 if (__D__) { checkFormFieldValueStringCoercion (obj) }
160 '' + obj;
161 `,
162 errors: [
163 {
164 message: prevStatementNotDevCheckMessage + '\n' + message,
165 },
166 ],
167 },
168 {
169 code: `
170 if (__DEV__) { checkFormFieldValueStringCoercion (obj) }
171 '' + notobjj;
172 `,
173 errors: [
174 {
175 message:
176 'Value passed to the check function before this coercion must match the value being coerced.' +
177 '\n' +
178 message,
179 },
180 ],
181 },
182 {
183 code: `
184 if (__DEV__) { checkFormFieldValueStringCoercion (obj) }
185 // must be right before the check call
186 someOtherCode();
187 '' + objj;
188 `,
189 errors: [
190 {
191 message: prevStatementNotDevCheckMessage + '\n' + message,
192 },
193 ],
194 },
195 {
196 code: `
197 if (__DEV__) { chexxxxBadNameCoercion (obj) }
198 '' + objj;
199 `,
200 errors: [
201 {
202 message:
203 'Missing or invalid check function call before this coercion.' +
204 ' Expected: call of a function like checkXXXStringCoercion. ' +
205 prevStatementNotDevCheckMessage +
206 '\n' +
207 message,
208 },
209 ],
210 },
211 {
212 code: `
213 if (__DEV__) { }
214 '' + objj;
215 `,
216 errors: [
217 {
218 message: prevStatementNotDevCheckMessage + '\n' + message,
219 },
220 ],
221 },
222 {
223 code: `
224 if (__DEV__) { if (x) {} }
225 '' + objj;
226 `,
227 errors: [
228 {
229 message:
230 'The DEV block before this coercion must only contain an expression. ' +
231 prevStatementNotDevCheckMessage +
232 '\n' +
233 message,
234 },
235 ],
236 },
237 {
238 code: `
239 if (a) {
240 if (__DEV__) {
241 // can't have additional code before the check call
242 if (b) {
243 checkKeyStringCoercion(obj);
244 }
245 }
246 g = f( c, d + (b ? '' + obj : '') + e);
247 }
248 `,
249 errors: [
250 {
251 message:
252 'The DEV block before this coercion must only contain an expression. ' +
253 prevStatementNotDevCheckMessage +
254 '\n' +
255 message,
256 },
257 ],
258 },
259 {
260 code: `
261 if (__DEV__) {
262 checkAttributeStringCoercion(expected, name);
263 }
264 // DEV check should be inside the if block
265 if (a && b) {
266 f('' + expected);
267 }
268 `,
269 errors: [
270 {
271 message: missingDevCheckMessage + '\n' + message,
272 },
273 ],
274 },
275 {
276 code: `'' + obj + ''`,
277 errors: [
278 {message: missingDevCheckMessage + '\n' + message},
279 {message: missingDevCheckMessage + '\n' + message},
280 ],
281 },
282 {
283 code: `foo\`text\` + ""`,
284 errors: [{message: missingDevCheckMessage + '\n' + message}],
285 },
286 ],
287 });