no-danger.js 2.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596
  1. /**
  2. * @fileoverview Prevent usage of dangerous JSX props
  3. * @author Scott Andrews
  4. */
  5. 'use strict';
  6. const has = require('hasown');
  7. const fromEntries = require('object.fromentries/polyfill')();
  8. const minimatch = require('minimatch');
  9. const docsUrl = require('../util/docsUrl');
  10. const jsxUtil = require('../util/jsx');
  11. const report = require('../util/report');
  12. // ------------------------------------------------------------------------------
  13. // Constants
  14. // ------------------------------------------------------------------------------
  15. const DANGEROUS_PROPERTY_NAMES = [
  16. 'dangerouslySetInnerHTML',
  17. ];
  18. const DANGEROUS_PROPERTIES = fromEntries(DANGEROUS_PROPERTY_NAMES.map((prop) => [prop, prop]));
  19. // ------------------------------------------------------------------------------
  20. // Helpers
  21. // ------------------------------------------------------------------------------
  22. /**
  23. * Checks if a JSX attribute is dangerous.
  24. * @param {string} name - Name of the attribute to check.
  25. * @returns {boolean} Whether or not the attribute is dangerous.
  26. */
  27. function isDangerous(name) {
  28. return has(DANGEROUS_PROPERTIES, name);
  29. }
  30. // ------------------------------------------------------------------------------
  31. // Rule Definition
  32. // ------------------------------------------------------------------------------
  33. const messages = {
  34. dangerousProp: 'Dangerous property \'{{name}}\' found',
  35. };
  36. /** @type {import('eslint').Rule.RuleModule} */
  37. module.exports = {
  38. meta: {
  39. docs: {
  40. description: 'Disallow usage of dangerous JSX properties',
  41. category: 'Best Practices',
  42. recommended: false,
  43. url: docsUrl('no-danger'),
  44. },
  45. messages,
  46. schema: [{
  47. type: 'object',
  48. properties: {
  49. customComponentNames: {
  50. items: {
  51. type: 'string',
  52. },
  53. minItems: 0,
  54. type: 'array',
  55. uniqueItems: true,
  56. },
  57. },
  58. }],
  59. },
  60. create(context) {
  61. const configuration = context.options[0] || {};
  62. const customComponentNames = configuration.customComponentNames || [];
  63. return {
  64. JSXAttribute(node) {
  65. const nodeName = node.parent.name;
  66. const functionName = nodeName.name || `${nodeName.object.name}.${nodeName.property.name}`;
  67. const enableCheckingCustomComponent = customComponentNames.some((name) => minimatch(functionName, name));
  68. if ((enableCheckingCustomComponent || jsxUtil.isDOMComponent(node.parent)) && isDangerous(node.name.name)) {
  69. report(context, messages.dangerousProp, 'dangerousProp', {
  70. node,
  71. data: {
  72. name: node.name.name,
  73. },
  74. });
  75. }
  76. },
  77. };
  78. },
  79. };