├── .dccache ├── .github ├── CODEOWNERS └── snyk-code-screenshot.png ├── README.md ├── lib ├── index.js ├── rules │ ├── no-dangerously-set-innerhtml.js │ ├── no-find-dom-node.js │ ├── no-javascript-urls.js │ └── no-refs.js └── util │ └── docs.js ├── package.json ├── shared └── messages.js └── tests └── lib └── rules ├── no-dangerously-set-innerhtml.js ├── no-find-dom-node.js ├── no-javascript-urls.js └── no-refs.js /.dccache: -------------------------------------------------------------------------------- 1 | {"/Users/lirantal/projects/repos/eslint-plugin-react-security/lib/index.js":[186,1626076323433.372,"859be5749b2cbfaa7804e8043216b783e616c30b91ea235a4b1c232ebc8531c1"],"/Users/lirantal/projects/repos/eslint-plugin-react-security/shared/messages.js":[254,1626076323434.7126,"9702be7c0ba72850fbff4a8b114e2f46229b98690f3f3556ffd012236be4af00"],"/Users/lirantal/projects/repos/eslint-plugin-react-security/lib/rules/no-dangerously-set-innerhtml.js":[1089,1626076323433.6375,"fe1f5332379625401fef4f20c7a1948ef38d2226435d9558939da719fe87637b"],"/Users/lirantal/projects/repos/eslint-plugin-react-security/lib/rules/no-find-dom-node.js":[1192,1626076323433.8083,"b06cf0c6ab65ac79440462252dd48bd0ff9343e49a8ffcf178fe3f3f5da60cc1"],"/Users/lirantal/projects/repos/eslint-plugin-react-security/lib/rules/no-javascript-urls.js":[1373,1626076323433.951,"7bb551a660b16163462043fd280f69b4347212459e9cc7b7ea34c9eb994a9ede"],"/Users/lirantal/projects/repos/eslint-plugin-react-security/lib/rules/no-refs.js":[983,1626076323434.0896,"08db4e24ab1f36e6353a33bd13b26131373a39b1ab79d97039667aacfae48814"],"/Users/lirantal/projects/repos/eslint-plugin-react-security/lib/util/docs.js":[389,1626076323434.3228,"dcbda9ac9393c76b7ac11112f42f78a869a534a74f50144a6e1794d9a80fdaa1"],"/Users/lirantal/projects/repos/eslint-plugin-react-security/tests/lib/rules/no-dangerously-set-innerhtml.js":[1071,1626076323435.1462,"830f95cbe700c2ee99f50c67e9bc0c2d7966297bacbeb0fa8888d9e113dbadd8"],"/Users/lirantal/projects/repos/eslint-plugin-react-security/tests/lib/rules/no-find-dom-node.js":[1048,1626076323435.3857,"a3765bb9e928bb39143b0745eac82d172dcada875893f792f44ceaeb6acf6c30"],"/Users/lirantal/projects/repos/eslint-plugin-react-security/tests/lib/rules/no-javascript-urls.js":[1019,1626076323435.535,"d7a096e2bf878048e9fdc2f59b9c325945c7c7d8faaccd1b1edf3ad6e235ef2b"],"/Users/lirantal/projects/repos/eslint-plugin-react-security/tests/lib/rules/no-refs.js":[972,1626076323435.6826,"ee4b45b02a87be16c12057a9d068e0017aa7bb94d97f5136e68416ff01d054ec"]} -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @snyk/devrel 2 | -------------------------------------------------------------------------------- /.github/snyk-code-screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/snyk-labs/eslint-plugin-react-security/e91c7b6a8deffef96ba27fbae4d9894936e6b2b8/.github/snyk-code-screenshot.png -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | A developer-friendly static code analysis for React security? 3 |
yes, it exists: 4 |

5 | 6 |
7 | Try Snyk Code, a developer-first SAST tool. 8 |
9 | Like an ESLint rule for react security, but better. 10 |
Did I mention it's free? 11 |

12 | 13 | ⚠️ Important notice, this repository *is not* publisher to npmjs, and it isn't the same as [eslint-plugin-react-security](https://www.npmjs.com/package/eslint-plugin-react-security), which is a different project, maintained by an entirely different developer. 14 | 15 | ⚠️ Maintenance notice: The `eslint-plugin-react-security` project is no longer under active maintenance by the Snyk team. 16 | 17 | Instead, we invite you to try out the Snyk Code IDE integrations for either [IntelliJ](https://support.snyk.io/hc/en-us/articles/360004032317-JetBrains-plugins) or [VSCode](https://support.snyk.io/hc/en-us/articles/360018585717-Visual-Studio-Code-extension-for-Snyk-Code-) which provide a developer-friendly secure coding experience while you code. 18 | -------------------------------------------------------------------------------- /lib/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @file Gathers all the ESLint rules and exports them on one object. 3 | * @author Ron Perris 4 | */ 5 | 6 | "use strict"; 7 | 8 | module.exports.rules = require("requireindex")(`${__dirname}/rules`); 9 | -------------------------------------------------------------------------------- /lib/rules/no-dangerously-set-innerhtml.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @file Rule to detect usage of dangerouslySetInnerHTML prop. 3 | * @author Ron Perris 4 | */ 5 | 6 | "use strict"; 7 | 8 | // ----------------------------------------------------------------------------- 9 | // Requirements 10 | // ----------------------------------------------------------------------------- 11 | 12 | const docs = require("../util/docs"); 13 | 14 | const message = require("../../shared/messages")[ 15 | "no-dangerously-set-innerhtml" 16 | ]; 17 | 18 | //------------------------------------------------------------------------------ 19 | // Rule Definition 20 | //------------------------------------------------------------------------------ 21 | 22 | module.exports = { 23 | meta: { 24 | type: "suggestion", 25 | 26 | docs: { 27 | description: "Detect usage of dangerouslySetInnerHTML prop.", 28 | category: "Security", 29 | recommended: true, 30 | url: docs("no-dangerously-set-innerhtml"), 31 | }, 32 | }, 33 | create: function (context) { 34 | return { 35 | JSXAttribute: function (node) { 36 | const prop = node.name.name; 37 | 38 | if (prop === "dangerouslySetInnerHTML") { 39 | context.report(node, message); 40 | } 41 | }, 42 | }; 43 | }, 44 | }; 45 | -------------------------------------------------------------------------------- /lib/rules/no-find-dom-node.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @file Rule to detect usage of ReactDOM.findDOMNode(). 3 | * @author Ron Perris 4 | */ 5 | 6 | "use strict"; 7 | 8 | // ----------------------------------------------------------------------------- 9 | // Requirements 10 | // ----------------------------------------------------------------------------- 11 | 12 | const docs = require("../util/docs"); 13 | 14 | const message = require("../../shared/messages")["no-find-dom-node"]; 15 | 16 | //------------------------------------------------------------------------------ 17 | // Rule Definition 18 | //------------------------------------------------------------------------------ 19 | 20 | module.exports = { 21 | meta: { 22 | type: "suggestion", 23 | 24 | docs: { 25 | description: "Detect usage of ReactDOM.findDOMNode().", 26 | category: "Security", 27 | recommended: true, 28 | url: docs("no-find-dom-node"), 29 | }, 30 | }, 31 | create: function (context) { 32 | return { 33 | CallExpression: function (node) { 34 | if (node.callee.name === "findDOMNode") { 35 | return context.report(node, message); 36 | } 37 | 38 | if ( 39 | node.callee.property && 40 | node.callee.property.name === "findDOMNode" 41 | ) { 42 | return context.report(node, message); 43 | } 44 | }, 45 | }; 46 | }, 47 | }; 48 | -------------------------------------------------------------------------------- /lib/rules/no-javascript-urls.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @file Rule to detect javascript: urls in JSX. 3 | * @author Ron Perris 4 | */ 5 | 6 | "use strict"; 7 | 8 | // ----------------------------------------------------------------------------- 9 | // Requirements 10 | // ----------------------------------------------------------------------------- 11 | 12 | const docs = require("../util/docs"); 13 | 14 | const message = require("../../shared/messages")["no-javascript-urls"]; 15 | 16 | //------------------------------------------------------------------------------ 17 | // Rule Definition 18 | //------------------------------------------------------------------------------ 19 | 20 | function isJavaScriptURL(prop) { 21 | const url = new URL(prop); 22 | 23 | if (url.protocol !== "javascript:") return false; 24 | 25 | return true; 26 | } 27 | 28 | function isAnchor(name) { 29 | if (name === "a") return true; 30 | } 31 | 32 | function isHref(name) { 33 | if (name === "href") return true; 34 | } 35 | 36 | module.exports = { 37 | meta: { 38 | type: "suggestion", 39 | 40 | docs: { 41 | description: "Detect usage of javascript: urls in JSX.", 42 | category: "Security", 43 | recommended: true, 44 | url: docs("no-javascript-urls"), 45 | }, 46 | }, 47 | create: function (context) { 48 | return { 49 | JSXAttribute: function (node) { 50 | if (!isAnchor(node.parent.name.name)) return; 51 | if (!isHref(node.name.name)) return; 52 | 53 | if (isJavaScriptURL(node.value.value)) { 54 | return context.report(node, message); 55 | } 56 | }, 57 | }; 58 | }, 59 | }; 60 | -------------------------------------------------------------------------------- /lib/rules/no-refs.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @file Rule to detect usage of ref prop. 3 | * @author Ron Perris 4 | */ 5 | 6 | "use strict"; 7 | 8 | // ----------------------------------------------------------------------------- 9 | // Requirements 10 | // ----------------------------------------------------------------------------- 11 | 12 | const docs = require("../util/docs"); 13 | 14 | const message = require("../../shared/messages")["no-refs"]; 15 | 16 | //------------------------------------------------------------------------------ 17 | // Rule Definition 18 | //------------------------------------------------------------------------------ 19 | 20 | module.exports = { 21 | meta: { 22 | type: "suggestion", 23 | 24 | docs: { 25 | description: "Detect usage of ref prop.", 26 | category: "Security", 27 | recommended: true, 28 | url: docs("no-refs"), 29 | }, 30 | }, 31 | create: function (context) { 32 | return { 33 | JSXAttribute: function (node) { 34 | const prop = node.name.name; 35 | 36 | if (prop === "ref") { 37 | context.report(node, message); 38 | } 39 | }, 40 | }; 41 | }, 42 | }; 43 | -------------------------------------------------------------------------------- /lib/util/docs.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @file Defines helper function for documentation URLs. 3 | * @author Ron Perris 4 | */ 5 | 6 | "use strict"; 7 | 8 | /** 9 | * Returns documentation url for rule. 10 | * @params {String} rule - Rule name. 11 | * @returns {String} URL for rule documentation. 12 | */ 13 | function docs(rule) { 14 | return `https://github.com/snyk-labs/eslint-plugin-react-security/tree/master/docs/rules/${rule}.md`; 15 | } 16 | 17 | module.exports = docs; 18 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "eslint-plugin-react-security", 3 | "version": "0.0.0", 4 | "description": "For finding security issues in React projects.", 5 | "keywords": [ 6 | "eslint", 7 | "eslintplugin", 8 | "eslint-plugin" 9 | ], 10 | "author": "Ron Perris ", 11 | "main": "lib/index.js", 12 | "scripts": { 13 | "test": "mocha tests --recursive" 14 | }, 15 | "dependencies": { 16 | "requireindex": "~1.1.0" 17 | }, 18 | "devDependencies": { 19 | "eslint": "^7.1.0", 20 | "mocha": "^7.2.0" 21 | }, 22 | "engines": { 23 | "node": ">=12.0.0" 24 | }, 25 | "license": "ISC" 26 | } 27 | -------------------------------------------------------------------------------- /shared/messages.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | "no-dangerously-set-innerhtml": "dangrouslySetInnerHTML prop usage detected", 3 | "no-javascript-urls": "javascript: url in JSX detected", 4 | "no-find-dom-node": "findDOMNode usage detected", 5 | "no-refs": "refs prop usage detected", 6 | }; 7 | -------------------------------------------------------------------------------- /tests/lib/rules/no-dangerously-set-innerhtml.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileoverview Tests for no-dangerously-set-innerhtml rule. 3 | * @author Ron Perris 4 | */ 5 | 6 | "use strict"; 7 | 8 | // ----------------------------------------------------------------------------- 9 | // Requirements 10 | // ----------------------------------------------------------------------------- 11 | 12 | const rule = require("../../../lib/rules/no-dangerously-set-innerhtml"); 13 | const { RuleTester } = require("eslint"); 14 | const message = require("../../../shared/messages")[ 15 | "no-dangerously-set-innerhtml" 16 | ]; 17 | 18 | const parserOptions = { 19 | ecmaVersion: 2018, 20 | sourceType: "module", 21 | ecmaFeatures: { 22 | jsx: true, 23 | }, 24 | }; 25 | 26 | // ----------------------------------------------------------------------------- 27 | // Tests 28 | // ----------------------------------------------------------------------------- 29 | 30 | const ruleTester = new RuleTester({ parserOptions }); 31 | 32 | ruleTester.run("no-dangerously-set-innerhtml", rule, { 33 | valid: [{ code: '
;' }], 34 | invalid: [ 35 | { 36 | code: '
;', 37 | errors: [{ message }], 38 | }, 39 | ], 40 | }); 41 | -------------------------------------------------------------------------------- /tests/lib/rules/no-find-dom-node.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileoverview Tests for ReactDOM.findDOMNode() usage rule. 3 | * @author Ron Perris 4 | */ 5 | 6 | "use strict"; 7 | 8 | // ----------------------------------------------------------------------------- 9 | // Requirements 10 | // ----------------------------------------------------------------------------- 11 | 12 | const rule = require("../../../lib/rules/no-find-dom-node"); 13 | const { RuleTester } = require("eslint"); 14 | const message = require("../../../shared/messages")["no-find-dom-node"]; 15 | 16 | const parserOptions = { 17 | ecmaVersion: 2018, 18 | sourceType: "module", 19 | ecmaFeatures: { 20 | jsx: true, 21 | }, 22 | }; 23 | 24 | // ----------------------------------------------------------------------------- 25 | // Tests 26 | // ----------------------------------------------------------------------------- 27 | 28 | const ruleTester = new RuleTester({ parserOptions }); 29 | 30 | ruleTester.run("no-find-dom-node", rule, { 31 | valid: [{ code: "foo()" }], 32 | invalid: [ 33 | { 34 | code: "findDOMNode()", 35 | errors: [{ message }], 36 | }, 37 | { 38 | code: "ReactDOM.findDOMNode()", 39 | errors: [{ message }], 40 | }, 41 | ], 42 | }); 43 | -------------------------------------------------------------------------------- /tests/lib/rules/no-javascript-urls.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileoverview Tests for javascript: urls in JSX rule. 3 | * @author Ron Perris 4 | */ 5 | 6 | "use strict"; 7 | 8 | // ----------------------------------------------------------------------------- 9 | // Requirements 10 | // ----------------------------------------------------------------------------- 11 | 12 | const rule = require("../../../lib/rules/no-javascript-urls"); 13 | const { RuleTester } = require("eslint"); 14 | const message = require("../../../shared/messages")["no-javascript-urls"]; 15 | 16 | const parserOptions = { 17 | ecmaVersion: 2018, 18 | sourceType: "module", 19 | ecmaFeatures: { 20 | jsx: true, 21 | }, 22 | }; 23 | 24 | // ----------------------------------------------------------------------------- 25 | // Tests 26 | // ----------------------------------------------------------------------------- 27 | 28 | const ruleTester = new RuleTester({ parserOptions }); 29 | 30 | ruleTester.run("no-javascript-urls", rule, { 31 | valid: [{ code: '' }], 32 | invalid: [ 33 | { 34 | code: '', 35 | errors: [{ message }], 36 | }, 37 | ], 38 | }); 39 | -------------------------------------------------------------------------------- /tests/lib/rules/no-refs.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileoverview Tests for JSX ref prop usage rule. 3 | * @author Ron Perris 4 | */ 5 | 6 | "use strict"; 7 | 8 | // ----------------------------------------------------------------------------- 9 | // Requirements 10 | // ----------------------------------------------------------------------------- 11 | 12 | const rule = require("../../../lib/rules/no-refs"); 13 | const { RuleTester } = require("eslint"); 14 | const message = require("../../../shared/messages")["no-refs"]; 15 | 16 | const parserOptions = { 17 | ecmaVersion: 2018, 18 | sourceType: "module", 19 | ecmaFeatures: { 20 | jsx: true, 21 | }, 22 | }; 23 | 24 | // ----------------------------------------------------------------------------- 25 | // Tests 26 | // ----------------------------------------------------------------------------- 27 | 28 | const ruleTester = new RuleTester({ parserOptions }); 29 | 30 | ruleTester.run("no-refs", rule, { 31 | valid: [{ code: '
;' }], 32 | invalid: [ 33 | { 34 | code: "
;", 35 | errors: [{ message }], 36 | }, 37 | ], 38 | }); 39 | --------------------------------------------------------------------------------