Update safe-string-coercion to handle additions of string literals (#25286)
* Update safe-string-coercion to handle additions of string literals Adding strings shouldn't trigger a lint violation of this rule, since adding strings are always safe.
lauren committed
Oct 4, 2022 at 12:29 UTC
af9afe9b3f622aa5eb4caf873025423ba8163c9f
2 files changed
+57
-8
scripts/eslint-rules/__tests__/safe-string-coercion-test.internal.js
+27
-4
@@ -11,6 +11,15 @@
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 =
@@ -57,7 +66,7 @@ ruleTester.run('eslint-rules/safe-string-coercion', rule, {
66
}
67
`,
68
`
60
- if (__DEV__) { checkFormFieldValueStringCoercion (obj) }
69
+ if (__DEV__) { checkFormFieldValueStringCoercion (obj) }
70
'' + obj;
71
`,
72
`
@@ -87,6 +96,9 @@ ruleTester.run('eslint-rules/safe-string-coercion', rule, {
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
{
@@ -145,7 +157,7 @@ ruleTester.run('eslint-rules/safe-string-coercion', rule, {
157
},
158
{
159
code: `
148
- if (__D__) { checkFormFieldValueStringCoercion (obj) }
160
+ if (__D__) { checkFormFieldValueStringCoercion (obj) }
161
'' + obj;
162
`,
163
errors: [
@@ -156,7 +168,7 @@ ruleTester.run('eslint-rules/safe-string-coercion', rule, {
168
},
169
{
170
code: `
159
- if (__DEV__) { checkFormFieldValueStringCoercion (obj) }
171
+ if (__DEV__) { checkFormFieldValueStringCoercion (obj) }
172
'' + notobjj;
173
`,
174
errors: [
@@ -172,7 +184,7 @@ ruleTester.run('eslint-rules/safe-string-coercion', rule, {
184
code: `
185
if (__DEV__) { checkFormFieldValueStringCoercion (obj) }
186
// must be right before the check call
175
- someOtherCode();
187
+ someOtherCode();
188
'' + objj;
189
`,
190
errors: [
@@ -261,5 +273,16 @@ ruleTester.run('eslint-rules/safe-string-coercion', rule, {
273
},
274
],
275
},
276
+ {
277
+ code: `'' + obj + ''`,
278
+ errors: [
279
+ {message: missingDevCheckMessage + '\n' + message},
280
+ {message: missingDevCheckMessage + '\n' + message},
281
+ ],
282
+ },
283
+ {
284
+ code: `foo\`text\` + ""`,
285
+ errors: [{message: missingDevCheckMessage + '\n' + message}],
286
+ },
287
],
288
});
scripts/eslint-rules/safe-string-coercion.js
+30
-4
@@ -17,6 +17,15 @@ function isEmptyLiteral(node) {
17
);
18
}
19
20
+function isStringLiteral(node) {
21
+ return (
22
+ // TaggedTemplateExpressions can return non-strings
23
+ (node.type === 'TemplateLiteral' &&
24
+ node.parent.type !== 'TaggedTemplateExpression') ||
25
+ (node.type === 'Literal' && typeof node.value === 'string')
26
+ );
27
+}
28
+
29
// Symbols and Temporal.* objects will throw when using `'' + value`, but that
30
// pattern can be faster than `String(value)` because JS engines can optimize
31
// `+` better in some cases. Therefore, in perf-sensitive production codepaths
@@ -120,9 +129,9 @@ function isSafeTypeofExpression(originalValueNode, node) {
129
return false;
130
}
131
123
-/**
132
+/**
133
Returns true if the code is inside an `if` block that validates the value
125
- excludes symbols and objects. Examples:
134
+ excludes symbols and objects. Examples:
135
* if (typeof value === 'string') { }
136
* if (typeof value === 'string' || typeof value === 'number') { }
137
* if (typeof value === 'string' || someOtherTest) { }
@@ -259,7 +268,24 @@ function hasCoercionCheck(node) {
268
}
269
}
270
262
-function plusEmptyString(context, node) {
271
+function isOnlyAddingStrings(node) {
272
+ if (node.operator !== '+') {
273
+ return;
274
+ }
275
+ if (isStringLiteral(node.left) && isStringLiteral(node.right)) {
276
+ // It's always safe to add string literals
277
+ return true;
278
+ }
279
+ if (node.left.type === 'BinaryExpression' && isStringLiteral(node.right)) {
280
+ return isOnlyAddingStrings(node.left);
281
+ }
282
+}
283
+
284
+function checkBinaryExpression(context, node) {
285
+ if (isOnlyAddingStrings(node)) {
286
+ return;
287
+ }
288
+
289
if (
290
node.operator === '+' &&
291
(isEmptyLiteral(node.left) || isEmptyLiteral(node.right))
@@ -337,7 +363,7 @@ module.exports = {
363
},
364
create(context) {
365
return {
340
- BinaryExpression: node => plusEmptyString(context, node),
366
+ BinaryExpression: node => checkBinaryExpression(context, node),
367
CallExpression: node => coerceWithStringConstructor(context, node),
368
};
369
},