jsx-no-script-url.js 2.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596
  1. /**
  2. * @fileoverview Prevent usage of `javascript:` URLs
  3. * @author Sergei Startsev
  4. */
  5. 'use strict';
  6. const docsUrl = require('../util/docsUrl');
  7. // ------------------------------------------------------------------------------
  8. // Rule Definition
  9. // ------------------------------------------------------------------------------
  10. // https://github.com/facebook/react/blob/d0ebde77f6d1232cefc0da184d731943d78e86f2/packages/react-dom/src/shared/sanitizeURL.js#L30
  11. /* eslint-disable-next-line max-len, no-control-regex */
  12. const isJavaScriptProtocol = /^[\u0000-\u001F ]*j[\r\n\t]*a[\r\n\t]*v[\r\n\t]*a[\r\n\t]*s[\r\n\t]*c[\r\n\t]*r[\r\n\t]*i[\r\n\t]*p[\r\n\t]*t[\r\n\t]*:/i;
  13. function hasJavaScriptProtocol(attr) {
  14. return attr.value && attr.value.type === 'Literal'
  15. && isJavaScriptProtocol.test(attr.value.value);
  16. }
  17. function shouldVerifyElement(node, config) {
  18. const name = node.name && node.name.name;
  19. return name === 'a' || config.find((i) => i.name === name);
  20. }
  21. function shouldVerifyProp(node, config) {
  22. const name = node.name && node.name.name;
  23. const parentName = node.parent.name && node.parent.name.name;
  24. if (parentName === 'a' && name === 'href') {
  25. return true;
  26. }
  27. const el = config.find((i) => i.name === parentName);
  28. if (!el) {
  29. return false;
  30. }
  31. const props = el.props || [];
  32. return node.name && props.indexOf(name) !== -1;
  33. }
  34. module.exports = {
  35. meta: {
  36. docs: {
  37. description: 'Forbid `javascript:` URLs',
  38. category: 'Best Practices',
  39. recommended: false,
  40. url: docsUrl('jsx-no-script-url')
  41. },
  42. messages: {
  43. noScriptURL: 'A future version of React will block javascript: URLs as a security precaution. '
  44. + 'Use event handlers instead if you can. If you need to generate unsafe HTML, try using dangerouslySetInnerHTML instead.'
  45. },
  46. schema: [{
  47. type: 'array',
  48. uniqueItems: true,
  49. items: {
  50. type: 'object',
  51. properties: {
  52. name: {
  53. type: 'string'
  54. },
  55. props: {
  56. type: 'array',
  57. items: {
  58. type: 'string',
  59. uniqueItems: true
  60. }
  61. }
  62. },
  63. required: ['name', 'props'],
  64. additionalProperties: false
  65. }
  66. }]
  67. },
  68. create(context) {
  69. const config = context.options[0] || [];
  70. return {
  71. JSXAttribute(node) {
  72. const parent = node.parent;
  73. if (shouldVerifyElement(parent, config) && shouldVerifyProp(node, config) && hasJavaScriptProtocol(node)) {
  74. context.report({
  75. node,
  76. messageId: 'noScriptURL'
  77. });
  78. }
  79. }
  80. };
  81. }
  82. };