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. });