eslint-plugin-security 1.5.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.
- package/.eslint-doc-generatorrc.js +9 -0
- package/.eslintrc +23 -5
- package/.github/ISSUE_TEMPLATE/bug-report.yml +85 -0
- package/.github/ISSUE_TEMPLATE/new-rule.yml +39 -0
- package/.github/ISSUE_TEMPLATE/rule-change.yml +61 -0
- package/.github/workflows/ci.yml +13 -5
- package/.github/workflows/pr.yml +6 -2
- package/.github/workflows/release-please.yml +39 -0
- package/.markdownlint.json +4 -0
- package/.markdownlintignore +3 -0
- package/CHANGELOG.md +54 -5
- package/README.md +39 -81
- package/docs/bypass-connect-csrf-protection-by-abusing.md +3 -3
- package/docs/rules/detect-bidi-characters.md +50 -0
- package/docs/rules/detect-buffer-noassert.md +9 -0
- package/docs/rules/detect-child-process.md +9 -0
- package/docs/rules/detect-disable-mustache-escape.md +9 -0
- package/docs/rules/detect-eval-with-expression.md +7 -0
- package/docs/rules/detect-new-buffer.md +5 -0
- package/docs/rules/detect-no-csrf-before-method-override.md +9 -0
- package/docs/rules/detect-non-literal-fs-filename.md +7 -0
- package/docs/rules/detect-non-literal-regexp.md +7 -0
- package/docs/rules/detect-non-literal-require.md +7 -0
- package/docs/rules/detect-object-injection.md +7 -0
- package/docs/rules/detect-possible-timing-attacks.md +5 -0
- package/docs/rules/detect-pseudoRandomBytes.md +5 -0
- package/docs/rules/detect-unsafe-regex.md +7 -0
- package/docs/the-dangers-of-square-bracket-notation.md +9 -9
- package/index.js +9 -6
- package/package.json +14 -6
- package/rules/detect-bidi-characters.js +101 -0
- package/rules/detect-buffer-noassert.js +3 -3
- package/rules/detect-child-process.js +36 -13
- package/rules/detect-disable-mustache-escape.js +5 -5
- package/rules/detect-eval-with-expression.js +5 -5
- package/rules/detect-new-buffer.js +6 -6
- package/rules/detect-no-csrf-before-method-override.js +3 -3
- package/rules/detect-non-literal-fs-filename.js +71 -23
- package/rules/detect-non-literal-regexp.js +2 -2
- package/rules/detect-non-literal-require.js +3 -3
- package/rules/detect-object-injection.js +4 -4
- package/rules/detect-possible-timing-attacks.js +6 -6
- package/rules/detect-pseudoRandomBytes.js +2 -2
- package/rules/detect-unsafe-regex.js +8 -8
- package/test/detect-bidi-characters.js +74 -0
- package/test/detect-child-process.js +37 -1
- package/test/detect-disable-mustache-escape.js +3 -3
- package/test/detect-eval-with-expression.js +4 -4
- package/test/detect-new-buffer.js +4 -4
- package/test/detect-no-csrf-before-method-override.js +3 -3
- package/test/detect-non-literal-fs-filename.js +135 -8
- package/test/detect-non-literal-regexp.js +5 -5
- package/test/detect-non-literal-require.js +5 -5
- package/test/detect-object-injection.js +3 -3
- package/test/detect-possible-timing-attacks.js +8 -8
- package/test/detect-pseudoRandomBytes.js +3 -3
- package/test/detect-unsafe-regexp.js +8 -8
- package/test/utils/import-utils.js +172 -0
- package/{rules → utils}/data/fsFunctionData.json +0 -0
- package/utils/import-utils.js +196 -0
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# Detects instances of "child_process" & non-literal "exec()" calls (`security/detect-child-process`)
|
|
2
|
+
|
|
3
|
+
⚠️ This rule _warns_ in the ✅ `recommended` config.
|
|
4
|
+
|
|
5
|
+
<!-- end auto-generated rule header -->
|
|
6
|
+
|
|
7
|
+
Detect instances of [`child_process`](https://nodejs.org/api/child_process.html) & non-literal [`exec()`](https://nodejs.org/api/child_process.html#child_process_child_process_exec_command_options_callback)
|
|
8
|
+
|
|
9
|
+
More information: [Avoiding Command Injection in Node.js](../avoid-command-injection-node.md)
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# Detects "object.escapeMarkup = false", which can be used with some template engines to disable escaping of HTML entities (`security/detect-disable-mustache-escape`)
|
|
2
|
+
|
|
3
|
+
⚠️ This rule _warns_ in the ✅ `recommended` config.
|
|
4
|
+
|
|
5
|
+
<!-- end auto-generated rule header -->
|
|
6
|
+
|
|
7
|
+
This can lead to Cross-Site Scripting (XSS) vulnerabilities.
|
|
8
|
+
|
|
9
|
+
More information: [OWASP XSS](<https://www.owasp.org/index.php/Cross-site_Scripting_(XSS)>)
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
# Detects "eval(variable)" which can allow an attacker to run arbitrary code inside your process (`security/detect-eval-with-expression`)
|
|
2
|
+
|
|
3
|
+
⚠️ This rule _warns_ in the ✅ `recommended` config.
|
|
4
|
+
|
|
5
|
+
<!-- end auto-generated rule header -->
|
|
6
|
+
|
|
7
|
+
More information: [What are the security issues with eval in JavaScript?](http://security.stackexchange.com/questions/94017/what-are-the-security-issues-with-eval-in-javascript)
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# Detects Express "csrf" middleware setup before "method-override" middleware (`security/detect-no-csrf-before-method-override`)
|
|
2
|
+
|
|
3
|
+
⚠️ This rule _warns_ in the ✅ `recommended` config.
|
|
4
|
+
|
|
5
|
+
<!-- end auto-generated rule header -->
|
|
6
|
+
|
|
7
|
+
This can allow `GET` requests (which are not checked by `csrf`) to turn into `POST` requests later.
|
|
8
|
+
|
|
9
|
+
More information: [Bypass Connect CSRF protection by abusing methodOverride Middleware](../bypass-connect-csrf-protection-by-abusing.md)
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
# Detects variable in filename argument of "fs" calls, which might allow an attacker to access anything on your system (`security/detect-non-literal-fs-filename`)
|
|
2
|
+
|
|
3
|
+
⚠️ This rule _warns_ in the ✅ `recommended` config.
|
|
4
|
+
|
|
5
|
+
<!-- end auto-generated rule header -->
|
|
6
|
+
|
|
7
|
+
More information: [OWASP Path Traversal](https://www.owasp.org/index.php/Path_Traversal)
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
# Detects "RegExp(variable)", which might allow an attacker to DOS your server with a long-running regular expression (`security/detect-non-literal-regexp`)
|
|
2
|
+
|
|
3
|
+
⚠️ This rule _warns_ in the ✅ `recommended` config.
|
|
4
|
+
|
|
5
|
+
<!-- end auto-generated rule header -->
|
|
6
|
+
|
|
7
|
+
More information: [Regular Expression DoS and Node.js](../regular-expression-dos-and-node.md)
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
# Detects "require(variable)", which might allow an attacker to load and run arbitrary code, or access arbitrary files on disk (`security/detect-non-literal-require`)
|
|
2
|
+
|
|
3
|
+
⚠️ This rule _warns_ in the ✅ `recommended` config.
|
|
4
|
+
|
|
5
|
+
<!-- end auto-generated rule header -->
|
|
6
|
+
|
|
7
|
+
More information: [Where does Node.js and require look for modules?](http://www.bennadel.com/blog/2169-where-does-node-js-and-require-look-for-modules.htm)
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
# Detects "variable[key]" as a left- or right-hand assignment operand (`security/detect-object-injection`)
|
|
2
|
+
|
|
3
|
+
⚠️ This rule _warns_ in the ✅ `recommended` config.
|
|
4
|
+
|
|
5
|
+
<!-- end auto-generated rule header -->
|
|
6
|
+
|
|
7
|
+
More information: [The Dangers of Square Bracket Notation](../the-dangers-of-square-bracket-notation.md)
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
# Detects potentially unsafe regular expressions, which may take a very long time to run, blocking the event loop (`security/detect-unsafe-regex`)
|
|
2
|
+
|
|
3
|
+
⚠️ This rule _warns_ in the ✅ `recommended` config.
|
|
4
|
+
|
|
5
|
+
<!-- end auto-generated rule header -->
|
|
6
|
+
|
|
7
|
+
More information: [Regular Expression DoS and Node.js](../regular-expression-dos-and-node.md)
|
|
@@ -8,7 +8,7 @@ _Note: These examples are simple, and seemingly obvious - we will take a look at
|
|
|
8
8
|
|
|
9
9
|
Let's take a look at why this could be a problem.
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
## Issue #1: Bracket object notation with user input grants access to every property available on the object
|
|
12
12
|
|
|
13
13
|
```js
|
|
14
14
|
exampleClass[userInput[1]] = userInput[2];
|
|
@@ -16,7 +16,7 @@ exampleClass[userInput[1]] = userInput[2];
|
|
|
16
16
|
|
|
17
17
|
I won't spend much time here, as I believe this is fairly well known. If exampleClass contains a sensitive property, the above code will allow it to be edited.
|
|
18
18
|
|
|
19
|
-
|
|
19
|
+
## Issue #2: Bracket object notation with user input grants access to every property available on the object, **_including prototypes._**
|
|
20
20
|
|
|
21
21
|
```js
|
|
22
22
|
userInput = ['constructor', '{}'];
|
|
@@ -25,7 +25,7 @@ exampleClass[userInput[1]] = userInput[2];
|
|
|
25
25
|
|
|
26
26
|
This looks pretty innocuous, even if it is an uncommon pattern. The problem here is that we can access or overwrite prototypes such as `constructor` or `__defineGetter__`, which may be used later on. The most likely outcome of this scenario would be an application crash, when a string is attempted to be called as a function.
|
|
27
27
|
|
|
28
|
-
|
|
28
|
+
## Issue #3: Bracket object notation with user input grants access to every property available on the object, including prototypes, **_which can lead to Remote Code Execution._**
|
|
29
29
|
|
|
30
30
|
Now here's where things get really dangerous. It's also where example code gets really implausible - bear with me.
|
|
31
31
|
|
|
@@ -43,7 +43,7 @@ function handler(userInput) {
|
|
|
43
43
|
|
|
44
44
|
In the previous section, I mentioned that constructor can be accessed from square brackets. In this case, since we are dealing with a function, the constructor we get back is the `Function` Constructor, which compiles a string of code into a function.
|
|
45
45
|
|
|
46
|
-
|
|
46
|
+
## Exploitation
|
|
47
47
|
|
|
48
48
|
In order to exploit the above code, we need a two stage exploit function.
|
|
49
49
|
|
|
@@ -84,11 +84,11 @@ user.anyVal = user.anyVal('date');
|
|
|
84
84
|
|
|
85
85
|
What we end up with is this:
|
|
86
86
|
|
|
87
|
-

|
|
87
|
+

|
|
88
88
|
|
|
89
89
|
Remote Code Execution. The biggest problem here is that there is very little indication in the code that this is what is going on. With something so serious, method calls tend to be very explicit - eval, child_process, etc. It's pretty difficult in node to accidentally introduce one of those into your application. Here though, without having either deep knowledge of JavaScript builtins or having done previous research, it is very easy to accidentally introduce this into your application.
|
|
90
90
|
|
|
91
|
-
|
|
91
|
+
## Isn't this so obscure that it doesn't matter a whole lot?
|
|
92
92
|
|
|
93
93
|
Well, yes and no. Is this particular vector a widespread problem? No, because current JavaScript style guides don't advocate programming this way. Might it become a widespread problem in the future? Absolutely. This pattern is avoided because it isn't common, and therefore not learned and taken up as habit, not because it's a known insecure pattern.
|
|
94
94
|
|
|
@@ -96,12 +96,12 @@ Yes, we are talking about some fairly extreme edge cases, but don't make the ass
|
|
|
96
96
|
|
|
97
97
|
Edge cases are uncommon, but because they are uncommon the problems with them are not well known, and they frequently go un-noticed during code review. If the code works, these types of problems tend to disappear. If the code works, and the problems are buried in a module nested n-levels deep, it's likely it won't be found until it causes problems, and by then it's too late. A blind require is essentially running untrusted code in your application. Be [aware of what you require.](https://requiresafe.com)
|
|
98
98
|
|
|
99
|
-
|
|
99
|
+
## How do I fix it?
|
|
100
100
|
|
|
101
101
|
The most direct fix here is going to be to **avoid the use of user input in property name fields**. This isn't reasonable in all circumstances, however, and there should be a way to safely use core language features.
|
|
102
102
|
|
|
103
|
-
Another option is to create a
|
|
103
|
+
Another option is to create a allowlist of allowed property names, and filter each user input through a helper function to check before allowing it to be used. This is a great option in situations where you know specifically what property names to allow.
|
|
104
104
|
|
|
105
|
-
In cases where you don't have a strictly defined data model ( which isn't ideal, but there are cases where it has to be so ) then using the same method as above, but with a
|
|
105
|
+
In cases where you don't have a strictly defined data model ( which isn't ideal, but there are cases where it has to be so ) then using the same method as above, but with a denylist of disallowed properties instead is a valid choice.
|
|
106
106
|
|
|
107
107
|
If you are using the `--harmony` flag or [io.js](https://iojs.org/), you also have the option of using [ECMAScript 6 direct proxies](http://wiki.ecmascript.org/doku.php?id=harmony:direct_proxies), which can stand in front of your real object ( private API ) and expose a limited subset of the object ( public API ). This is probably the best approach if you are using this pattern, as it is most consistent with typical object oriented programming paradigms.
|
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,7 +34,8 @@ 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: {
|
|
@@ -51,8 +53,9 @@ module.exports = {
|
|
|
51
53
|
'security/detect-object-injection': 'warn',
|
|
52
54
|
'security/detect-possible-timing-attacks': 'warn',
|
|
53
55
|
'security/detect-pseudoRandomBytes': 'warn',
|
|
54
|
-
'security/detect-unsafe-regex': 'warn'
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
56
|
+
'security/detect-unsafe-regex': 'warn',
|
|
57
|
+
'security/detect-bidi-characters': 'warn',
|
|
58
|
+
},
|
|
59
|
+
},
|
|
60
|
+
},
|
|
58
61
|
};
|
package/package.json
CHANGED
|
@@ -1,16 +1,20 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "eslint-plugin-security",
|
|
3
|
-
"version": "1.
|
|
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
|
+
"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",
|
|
8
15
|
"release": "npx semantic-release",
|
|
9
|
-
"test": "mocha test
|
|
10
|
-
"
|
|
11
|
-
"lint": "eslint .",
|
|
12
|
-
"lint:fix": "eslint --fix .",
|
|
13
|
-
"cont-int": "npm test && npm run lint"
|
|
16
|
+
"test": "mocha test/**",
|
|
17
|
+
"update:eslint-docs": "eslint-doc-generator"
|
|
14
18
|
},
|
|
15
19
|
"repository": {
|
|
16
20
|
"type": "git",
|
|
@@ -46,8 +50,12 @@
|
|
|
46
50
|
"eslint": "^8.11.0",
|
|
47
51
|
"eslint-config-nodesecurity": "^1.3.1",
|
|
48
52
|
"eslint-config-prettier": "^8.5.0",
|
|
53
|
+
"eslint-doc-generator": "^1.0.2",
|
|
54
|
+
"eslint-plugin-eslint-plugin": "^5.0.2",
|
|
49
55
|
"lint-staged": "^12.3.7",
|
|
56
|
+
"markdownlint-cli": "^0.32.2",
|
|
50
57
|
"mocha": "^9.2.2",
|
|
58
|
+
"npm-run-all": "^4.1.5",
|
|
51
59
|
"prettier": "^2.6.2",
|
|
52
60
|
"semantic-release": "^19.0.2",
|
|
53
61
|
"yorkie": "^2.0.0"
|
|
@@ -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
|
+
};
|
|
@@ -51,10 +51,10 @@ module.exports = {
|
|
|
51
51
|
meta: {
|
|
52
52
|
type: 'error',
|
|
53
53
|
docs: {
|
|
54
|
-
description: '
|
|
54
|
+
description: 'Detects calls to "buffer" with "noAssert" flag set.',
|
|
55
55
|
category: 'Possible Security Vulnerability',
|
|
56
56
|
recommended: true,
|
|
57
|
-
url: 'https://github.com/
|
|
57
|
+
url: 'https://github.com/eslint-community/eslint-plugin-security/blob/main/docs/rules/detect-buffer-noassert.md',
|
|
58
58
|
},
|
|
59
59
|
__methodsToCheck: {
|
|
60
60
|
read,
|
|
@@ -72,7 +72,7 @@ module.exports = {
|
|
|
72
72
|
}
|
|
73
73
|
|
|
74
74
|
if (index && node.parent && node.parent.arguments && node.parent.arguments[index] && node.parent.arguments[index].value) {
|
|
75
|
-
return context.report(node, `Found Buffer.${node.property.name} with noAssert flag set true`);
|
|
75
|
+
return context.report({ node: node, message: `Found Buffer.${node.property.name} with noAssert flag set true` });
|
|
76
76
|
}
|
|
77
77
|
},
|
|
78
78
|
};
|
|
@@ -9,40 +9,63 @@
|
|
|
9
9
|
// Rule Definition
|
|
10
10
|
//------------------------------------------------------------------------------
|
|
11
11
|
|
|
12
|
-
/*
|
|
13
|
-
* Stores variable names pointing to child_process to check (child_process).exec()
|
|
14
|
-
*/
|
|
15
|
-
const names = [];
|
|
16
|
-
|
|
17
12
|
module.exports = {
|
|
18
13
|
meta: {
|
|
19
14
|
type: 'error',
|
|
20
15
|
docs: {
|
|
21
|
-
description: '
|
|
16
|
+
description: 'Detects instances of "child_process" & non-literal "exec()" calls.',
|
|
22
17
|
category: 'Possible Security Vulnerability',
|
|
23
18
|
recommended: true,
|
|
24
|
-
url: 'https://github.com/
|
|
19
|
+
url: 'https://github.com/eslint-community/eslint-plugin-security/blob/main/docs/rules/detect-child-process.md',
|
|
25
20
|
},
|
|
26
21
|
},
|
|
27
22
|
create: function (context) {
|
|
23
|
+
/*
|
|
24
|
+
* Stores variable identifiers pointing to child_process to check (child_process).exec()
|
|
25
|
+
*/
|
|
26
|
+
const childProcessIdentifiers = new Set();
|
|
27
|
+
|
|
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
|
+
}
|
|
44
|
+
|
|
28
45
|
return {
|
|
29
46
|
CallExpression: function (node) {
|
|
30
47
|
if (node.callee.name === 'require') {
|
|
31
48
|
const args = node.arguments[0];
|
|
32
49
|
if (args && args.type === 'Literal' && args.value === 'child_process') {
|
|
50
|
+
let pattern;
|
|
33
51
|
if (node.parent.type === 'VariableDeclarator') {
|
|
34
|
-
|
|
52
|
+
pattern = node.parent.id;
|
|
35
53
|
} else if (node.parent.type === 'AssignmentExpression' && node.parent.operator === '=') {
|
|
36
|
-
|
|
54
|
+
pattern = node.parent.left;
|
|
55
|
+
}
|
|
56
|
+
if (pattern) {
|
|
57
|
+
extractChildProcessIdentifiers(pattern);
|
|
58
|
+
}
|
|
59
|
+
if (!pattern || pattern.type === 'Identifier') {
|
|
60
|
+
return context.report({ node: node, message: 'Found require("child_process")' });
|
|
37
61
|
}
|
|
38
|
-
return context.report(node, 'Found require("child_process")');
|
|
39
62
|
}
|
|
40
63
|
}
|
|
41
64
|
},
|
|
42
65
|
MemberExpression: function (node) {
|
|
43
|
-
if (node.property.name === 'exec' &&
|
|
44
|
-
if (node.parent && node.parent.arguments.length && node.parent.arguments[0].type !== 'Literal') {
|
|
45
|
-
return context.report(node, 'Found child_process.exec() with non Literal first argument');
|
|
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' });
|
|
46
69
|
}
|
|
47
70
|
}
|
|
48
71
|
},
|
|
@@ -7,8 +7,8 @@ module.exports = {
|
|
|
7
7
|
description: 'Detects "object.escapeMarkup = false", which can be used with some template engines to disable escaping of HTML entities.',
|
|
8
8
|
category: 'Possible Security Vulnerability',
|
|
9
9
|
recommended: true,
|
|
10
|
-
url: 'https://github.com/
|
|
11
|
-
}
|
|
10
|
+
url: 'https://github.com/eslint-community/eslint-plugin-security/blob/main/docs/rules/detect-disable-mustache-escape.md',
|
|
11
|
+
},
|
|
12
12
|
},
|
|
13
13
|
create: function (context) {
|
|
14
14
|
return {
|
|
@@ -17,12 +17,12 @@ module.exports = {
|
|
|
17
17
|
if (node.left.property) {
|
|
18
18
|
if (node.left.property.name === 'escapeMarkup') {
|
|
19
19
|
if (node.right.value === false) {
|
|
20
|
-
context.report(node, 'Markup escaping disabled.');
|
|
20
|
+
context.report({ node: node, message: 'Markup escaping disabled.' });
|
|
21
21
|
}
|
|
22
22
|
}
|
|
23
23
|
}
|
|
24
24
|
}
|
|
25
|
-
}
|
|
25
|
+
},
|
|
26
26
|
};
|
|
27
|
-
}
|
|
27
|
+
},
|
|
28
28
|
};
|
|
@@ -16,16 +16,16 @@ module.exports = {
|
|
|
16
16
|
description: 'Detects "eval(variable)" which can allow an attacker to run arbitrary code inside your process.',
|
|
17
17
|
category: 'Possible Security Vulnerability',
|
|
18
18
|
recommended: true,
|
|
19
|
-
url: 'https://github.com/
|
|
20
|
-
}
|
|
19
|
+
url: 'https://github.com/eslint-community/eslint-plugin-security/blob/main/docs/rules/detect-eval-with-expression.md',
|
|
20
|
+
},
|
|
21
21
|
},
|
|
22
22
|
create: function (context) {
|
|
23
23
|
return {
|
|
24
24
|
CallExpression: function (node) {
|
|
25
25
|
if (node.callee.name === 'eval' && node.arguments[0].type !== 'Literal') {
|
|
26
|
-
context.report(node, `eval with argument of type ${node.arguments[0].type}`);
|
|
26
|
+
context.report({ node: node, message: `eval with argument of type ${node.arguments[0].type}` });
|
|
27
27
|
}
|
|
28
|
-
}
|
|
28
|
+
},
|
|
29
29
|
};
|
|
30
|
-
}
|
|
30
|
+
},
|
|
31
31
|
};
|
|
@@ -4,19 +4,19 @@ module.exports = {
|
|
|
4
4
|
meta: {
|
|
5
5
|
type: 'error',
|
|
6
6
|
docs: {
|
|
7
|
-
description: '
|
|
7
|
+
description: 'Detects instances of new Buffer(argument) where argument is any non-literal value.',
|
|
8
8
|
category: 'Possible Security Vulnerability',
|
|
9
9
|
recommended: true,
|
|
10
|
-
url: 'https://github.com/
|
|
11
|
-
}
|
|
10
|
+
url: 'https://github.com/eslint-community/eslint-plugin-security/blob/main/docs/rules/detect-new-buffer.md',
|
|
11
|
+
},
|
|
12
12
|
},
|
|
13
13
|
create: function (context) {
|
|
14
14
|
return {
|
|
15
15
|
NewExpression: function (node) {
|
|
16
16
|
if (node.callee.name === 'Buffer' && node.arguments[0] && node.arguments[0].type !== 'Literal') {
|
|
17
|
-
return context.report(node, 'Found new Buffer');
|
|
17
|
+
return context.report({ node: node, message: 'Found new Buffer' });
|
|
18
18
|
}
|
|
19
|
-
}
|
|
19
|
+
},
|
|
20
20
|
};
|
|
21
|
-
}
|
|
21
|
+
},
|
|
22
22
|
};
|
|
@@ -16,7 +16,7 @@ module.exports = {
|
|
|
16
16
|
description: 'Detects Express "csrf" middleware setup before "method-override" middleware.',
|
|
17
17
|
category: 'Possible Security Vulnerability',
|
|
18
18
|
recommended: true,
|
|
19
|
-
url: 'https://github.com/
|
|
19
|
+
url: 'https://github.com/eslint-community/eslint-plugin-security/blob/main/docs/rules/detect-no-csrf-before-method-override.md',
|
|
20
20
|
},
|
|
21
21
|
},
|
|
22
22
|
create: function (context) {
|
|
@@ -24,7 +24,7 @@ module.exports = {
|
|
|
24
24
|
|
|
25
25
|
return {
|
|
26
26
|
CallExpression: function (node) {
|
|
27
|
-
const token = context.getTokens(node)[0];
|
|
27
|
+
const token = context.getSourceCode().getTokens(node)[0];
|
|
28
28
|
const nodeValue = token.value;
|
|
29
29
|
|
|
30
30
|
if (nodeValue === 'express') {
|
|
@@ -33,7 +33,7 @@ module.exports = {
|
|
|
33
33
|
}
|
|
34
34
|
|
|
35
35
|
if (node.callee.property.name === 'methodOverride' && csrf) {
|
|
36
|
-
context.report(node, 'express.csrf() middleware found before express.methodOverride()');
|
|
36
|
+
context.report({ node: node, message: 'express.csrf() middleware found before express.methodOverride()' });
|
|
37
37
|
}
|
|
38
38
|
if (node.callee.property.name === 'csrf') {
|
|
39
39
|
// Keep track of found CSRF
|