eslint-plugin-security 1.4.0 → 1.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (66) hide show
  1. package/.eslint-doc-generatorrc.js +9 -0
  2. package/.eslintrc +29 -1
  3. package/.github/ISSUE_TEMPLATE/bug-report.yml +85 -0
  4. package/.github/ISSUE_TEMPLATE/new-rule.yml +39 -0
  5. package/.github/ISSUE_TEMPLATE/rule-change.yml +61 -0
  6. package/.github/workflows/ci.yml +55 -0
  7. package/.github/workflows/pr.yml +19 -0
  8. package/.github/workflows/release-please.yml +39 -0
  9. package/.markdownlint.json +4 -0
  10. package/.markdownlintignore +3 -0
  11. package/.prettierrc.json +7 -0
  12. package/CHANGELOG.md +114 -34
  13. package/README.md +45 -85
  14. package/docs/avoid-command-injection-node.md +85 -0
  15. package/docs/bypass-connect-csrf-protection-by-abusing.md +42 -0
  16. package/docs/regular-expression-dos-and-node.md +83 -0
  17. package/docs/rules/detect-bidi-characters.md +50 -0
  18. package/docs/rules/detect-buffer-noassert.md +9 -0
  19. package/docs/rules/detect-child-process.md +9 -0
  20. package/docs/rules/detect-disable-mustache-escape.md +9 -0
  21. package/docs/rules/detect-eval-with-expression.md +7 -0
  22. package/docs/rules/detect-new-buffer.md +5 -0
  23. package/docs/rules/detect-no-csrf-before-method-override.md +9 -0
  24. package/docs/rules/detect-non-literal-fs-filename.md +7 -0
  25. package/docs/rules/detect-non-literal-regexp.md +7 -0
  26. package/docs/rules/detect-non-literal-require.md +7 -0
  27. package/docs/rules/detect-object-injection.md +7 -0
  28. package/docs/rules/detect-possible-timing-attacks.md +5 -0
  29. package/docs/rules/detect-pseudoRandomBytes.md +5 -0
  30. package/docs/rules/detect-unsafe-regex.md +7 -0
  31. package/docs/the-dangers-of-square-bracket-notation.md +107 -0
  32. package/index.js +10 -9
  33. package/package.json +34 -7
  34. package/rules/detect-bidi-characters.js +101 -0
  35. package/rules/detect-buffer-noassert.js +66 -55
  36. package/rules/detect-child-process.js +57 -25
  37. package/rules/detect-disable-mustache-escape.js +24 -14
  38. package/rules/detect-eval-with-expression.js +19 -9
  39. package/rules/detect-new-buffer.js +19 -16
  40. package/rules/detect-no-csrf-before-method-override.js +32 -25
  41. package/rules/detect-non-literal-fs-filename.js +86 -33
  42. package/rules/detect-non-literal-regexp.js +24 -18
  43. package/rules/detect-non-literal-require.js +25 -17
  44. package/rules/detect-object-injection.js +61 -59
  45. package/rules/detect-possible-timing-attacks.js +40 -42
  46. package/rules/detect-pseudoRandomBytes.js +18 -11
  47. package/rules/detect-unsafe-regex.js +36 -23
  48. package/test/detect-bidi-characters.js +74 -0
  49. package/test/detect-buffer-noassert.js +18 -18
  50. package/test/detect-child-process.js +49 -23
  51. package/test/detect-disable-mustache-escape.js +3 -4
  52. package/test/detect-eval-with-expression.js +4 -5
  53. package/test/detect-new-buffer.js +4 -5
  54. package/test/detect-no-csrf-before-method-override.js +3 -4
  55. package/test/detect-non-literal-fs-filename.js +135 -9
  56. package/test/detect-non-literal-regexp.js +5 -6
  57. package/test/detect-non-literal-require.js +11 -8
  58. package/test/detect-object-injection.js +3 -5
  59. package/test/detect-possible-timing-attacks.js +8 -10
  60. package/test/detect-pseudoRandomBytes.js +3 -4
  61. package/test/detect-unsafe-regexp.js +9 -11
  62. package/test/utils/import-utils.js +172 -0
  63. package/utils/data/fsFunctionData.json +51 -0
  64. package/utils/import-utils.js +196 -0
  65. package/.npmignore +0 -1
  66. package/rules/data/fsFunctionData.json +0 -51
package/index.js CHANGED
@@ -18,7 +18,8 @@ module.exports = {
18
18
  'detect-child-process': require('./rules/detect-child-process'),
19
19
  'detect-disable-mustache-escape': require('./rules/detect-disable-mustache-escape'),
20
20
  'detect-object-injection': require('./rules/detect-object-injection'),
21
- 'detect-new-buffer': require('./rules/detect-new-buffer')
21
+ 'detect-new-buffer': require('./rules/detect-new-buffer'),
22
+ 'detect-bidi-characters': require('./rules/detect-bidi-characters'),
22
23
  },
23
24
  rulesConfig: {
24
25
  'detect-unsafe-regex': 0,
@@ -33,13 +34,12 @@ module.exports = {
33
34
  'detect-child-process': 0,
34
35
  'detect-disable-mustache-escape': 0,
35
36
  'detect-object-injection': 0,
36
- 'detect-new-buffer': 0
37
+ 'detect-new-buffer': 0,
38
+ 'detect-bidi-characters': 0,
37
39
  },
38
40
  configs: {
39
41
  recommended: {
40
- plugins: [
41
- 'security'
42
- ],
42
+ plugins: ['security'],
43
43
  rules: {
44
44
  'security/detect-buffer-noassert': 'warn',
45
45
  'security/detect-child-process': 'warn',
@@ -53,8 +53,9 @@ module.exports = {
53
53
  'security/detect-object-injection': 'warn',
54
54
  'security/detect-possible-timing-attacks': 'warn',
55
55
  'security/detect-pseudoRandomBytes': 'warn',
56
- 'security/detect-unsafe-regex': 'warn'
57
- }
58
- }
59
- }
56
+ 'security/detect-unsafe-regex': 'warn',
57
+ 'security/detect-bidi-characters': 'warn',
58
+ },
59
+ },
60
+ },
60
61
  };
package/package.json CHANGED
@@ -1,13 +1,20 @@
1
1
  {
2
2
  "name": "eslint-plugin-security",
3
- "version": "1.4.0",
3
+ "version": "1.6.0",
4
4
  "description": "Security rules for eslint",
5
5
  "main": "index.js",
6
6
  "scripts": {
7
7
  "changelog": "changelog eslint-plugin-security all > CHANGELOG.md",
8
- "test": "./node_modules/.bin/mocha test/**/*",
9
- "lint": "./node_modules/.bin/eslint .",
10
- "cont-int": "npm test && npm run-script lint"
8
+ "cont-int": "npm test && npm run lint",
9
+ "format": "prettier --write .",
10
+ "lint": "npm-run-all \"lint:*\"",
11
+ "lint:docs": "markdownlint \"**/*.md\"",
12
+ "lint:eslint-docs": "npm run update:eslint-docs -- --check",
13
+ "lint:js": "eslint .",
14
+ "lint:js:fix": "npm run lint:js -- --fix",
15
+ "release": "npx semantic-release",
16
+ "test": "mocha test/**",
17
+ "update:eslint-docs": "eslint-doc-generator"
11
18
  },
12
19
  "repository": {
13
20
  "type": "git",
@@ -24,13 +31,33 @@
24
31
  "url": "https://github.com/nodesecurity/eslint-plugin-security/issues"
25
32
  },
26
33
  "homepage": "https://github.com/nodesecurity/eslint-plugin-security#readme",
34
+ "gitHooks": {
35
+ "pre-commit": "lint-staged"
36
+ },
37
+ "lint-staged": {
38
+ "*.js": [
39
+ "prettier --write",
40
+ "eslint --fix"
41
+ ],
42
+ "*.md": "prettier --write",
43
+ "*.yml": "prettier --write"
44
+ },
27
45
  "dependencies": {
28
- "safe-regex": "^1.1.0"
46
+ "safe-regex": "^2.1.1"
29
47
  },
30
48
  "devDependencies": {
31
49
  "changelog": "1.3.0",
32
- "eslint": "^2.10.1",
50
+ "eslint": "^8.11.0",
33
51
  "eslint-config-nodesecurity": "^1.3.1",
34
- "mocha": "^2.4.5"
52
+ "eslint-config-prettier": "^8.5.0",
53
+ "eslint-doc-generator": "^1.0.2",
54
+ "eslint-plugin-eslint-plugin": "^5.0.2",
55
+ "lint-staged": "^12.3.7",
56
+ "markdownlint-cli": "^0.32.2",
57
+ "mocha": "^9.2.2",
58
+ "npm-run-all": "^4.1.5",
59
+ "prettier": "^2.6.2",
60
+ "semantic-release": "^19.0.2",
61
+ "yorkie": "^2.0.0"
35
62
  }
36
63
  }
@@ -0,0 +1,101 @@
1
+ /**
2
+ * Detect trojan source attacks that employ unicode bidi attacks to inject malicious code
3
+ * @author Luciamo Mammino
4
+ * @author Simone Sanfratello
5
+ * @author Liran Tal
6
+ */
7
+
8
+ 'use strict';
9
+
10
+ const dangerousBidiCharsRegexp = /[\u061C\u200E\u200F\u202A\u202B\u202C\u202D\u202E\u2066\u2067\u2068\u2069]/gu;
11
+
12
+ /**
13
+ * Detects all the dangerous bidi characters in a given source text
14
+ *
15
+ * @param {object} options - Options
16
+ * @param {string} options.sourceText - The source text to search for dangerous bidi characters
17
+ * @param {number} options.firstLineOffset - The offset of the first line in the source text
18
+ * @returns {Array<{line: number, column: number}>} - An array of reports, each report is an
19
+ * object with the line and column of the dangerous character
20
+ */
21
+ function detectBidiCharacters({ sourceText, firstLineOffset }) {
22
+ const sourceTextToSearch = sourceText.toString();
23
+
24
+ const lines = sourceTextToSearch.split(/\r?\n/);
25
+
26
+ return lines.reduce((reports, line, lineIndex) => {
27
+ let match;
28
+ let offset = lineIndex == 0 ? firstLineOffset : 0;
29
+
30
+ while ((match = dangerousBidiCharsRegexp.exec(line)) !== null) {
31
+ reports.push({ line: lineIndex, column: offset + match.index });
32
+ }
33
+
34
+ return reports;
35
+ }, []);
36
+ }
37
+
38
+ function report({ context, node, tokens, message, firstLineOffset }) {
39
+ if (!tokens || !Array.isArray(tokens)) {
40
+ return;
41
+ }
42
+ tokens.forEach((token) => {
43
+ const reports = detectBidiCharacters({ sourceText: token.value, firstLineOffset: token.loc.start.column + firstLineOffset });
44
+
45
+ reports.forEach((report) => {
46
+ context.report({
47
+ node: node,
48
+ data: {
49
+ text: token.value,
50
+ },
51
+ loc: {
52
+ start: {
53
+ line: token.loc.start.line + report.line,
54
+ column: report.column,
55
+ },
56
+ end: {
57
+ line: token.loc.start.line + report.line,
58
+ column: report.column + 1,
59
+ },
60
+ },
61
+ message,
62
+ });
63
+ });
64
+ });
65
+ }
66
+
67
+ //------------------------------------------------------------------------------
68
+ // Rule Definition
69
+ //------------------------------------------------------------------------------
70
+
71
+ module.exports = {
72
+ meta: {
73
+ type: 'error',
74
+ docs: {
75
+ description: 'Detects trojan source attacks that employ unicode bidi attacks to inject malicious code.',
76
+ category: 'Possible Security Vulnerability',
77
+ recommended: true,
78
+ url: 'https://github.com/eslint-community/eslint-plugin-security/blob/main/docs/rules/detect-bidi-characters.md',
79
+ },
80
+ },
81
+ create: function (context) {
82
+ return {
83
+ Program: function (node) {
84
+ report({
85
+ context,
86
+ node,
87
+ tokens: node.tokens,
88
+ firstLineOffset: 0,
89
+ message: "Detected potential trojan source attack with unicode bidi introduced in this code: '{{text}}'.",
90
+ });
91
+ report({
92
+ context,
93
+ node,
94
+ tokens: node.comments,
95
+ firstLineOffset: 2,
96
+ message: "Detected potential trojan source attack with unicode bidi introduced in this comment: '{{text}}'.",
97
+ });
98
+ },
99
+ };
100
+ },
101
+ };
@@ -1,69 +1,80 @@
1
1
  /**
2
2
  * Tries to detect buffer read / write calls that use noAssert set to true
3
- * @author Adam Baldwin
3
+ * @author Adam Baldwin
4
4
  */
5
5
 
6
- //------------------------------------------------------------------------------
7
- // Rule Definition
8
- //------------------------------------------------------------------------------
6
+ 'use strict';
9
7
 
10
- var names = [];
8
+ //-----------------------------------------------------------------------------
9
+ // Helpers
10
+ //-----------------------------------------------------------------------------
11
11
 
12
- module.exports = function(context) {
12
+ const read = [
13
+ 'readUInt8',
14
+ 'readUInt16LE',
15
+ 'readUInt16BE',
16
+ 'readUInt32LE',
17
+ 'readUInt32BE',
18
+ 'readInt8',
19
+ 'readInt16LE',
20
+ 'readInt16BE',
21
+ 'readInt32LE',
22
+ 'readInt32BE',
23
+ 'readFloatLE',
24
+ 'readFloatBE',
25
+ 'readDoubleLE',
26
+ 'readDoubleBE',
27
+ ];
13
28
 
14
- "use strict";
29
+ const write = [
30
+ 'writeUInt8',
31
+ 'writeUInt16LE',
32
+ 'writeUInt16BE',
33
+ 'writeUInt32LE',
34
+ 'writeUInt32BE',
35
+ 'writeInt8',
36
+ 'writeInt16LE',
37
+ 'writeInt16BE',
38
+ 'writeInt32LE',
39
+ 'writeInt32BE',
40
+ 'writeFloatLE',
41
+ 'writeFloatBE',
42
+ 'writeDoubleLE',
43
+ 'writeDoubleBE',
44
+ ];
15
45
 
16
- var read = [
17
- "readUInt8",
18
- "readUInt16LE",
19
- "readUInt16BE",
20
- "readUInt32LE",
21
- "readUInt32BE",
22
- "readInt8",
23
- "readInt16LE",
24
- "readInt16BE",
25
- "readInt32LE",
26
- "readInt32BE",
27
- "readFloatLE",
28
- "readFloatBE",
29
- "readDoubleL",
30
- "readDoubleBE"
31
- ];
32
-
33
- var write = [
34
- "writeUInt8",
35
- "writeUInt16LE",
36
- "writeUInt16BE",
37
- "writeUInt32LE",
38
- "writeUInt32BE",
39
- "writeInt8",
40
- "writeInt16LE",
41
- "writeInt16BE",
42
- "writeInt32LE",
43
- "writeInt32BE",
44
- "writeFloatLE",
45
- "writeFloatBE",
46
- "writeDoubleLE",
47
- "writeDoubleBE"
48
- ];
46
+ //------------------------------------------------------------------------------
47
+ // Rule Definition
48
+ //------------------------------------------------------------------------------
49
49
 
50
+ module.exports = {
51
+ meta: {
52
+ type: 'error',
53
+ docs: {
54
+ description: 'Detects calls to "buffer" with "noAssert" flag set.',
55
+ category: 'Possible Security Vulnerability',
56
+ recommended: true,
57
+ url: 'https://github.com/eslint-community/eslint-plugin-security/blob/main/docs/rules/detect-buffer-noassert.md',
58
+ },
59
+ __methodsToCheck: {
60
+ read,
61
+ write,
62
+ },
63
+ },
64
+ create: function (context) {
50
65
  return {
51
- "MemberExpression": function (node) {
52
- var index;
53
- if (read.indexOf(node.property.name) !== -1) {
54
- index = 1;
55
- } else if (write.indexOf(node.property.name) !== -1) {
56
- index = 2;
57
- }
58
-
59
- if (index && node.parent && node.parent.arguments && node.parent.arguments[index] && node.parent.arguments[index].value) {
60
- var token = context.getTokens(node)[0];
61
- return context.report(node, 'Found Buffer.' + node.property.name + ' with noAssert flag set true');
62
-
63
- }
66
+ MemberExpression: function (node) {
67
+ let index;
68
+ if (read.indexOf(node.property.name) !== -1) {
69
+ index = 1;
70
+ } else if (write.indexOf(node.property.name) !== -1) {
71
+ index = 2;
64
72
  }
65
73
 
74
+ if (index && node.parent && node.parent.arguments && node.parent.arguments[index] && node.parent.arguments[index].value) {
75
+ return context.report({ node: node, message: `Found Buffer.${node.property.name} with noAssert flag set true` });
76
+ }
77
+ },
66
78
  };
67
-
79
+ },
68
80
  };
69
-
@@ -3,40 +3,72 @@
3
3
  * @author Adam Baldwin
4
4
  */
5
5
 
6
+ 'use strict';
7
+
6
8
  //------------------------------------------------------------------------------
7
9
  // Rule Definition
8
10
  //------------------------------------------------------------------------------
9
11
 
10
- var names = [];
11
-
12
- module.exports = function(context) {
12
+ module.exports = {
13
+ meta: {
14
+ type: 'error',
15
+ docs: {
16
+ description: 'Detects instances of "child_process" & non-literal "exec()" calls.',
17
+ category: 'Possible Security Vulnerability',
18
+ recommended: true,
19
+ url: 'https://github.com/eslint-community/eslint-plugin-security/blob/main/docs/rules/detect-child-process.md',
20
+ },
21
+ },
22
+ create: function (context) {
23
+ /*
24
+ * Stores variable identifiers pointing to child_process to check (child_process).exec()
25
+ */
26
+ const childProcessIdentifiers = new Set();
13
27
 
14
- "use strict";
28
+ /**
29
+ * Extract identifiers assigned the expression `require("child_process")`.
30
+ * @param {Pattern} node
31
+ */
32
+ function extractChildProcessIdentifiers(node) {
33
+ if (node.type !== 'Identifier') {
34
+ return;
35
+ }
36
+ const variable = context.getScope().set.get(node.name);
37
+ if (!variable) {
38
+ return;
39
+ }
40
+ for (const reference of variable.references) {
41
+ childProcessIdentifiers.add(reference.identifier);
42
+ }
43
+ }
15
44
 
16
45
  return {
17
- "CallExpression": function (node) {
18
- var token = context.getTokens(node)[0];
19
- if (node.callee.name === 'require') {
20
- var args = node.arguments[0];
21
- if (args && args.type === 'Literal' && args.value === 'child_process') {
22
- if (node.parent.type === 'VariableDeclarator') {
23
- names.push(node.parent.id.name);
24
- } else if (node.parent.type === 'AssignmentExpression' && node.parent.operator === '=') {
25
- names.push(node.parent.left.name);
26
- }
27
- return context.report(node, 'Found require("child_process")');
28
- }
46
+ CallExpression: function (node) {
47
+ if (node.callee.name === 'require') {
48
+ const args = node.arguments[0];
49
+ if (args && args.type === 'Literal' && args.value === 'child_process') {
50
+ let pattern;
51
+ if (node.parent.type === 'VariableDeclarator') {
52
+ pattern = node.parent.id;
53
+ } else if (node.parent.type === 'AssignmentExpression' && node.parent.operator === '=') {
54
+ pattern = node.parent.left;
55
+ }
56
+ if (pattern) {
57
+ extractChildProcessIdentifiers(pattern);
29
58
  }
30
- },
31
- "MemberExpression": function (node) {
32
- var token = context.getTokens(node)[0];
33
- if (node.property.name === 'exec' && names.indexOf(node.object.name) > -1) {
34
- if (node.parent && node.parent.arguments && node.parent.arguments[0].type !== 'Literal') {
35
- return context.report(node, 'Found child_process.exec() with non Literal first argument');
36
- }
59
+ if (!pattern || pattern.type === 'Identifier') {
60
+ return context.report({ node: node, message: 'Found require("child_process")' });
37
61
  }
62
+ }
38
63
  }
39
-
64
+ },
65
+ MemberExpression: function (node) {
66
+ if (node.property.name === 'exec' && childProcessIdentifiers.has(node.object)) {
67
+ if (node.parent && node.parent.arguments && node.parent.arguments.length && node.parent.arguments[0].type !== 'Literal') {
68
+ return context.report({ node: node, message: 'Found child_process.exec() with non Literal first argument' });
69
+ }
70
+ }
71
+ },
40
72
  };
41
-
73
+ },
42
74
  };
@@ -1,18 +1,28 @@
1
- module.exports = function(context) {
1
+ 'use strict';
2
2
 
3
- "use strict";
3
+ module.exports = {
4
+ meta: {
5
+ type: 'error',
6
+ docs: {
7
+ description: 'Detects "object.escapeMarkup = false", which can be used with some template engines to disable escaping of HTML entities.',
8
+ category: 'Possible Security Vulnerability',
9
+ recommended: true,
10
+ url: 'https://github.com/eslint-community/eslint-plugin-security/blob/main/docs/rules/detect-disable-mustache-escape.md',
11
+ },
12
+ },
13
+ create: function (context) {
4
14
  return {
5
- "AssignmentExpression": function(node) {
6
- if (node.operator === '=') {
7
- if (node.left.property) {
8
- if (node.left.property.name == 'escapeMarkup') {
9
- if (node.right.value == false) {
10
- context.report(node, 'Markup escaping disabled.')
11
- }
12
- }
13
- }
15
+ AssignmentExpression: function (node) {
16
+ if (node.operator === '=') {
17
+ if (node.left.property) {
18
+ if (node.left.property.name === 'escapeMarkup') {
19
+ if (node.right.value === false) {
20
+ context.report({ node: node, message: 'Markup escaping disabled.' });
21
+ }
14
22
  }
23
+ }
15
24
  }
16
- }
17
-
18
- }
25
+ },
26
+ };
27
+ },
28
+ };
@@ -1,21 +1,31 @@
1
1
  /**
2
- * Idnetifies eval with expression
2
+ * Identifies eval with expression
3
3
  * @author Adam Baldwin
4
4
  */
5
5
 
6
+ 'use strict';
7
+
6
8
  //------------------------------------------------------------------------------
7
9
  // Rule Definition
8
10
  //------------------------------------------------------------------------------
9
11
 
10
- module.exports = function(context) {
11
-
12
- "use strict";
13
-
12
+ module.exports = {
13
+ meta: {
14
+ type: 'error',
15
+ docs: {
16
+ description: 'Detects "eval(variable)" which can allow an attacker to run arbitrary code inside your process.',
17
+ category: 'Possible Security Vulnerability',
18
+ recommended: true,
19
+ url: 'https://github.com/eslint-community/eslint-plugin-security/blob/main/docs/rules/detect-eval-with-expression.md',
20
+ },
21
+ },
22
+ create: function (context) {
14
23
  return {
15
- "CallExpression": function(node) {
16
- if (node.callee.name === "eval" && node.arguments[0].type !== 'Literal') {
17
- context.report(node, "eval with argument of type " + node.arguments[0].type);
18
- }
24
+ CallExpression: function (node) {
25
+ if (node.callee.name === 'eval' && node.arguments[0].type !== 'Literal') {
26
+ context.report({ node: node, message: `eval with argument of type ${node.arguments[0].type}` });
19
27
  }
28
+ },
20
29
  };
30
+ },
21
31
  };
@@ -1,19 +1,22 @@
1
- module.exports = function (context) {
2
- // Detects instances of new Buffer(argument)
3
- // where argument is any non literal value.
4
- return {
5
- "NewExpression": function (node) {
6
- if (node.callee.name === 'Buffer' &&
7
- node.arguments[0] &&
8
- node.arguments[0].type != 'Literal') {
1
+ 'use strict';
9
2
 
10
- return context.report(node, "Found new Buffer");
3
+ module.exports = {
4
+ meta: {
5
+ type: 'error',
6
+ docs: {
7
+ description: 'Detects instances of new Buffer(argument) where argument is any non-literal value.',
8
+ category: 'Possible Security Vulnerability',
9
+ recommended: true,
10
+ url: 'https://github.com/eslint-community/eslint-plugin-security/blob/main/docs/rules/detect-new-buffer.md',
11
+ },
12
+ },
13
+ create: function (context) {
14
+ return {
15
+ NewExpression: function (node) {
16
+ if (node.callee.name === 'Buffer' && node.arguments[0] && node.arguments[0].type !== 'Literal') {
17
+ return context.report({ node: node, message: 'Found new Buffer' });
11
18
  }
12
-
13
-
14
-
15
- }
19
+ },
16
20
  };
17
-
18
- }
19
-
21
+ },
22
+ };
@@ -3,37 +3,44 @@
3
3
  * @author Adam Baldwin
4
4
  */
5
5
 
6
+ 'use strict';
7
+
6
8
  //------------------------------------------------------------------------------
7
9
  // Rule Definition
8
10
  //------------------------------------------------------------------------------
9
11
 
10
-
11
- module.exports = function(context) {
12
-
13
- "use strict";
14
- var csrf = false;
12
+ module.exports = {
13
+ meta: {
14
+ type: 'error',
15
+ docs: {
16
+ description: 'Detects Express "csrf" middleware setup before "method-override" middleware.',
17
+ category: 'Possible Security Vulnerability',
18
+ recommended: true,
19
+ url: 'https://github.com/eslint-community/eslint-plugin-security/blob/main/docs/rules/detect-no-csrf-before-method-override.md',
20
+ },
21
+ },
22
+ create: function (context) {
23
+ let csrf = false;
15
24
 
16
25
  return {
17
- "CallExpression": function(node) {
18
- var token = context.getTokens(node)[0],
19
- nodeType = token.type,
20
- nodeValue = token.value;
21
-
22
- if (nodeValue === "express") {
23
- if (!node.callee || !node.callee.property) {
24
- return;
25
- }
26
-
27
- if (node.callee.property.name === "methodOverride" && csrf) {
28
- context.report(node, "express.csrf() middleware found before express.methodOverride()");
29
- }
30
- if (node.callee.property.name === "csrf") {
31
- // Keep track of found CSRF
32
- csrf = true;
33
- }
34
- }
26
+ CallExpression: function (node) {
27
+ const token = context.getSourceCode().getTokens(node)[0];
28
+ const nodeValue = token.value;
29
+
30
+ if (nodeValue === 'express') {
31
+ if (!node.callee || !node.callee.property) {
32
+ return;
33
+ }
34
+
35
+ if (node.callee.property.name === 'methodOverride' && csrf) {
36
+ context.report({ node: node, message: 'express.csrf() middleware found before express.methodOverride()' });
37
+ }
38
+ if (node.callee.property.name === 'csrf') {
39
+ // Keep track of found CSRF
40
+ csrf = true;
41
+ }
35
42
  }
43
+ },
36
44
  };
37
-
45
+ },
38
46
  };
39
-