/*** Copyright (c) Meta Platforms, Inc. and affiliates.** This source code is licensed under the MIT license found in the* LICENSE file in the root directory of this source tree.** @emails react-core*/'use strict';
const rule = require('../safe-string-coercion');
const {RuleTester} = require('eslint');
RuleTester.setDefaultConfig({
parser: require.resolve('babel-eslint'),
parserOptions: {ecmaVersion: 6,
sourceType: 'module',
},});const ruleTester = new RuleTester();
const missingDevCheckMessage =
'Missing DEV check before this string coercion.' +
' Check should be in this format:\n' +
' if (__DEV__) {\n' +
' checkXxxxxStringCoercion(value);\n' +
' }';
const prevStatementNotDevCheckMessage =
'The statement before this coercion must be a DEV check in this format:\n' +
' if (__DEV__) {\n' +
' checkXxxxxStringCoercion(value);\n' +
' }';
const message =
"Using `'' + value` or `value + ''` is fast to coerce strings, but may throw." +
' For prod code, add a DEV check from shared/CheckStringCoercion immediately' +
' before this coercion.' +
' For non-prod code and prod error handling, use `String(value)` instead.';
ruleTester.run('eslint-rules/safe-string-coercion', rule, {
valid: [{code: 'String(obj)',
options: [{isProductionUserAppCode: false}],
},'String(obj)',
"'a' + obj",
`function getValueForAttribute(node,name,expected) {if (__DEV__) {var value = node.getAttribute(name);if (__DEV__) {checkAttributeStringCoercion(expected, name);}if (value === '' + expected) {return expected;}return value;}}`,
`if (__DEV__) { checkFormFieldValueStringCoercion (obj) }'' + obj;`,
`function f(a, index) {if (typeof a === 'object' && a !== null && a.key != null) {if (__DEV__) {checkKeyStringCoercion(a.key);}return f('' + a.key);}return a;}`,
"'' + i++",
"'' + +i",
"'' + +i",
"+i + ''",
"if (typeof obj === 'string') { '' + obj }",
"if (typeof obj === 'string' || typeof obj === 'number') { '' + obj }",
"if (typeof obj === 'string' && somethingElse) { '' + obj }",
"if (typeof obj === 'number' && somethingElse) { '' + obj }",
"if (typeof obj === 'bigint' && somethingElse) { '' + obj }",
"if (typeof obj === 'undefined' && somethingElse) { '' + obj }",
"if (typeof nextProp === 'number') { setTextContent(domElement, '' + nextProp); }",
// These twe below are sneaky. The inner `if` is unsafe, but the outer `if`
// ensures that the unsafe code will never be run. It's bad code, but
// doesn't violate this rule.
"if (typeof obj === 'string') { if (typeof obj === 'string' && obj.length) {} else {'' + obj} }",
"if (typeof obj === 'string') if (typeof obj === 'string' && obj.length) {} else {'' + obj}",
"'' + ''",
"'' + '' + ''",
"`test${foo}` + ''",
],invalid: [{code: "'' + obj",
errors: [{message: missingDevCheckMessage + '\n' + message,
},],},{code: "obj + ''",
errors: [{message: missingDevCheckMessage + '\n' + message,
},],},{code: 'String(obj)',
options: [{isProductionUserAppCode: true}],
errors: [{message:"For perf-sensitive coercion, avoid `String(value)`. Instead, use `'' + value`." +
' Precede it with a DEV check from shared/CheckStringCoercion' +
' unless Symbol and Temporal.* values are impossible.' +
' For non-prod code and prod error handling, use `String(value)` and disable this rule.',
},],},{code: "if (typeof obj === 'object') { '' + obj }",
errors: [{message: missingDevCheckMessage + '\n' + message,
},],},{code: "if (typeof obj === 'string') { } else if (typeof obj === 'object') {'' + obj}",
errors: [{message: missingDevCheckMessage + '\n' + message,
},],},{code: "if (typeof obj === 'string' && obj.length) {} else {'' + obj}",
errors: [{message: missingDevCheckMessage + '\n' + message,
},],},{code: `if (__D__) { checkFormFieldValueStringCoercion (obj) }'' + obj;`,
errors: [{message: prevStatementNotDevCheckMessage + '\n' + message,
},],},{code: `if (__DEV__) { checkFormFieldValueStringCoercion (obj) }'' + notobjj;`,
errors: [{message:'Value passed to the check function before this coercion must match the value being coerced.' +
'\n' +
message,
},],},{code: `if (__DEV__) { checkFormFieldValueStringCoercion (obj) }// must be right before the check callsomeOtherCode();'' + objj;`,
errors: [{message: prevStatementNotDevCheckMessage + '\n' + message,
},],},{code: `if (__DEV__) { chexxxxBadNameCoercion (obj) }'' + objj;`,
errors: [{message:'Missing or invalid check function call before this coercion.' +
' Expected: call of a function like checkXXXStringCoercion. ' +
prevStatementNotDevCheckMessage +
'\n' +
message,
},],},{code: `if (__DEV__) { }'' + objj;`,
errors: [{message: prevStatementNotDevCheckMessage + '\n' + message,
},],},{code: `if (__DEV__) { if (x) {} }'' + objj;`,
errors: [{message:'The DEV block before this coercion must only contain an expression. ' +
prevStatementNotDevCheckMessage +
'\n' +
message,
},],},{code: `if (a) {if (__DEV__) {// can't have additional code before the check callif (b) {checkKeyStringCoercion(obj);}}g = f( c, d + (b ? '' + obj : '') + e);}`,
errors: [{message:'The DEV block before this coercion must only contain an expression. ' +
prevStatementNotDevCheckMessage +
'\n' +
message,
},],},{code: `if (__DEV__) {checkAttributeStringCoercion(expected, name);}// DEV check should be inside the if blockif (a && b) {f('' + expected);}`,
errors: [{message: missingDevCheckMessage + '\n' + message,
},],},{code: `'' + obj + ''`,
errors: [{message: missingDevCheckMessage + '\n' + message},
{message: missingDevCheckMessage + '\n' + message},
],},{code: `foo\`text\` + ""`,
errors: [{message: missingDevCheckMessage + '\n' + message}],
},],});