eslint-plugin-security 1.0.0 → 1.4.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/.eslintrc +3 -0
- package/CHANGELOG.md +34 -0
- package/README.md +92 -13
- package/index.js +26 -2
- package/package.json +9 -3
- package/rules/detect-buffer-noassert.js +1 -6
- package/rules/detect-child-process.js +2 -6
- package/rules/detect-new-buffer.js +19 -0
- package/rules/detect-no-csrf-before-method-override.js +6 -2
- package/rules/detect-non-literal-fs-filename.js +2 -6
- package/rules/detect-non-literal-regexp.js +2 -5
- package/rules/detect-non-literal-require.js +1 -5
- package/rules/detect-object-injection.js +3 -3
- package/rules/detect-possible-timing-attacks.js +2 -6
- package/rules/detect-pseudoRandomBytes.js +1 -5
- package/rules/detect-unsafe-regex.js +9 -5
- package/test/detect-buffer-noassert.js +30 -0
- package/test/detect-child-process.js +35 -0
- package/test/detect-disable-mustache-escape.js +17 -0
- package/test/detect-eval-with-expression.js +17 -0
- package/test/detect-new-buffer.js +18 -0
- package/test/detect-no-csrf-before-method-override.js +17 -0
- package/test/detect-non-literal-fs-filename.js +19 -0
- package/test/detect-non-literal-regexp.js +18 -0
- package/test/detect-non-literal-require.js +18 -0
- package/test/detect-object-injection.js +47 -0
- package/test/detect-possible-timing-attacks.js +36 -0
- package/test/detect-pseudoRandomBytes.js +18 -0
- package/test/detect-unsafe-regexp.js +29 -0
package/.eslintrc
ADDED
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
1.4.0 / 2017-06-12
|
|
2
|
+
==================
|
|
3
|
+
|
|
4
|
+
* Add recommended ruleset to the usage example
|
|
5
|
+
* Removes filenames from error output
|
|
6
|
+
|
|
7
|
+
1.3.0 / 2017-02-09
|
|
8
|
+
==================
|
|
9
|
+
|
|
10
|
+
* README.md - document detect-disable-mustache-escape rule
|
|
11
|
+
* README.md - documentation detect-new-buffer rule
|
|
12
|
+
* Fixed crash with `detect-no-csrf-before-method-override` rule.
|
|
13
|
+
* Style guide applied to all the code involving the tests
|
|
14
|
+
* Removing a repeated test and style changes
|
|
15
|
+
* ESLint added to the workflow
|
|
16
|
+
* Removed not needed variables
|
|
17
|
+
* Fix to a problem with a rule detected implementing the tests
|
|
18
|
+
* Test engine with tests for all the rules
|
|
19
|
+
* Add additional information to README for each rule
|
|
20
|
+
|
|
21
|
+
1.2.0 / 2016-01-21
|
|
22
|
+
==================
|
|
23
|
+
|
|
24
|
+
* updated to check for new RegExp too
|
|
25
|
+
|
|
26
|
+
1.1.0 / 2016-01-06
|
|
27
|
+
==================
|
|
28
|
+
|
|
29
|
+
* adding eslint rule to detect new buffer hotspot
|
|
30
|
+
|
|
31
|
+
1.0.0 / 2015-11-15
|
|
32
|
+
==================
|
|
33
|
+
|
|
34
|
+
* rules disabled by default
|
package/README.md
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
# eslint-plugin-security
|
|
2
|
+
|
|
2
3
|
ESLint rules for Node Security
|
|
3
4
|
|
|
4
|
-
|
|
5
|
+
This project will help identify potential security hotspots, but finds a lot of false positives which need triage by a human.
|
|
5
6
|
|
|
6
7
|
### Installation
|
|
7
8
|
|
|
@@ -14,20 +15,98 @@ Add the following to your `.eslintrc` file:
|
|
|
14
15
|
```js
|
|
15
16
|
"plugins": [
|
|
16
17
|
"security"
|
|
18
|
+
],
|
|
19
|
+
"extends": [
|
|
20
|
+
"plugin:security/recommended"
|
|
17
21
|
]
|
|
18
22
|
```
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
## Developer guide
|
|
26
|
+
|
|
27
|
+
- Use [GitHub pull requests](https://help.github.com/articles/using-pull-requests).
|
|
28
|
+
- Conventions:
|
|
29
|
+
- We use our [custom ESLint setup](https://github.com/nodesecurity/eslint-config-nodesecurity).
|
|
30
|
+
- Please implement a test for each new rule and use this command to be sure the new code respects the style guide and the tests keep passing:
|
|
31
|
+
```sh
|
|
32
|
+
npm run-script cont-int
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
### Tests
|
|
36
|
+
```sh
|
|
37
|
+
npm test
|
|
38
|
+
```
|
|
39
|
+
|
|
19
40
|
### Rules
|
|
20
41
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
42
|
+
#### `detect-unsafe-regex`
|
|
43
|
+
|
|
44
|
+
Locates potentially unsafe regular expressions, which may take a very long time to run, blocking the event loop.
|
|
45
|
+
|
|
46
|
+
More information: https://blog.liftsecurity.io/2014/11/03/regular-expression-dos-and-node.js
|
|
47
|
+
|
|
48
|
+
#### `detect-buffer-noassert`
|
|
49
|
+
|
|
50
|
+
Detects calls to [`buffer`](https://nodejs.org/api/buffer.html) with `noAssert` flag set
|
|
51
|
+
|
|
52
|
+
From the Node.js API docs: "Setting `noAssert` to true skips validation of the `offset`. This allows the `offset` to be beyond the end of the `Buffer`."
|
|
53
|
+
|
|
54
|
+
#### `detect-child-process`
|
|
55
|
+
|
|
56
|
+
Detects 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)
|
|
57
|
+
|
|
58
|
+
More information: https://blog.liftsecurity.io/2014/08/19/Avoid-Command-Injection-Node.js
|
|
59
|
+
|
|
60
|
+
#### `detect-disable-mustache-escape`
|
|
61
|
+
|
|
62
|
+
Detects `object.escapeMarkup = false`, which can be used with some template engines to disable escaping of HTML entities. This can lead to Cross-Site Scripting (XSS) vulnerabilities.
|
|
63
|
+
|
|
64
|
+
More information: https://www.owasp.org/index.php/Cross-site_Scripting_(XSS)
|
|
65
|
+
|
|
66
|
+
#### `detect-eval-with-expression`
|
|
67
|
+
|
|
68
|
+
Detects `eval(variable)` which can allow an attacker to run arbitary code inside your process.
|
|
69
|
+
|
|
70
|
+
More information: http://security.stackexchange.com/questions/94017/what-are-the-security-issues-with-eval-in-javascript
|
|
71
|
+
|
|
72
|
+
#### `detect-no-csrf-before-method-override`
|
|
73
|
+
|
|
74
|
+
Detects Express `csrf` middleware setup before `method-override` middleware. This can allow `GET` requests (which are not checked by `csrf`) to turn into `POST` requests later.
|
|
75
|
+
|
|
76
|
+
More information: https://blog.liftsecurity.io/2013/09/07/bypass-connect-csrf-protection-by-abusing
|
|
77
|
+
|
|
78
|
+
#### `detect-non-literal-fs-filename`
|
|
79
|
+
|
|
80
|
+
Detects variable in filename argument of `fs` calls, which might allow an attacker to access anything on your system.
|
|
81
|
+
|
|
82
|
+
More information: https://www.owasp.org/index.php/Path_Traversal
|
|
83
|
+
|
|
84
|
+
#### `detect-non-literal-regexp`
|
|
85
|
+
|
|
86
|
+
Detects `RegExp(variable)`, which might allow an attacker to DOS your server with a long-running regular expression.
|
|
87
|
+
|
|
88
|
+
More information: https://blog.liftsecurity.io/2014/11/03/regular-expression-dos-and-node.js
|
|
89
|
+
|
|
90
|
+
#### `detect-non-literal-require`
|
|
91
|
+
|
|
92
|
+
Detects `require(variable)`, which might allow an attacker to load and run arbitrary code, or access arbitrary files on disk.
|
|
93
|
+
|
|
94
|
+
More information: http://www.bennadel.com/blog/2169-where-does-node-js-and-require-look-for-modules.htm
|
|
95
|
+
|
|
96
|
+
#### `detect-object-injection`
|
|
97
|
+
|
|
98
|
+
Detects `variable[key]` as a left- or right-hand assignment operand.
|
|
99
|
+
|
|
100
|
+
More information: https://blog.liftsecurity.io/2015/01/14/the-dangers-of-square-bracket-notation/
|
|
101
|
+
|
|
102
|
+
#### `detect-possible-timing-attacks`
|
|
103
|
+
|
|
104
|
+
Detects insecure comparisons (`==`, `!=`, `!==` and `===`), which check input sequentially.
|
|
105
|
+
|
|
106
|
+
More information: https://snyk.io/blog/node-js-timing-attack-ccc-ctf/
|
|
107
|
+
|
|
108
|
+
#### `detect-pseudoRandomBytes`
|
|
109
|
+
|
|
110
|
+
Detects if `pseudoRandomBytes()` is in use, which might not give you the randomness you need and expect.
|
|
33
111
|
|
|
112
|
+
More information: http://stackoverflow.com/questions/18130254/randombytes-vs-pseudorandombytes
|
package/index.js
CHANGED
|
@@ -17,7 +17,8 @@ module.exports = {
|
|
|
17
17
|
'detect-buffer-noassert': require('./rules/detect-buffer-noassert'),
|
|
18
18
|
'detect-child-process': require('./rules/detect-child-process'),
|
|
19
19
|
'detect-disable-mustache-escape': require('./rules/detect-disable-mustache-escape'),
|
|
20
|
-
'detect-object-injection': require('./rules/detect-object-injection')
|
|
20
|
+
'detect-object-injection': require('./rules/detect-object-injection'),
|
|
21
|
+
'detect-new-buffer': require('./rules/detect-new-buffer')
|
|
21
22
|
},
|
|
22
23
|
rulesConfig: {
|
|
23
24
|
'detect-unsafe-regex': 0,
|
|
@@ -31,6 +32,29 @@ module.exports = {
|
|
|
31
32
|
'detect-buffer-noassert': 0,
|
|
32
33
|
'detect-child-process': 0,
|
|
33
34
|
'detect-disable-mustache-escape': 0,
|
|
34
|
-
'detect-object-injection': 0
|
|
35
|
+
'detect-object-injection': 0,
|
|
36
|
+
'detect-new-buffer': 0
|
|
37
|
+
},
|
|
38
|
+
configs: {
|
|
39
|
+
recommended: {
|
|
40
|
+
plugins: [
|
|
41
|
+
'security'
|
|
42
|
+
],
|
|
43
|
+
rules: {
|
|
44
|
+
'security/detect-buffer-noassert': 'warn',
|
|
45
|
+
'security/detect-child-process': 'warn',
|
|
46
|
+
'security/detect-disable-mustache-escape': 'warn',
|
|
47
|
+
'security/detect-eval-with-expression': 'warn',
|
|
48
|
+
'security/detect-new-buffer': 'warn',
|
|
49
|
+
'security/detect-no-csrf-before-method-override': 'warn',
|
|
50
|
+
'security/detect-non-literal-fs-filename': 'warn',
|
|
51
|
+
'security/detect-non-literal-regexp': 'warn',
|
|
52
|
+
'security/detect-non-literal-require': 'warn',
|
|
53
|
+
'security/detect-object-injection': 'warn',
|
|
54
|
+
'security/detect-possible-timing-attacks': 'warn',
|
|
55
|
+
'security/detect-pseudoRandomBytes': 'warn',
|
|
56
|
+
'security/detect-unsafe-regex': 'warn'
|
|
57
|
+
}
|
|
58
|
+
}
|
|
35
59
|
}
|
|
36
60
|
};
|
package/package.json
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "eslint-plugin-security",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.4.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
11
|
},
|
|
9
12
|
"repository": {
|
|
10
13
|
"type": "git",
|
|
@@ -25,6 +28,9 @@
|
|
|
25
28
|
"safe-regex": "^1.1.0"
|
|
26
29
|
},
|
|
27
30
|
"devDependencies": {
|
|
28
|
-
"
|
|
31
|
+
"changelog": "1.3.0",
|
|
32
|
+
"eslint": "^2.10.1",
|
|
33
|
+
"eslint-config-nodesecurity": "^1.3.1",
|
|
34
|
+
"mocha": "^2.4.5"
|
|
29
35
|
}
|
|
30
36
|
}
|
|
@@ -47,11 +47,6 @@ module.exports = function(context) {
|
|
|
47
47
|
"writeDoubleBE"
|
|
48
48
|
];
|
|
49
49
|
|
|
50
|
-
|
|
51
|
-
var getSource = function (token) {
|
|
52
|
-
return token.loc.start.line+ ': ' + context.getSourceLines().slice(token.loc.start.line-1, token.loc.end.line).join('\n\t');
|
|
53
|
-
}
|
|
54
|
-
|
|
55
50
|
return {
|
|
56
51
|
"MemberExpression": function (node) {
|
|
57
52
|
var index;
|
|
@@ -63,7 +58,7 @@ module.exports = function(context) {
|
|
|
63
58
|
|
|
64
59
|
if (index && node.parent && node.parent.arguments && node.parent.arguments[index] && node.parent.arguments[index].value) {
|
|
65
60
|
var token = context.getTokens(node)[0];
|
|
66
|
-
return context.report(node, '
|
|
61
|
+
return context.report(node, 'Found Buffer.' + node.property.name + ' with noAssert flag set true');
|
|
67
62
|
|
|
68
63
|
}
|
|
69
64
|
}
|
|
@@ -13,10 +13,6 @@ module.exports = function(context) {
|
|
|
13
13
|
|
|
14
14
|
"use strict";
|
|
15
15
|
|
|
16
|
-
var getSource = function (token) {
|
|
17
|
-
return token.loc.start.line+ ': ' + context.getSourceLines().slice(token.loc.start.line-1, token.loc.end.line).join('\n\t');
|
|
18
|
-
}
|
|
19
|
-
|
|
20
16
|
return {
|
|
21
17
|
"CallExpression": function (node) {
|
|
22
18
|
var token = context.getTokens(node)[0];
|
|
@@ -28,7 +24,7 @@ module.exports = function(context) {
|
|
|
28
24
|
} else if (node.parent.type === 'AssignmentExpression' && node.parent.operator === '=') {
|
|
29
25
|
names.push(node.parent.left.name);
|
|
30
26
|
}
|
|
31
|
-
return context.report(node, '
|
|
27
|
+
return context.report(node, 'Found require("child_process")');
|
|
32
28
|
}
|
|
33
29
|
}
|
|
34
30
|
},
|
|
@@ -36,7 +32,7 @@ module.exports = function(context) {
|
|
|
36
32
|
var token = context.getTokens(node)[0];
|
|
37
33
|
if (node.property.name === 'exec' && names.indexOf(node.object.name) > -1) {
|
|
38
34
|
if (node.parent && node.parent.arguments && node.parent.arguments[0].type !== 'Literal') {
|
|
39
|
-
return context.report(node, '
|
|
35
|
+
return context.report(node, 'Found child_process.exec() with non Literal first argument');
|
|
40
36
|
}
|
|
41
37
|
}
|
|
42
38
|
}
|
|
@@ -0,0 +1,19 @@
|
|
|
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') {
|
|
9
|
+
|
|
10
|
+
return context.report(node, "Found new Buffer");
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
}
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
}
|
|
19
|
+
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Check and see if CSRF middleware is before methodOverride
|
|
3
|
-
* @author Adam Baldwin
|
|
2
|
+
* Check and see if CSRF middleware is before methodOverride
|
|
3
|
+
* @author Adam Baldwin
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
//------------------------------------------------------------------------------
|
|
@@ -20,6 +20,10 @@ module.exports = function(context) {
|
|
|
20
20
|
nodeValue = token.value;
|
|
21
21
|
|
|
22
22
|
if (nodeValue === "express") {
|
|
23
|
+
if (!node.callee || !node.callee.property) {
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
|
|
23
27
|
if (node.callee.property.name === "methodOverride" && csrf) {
|
|
24
28
|
context.report(node, "express.csrf() middleware found before express.methodOverride()");
|
|
25
29
|
}
|
|
@@ -15,10 +15,6 @@ module.exports = function(context) {
|
|
|
15
15
|
|
|
16
16
|
"use strict";
|
|
17
17
|
|
|
18
|
-
var getSource = function (token) {
|
|
19
|
-
return token.loc.start.line+ ': ' + context.getSourceLines().slice(token.loc.start.line-1, token.loc.end.line).join('\n\t');
|
|
20
|
-
}
|
|
21
|
-
|
|
22
18
|
return {
|
|
23
19
|
"MemberExpression": function (node) {
|
|
24
20
|
var result = [];
|
|
@@ -36,13 +32,13 @@ module.exports = function(context) {
|
|
|
36
32
|
|
|
37
33
|
if (result.length > 0) {
|
|
38
34
|
var token = context.getTokens(node)[0];
|
|
39
|
-
return context.report(node, '
|
|
35
|
+
return context.report(node, 'Found fs.' + node.property.name + ' with non literal argument at index ' + result.join(','));
|
|
40
36
|
}
|
|
41
37
|
|
|
42
38
|
|
|
43
39
|
/*
|
|
44
40
|
if (node.parent && node.parent.arguments && node.parent.arguments[index].value) {
|
|
45
|
-
return context.report(node, 'found Buffer.' + node.property.name + ' with noAssert flag set true
|
|
41
|
+
return context.report(node, 'found Buffer.' + node.property.name + ' with noAssert flag set true');
|
|
46
42
|
|
|
47
43
|
}
|
|
48
44
|
*/
|
|
@@ -12,16 +12,13 @@ module.exports = function(context) {
|
|
|
12
12
|
|
|
13
13
|
"use strict";
|
|
14
14
|
|
|
15
|
-
var getSource = function(token) {
|
|
16
|
-
return token.loc.start.line + ': ' + context.getSourceLines().slice(token.loc.start.line - 1, token.loc.end.line).join('\n\t');
|
|
17
|
-
}
|
|
18
15
|
return {
|
|
19
|
-
"
|
|
16
|
+
"NewExpression": function(node) {
|
|
20
17
|
if (node.callee.name === 'RegExp') {
|
|
21
18
|
var args = node.arguments;
|
|
22
19
|
if (args && args.length > 0 && args[0].type !== 'Literal') {
|
|
23
20
|
var token = context.getTokens(node)[0];
|
|
24
|
-
return context.report(node, '
|
|
21
|
+
return context.report(node, 'Found non-literal argument to RegExp Constructor');
|
|
25
22
|
}
|
|
26
23
|
}
|
|
27
24
|
|
|
@@ -11,17 +11,13 @@ module.exports = function(context) {
|
|
|
11
11
|
|
|
12
12
|
"use strict";
|
|
13
13
|
|
|
14
|
-
var getSource = function (token) {
|
|
15
|
-
return token.loc.start.line+ ': ' + context.getSourceLines().slice(token.loc.start.line-1, token.loc.end.line).join('\n\t');
|
|
16
|
-
}
|
|
17
|
-
|
|
18
14
|
return {
|
|
19
15
|
"CallExpression": function (node) {
|
|
20
16
|
if (node.callee.name === 'require') {
|
|
21
17
|
var args = node.arguments;
|
|
22
18
|
if (args && args.length > 0 && args[0].type !== 'Literal') {
|
|
23
19
|
var token = context.getTokens(node)[0];
|
|
24
|
-
return context.report(node, '
|
|
20
|
+
return context.report(node, 'Found non-literal argument in require');
|
|
25
21
|
}
|
|
26
22
|
}
|
|
27
23
|
|
|
@@ -59,13 +59,13 @@ var isChanged = false;
|
|
|
59
59
|
var token = context.getTokens(node)[0];
|
|
60
60
|
if (node.property.type === 'Identifier') {
|
|
61
61
|
if (node.parent.type === 'VariableDeclarator') {
|
|
62
|
-
context.report(node, 'Variable Assigned to Object Injection Sink
|
|
62
|
+
context.report(node, 'Variable Assigned to Object Injection Sink');
|
|
63
63
|
|
|
64
64
|
} else if (node.parent.type === 'CallExpression') {
|
|
65
65
|
// console.log(node.parent)
|
|
66
|
-
context.report(node, 'Function Call Object Injection Sink
|
|
66
|
+
context.report(node, 'Function Call Object Injection Sink');
|
|
67
67
|
} else {
|
|
68
|
-
context.report(node, 'Generic Object Injection Sink
|
|
68
|
+
context.report(node, 'Generic Object Injection Sink');
|
|
69
69
|
|
|
70
70
|
}
|
|
71
71
|
|
|
@@ -32,10 +32,6 @@ module.exports = function(context) {
|
|
|
32
32
|
|
|
33
33
|
"use strict";
|
|
34
34
|
|
|
35
|
-
var getSource = function (token) {
|
|
36
|
-
return token.loc.start.line+ ': ' + context.getSourceLines().slice(token.loc.start.line-1, token.loc.end.line).join('\n\t');
|
|
37
|
-
}
|
|
38
|
-
|
|
39
35
|
return {
|
|
40
36
|
"IfStatement": function(node) {
|
|
41
37
|
if (node.test && node.test.type === 'BinaryExpression') {
|
|
@@ -46,14 +42,14 @@ module.exports = function(context) {
|
|
|
46
42
|
if (node.test.left) {
|
|
47
43
|
var left = containsKeyword(node.test.left);
|
|
48
44
|
if (left) {
|
|
49
|
-
return context.report(node, "Potential timing attack, left side: " + left
|
|
45
|
+
return context.report(node, "Potential timing attack, left side: " + left);
|
|
50
46
|
}
|
|
51
47
|
}
|
|
52
48
|
|
|
53
49
|
if (node.test.right) {
|
|
54
50
|
var right = containsKeyword(node.test.right);
|
|
55
51
|
if (right) {
|
|
56
|
-
return context.report(node, "Potential timing attack, right side: " + right
|
|
52
|
+
return context.report(node, "Potential timing attack, right side: " + right);
|
|
57
53
|
}
|
|
58
54
|
}
|
|
59
55
|
}
|
|
@@ -11,15 +11,11 @@ module.exports = function(context) {
|
|
|
11
11
|
|
|
12
12
|
"use strict";
|
|
13
13
|
|
|
14
|
-
var getSource = function (token) {
|
|
15
|
-
return token.loc.start.line+ ': ' + context.getSourceLines().slice(token.loc.start.line-1, token.loc.end.line).join('\n\t');
|
|
16
|
-
}
|
|
17
|
-
|
|
18
14
|
return {
|
|
19
15
|
"MemberExpression": function (node) {
|
|
20
16
|
if (node.property.name === 'pseudoRandomBytes') {
|
|
21
17
|
var token = context.getTokens(node)[0];
|
|
22
|
-
return context.report(node, '
|
|
18
|
+
return context.report(node, 'Found crypto.pseudoRandomBytes which does not produce cryptographically strong numbers');
|
|
23
19
|
}
|
|
24
20
|
}
|
|
25
21
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
var safe = require('safe-regex');
|
|
2
2
|
/**
|
|
3
3
|
* Check if the regex is evil or not using the safe-regex module
|
|
4
|
-
* @author Adam Baldwin
|
|
4
|
+
* @author Adam Baldwin
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
//------------------------------------------------------------------------------
|
|
@@ -11,9 +11,6 @@ var safe = require('safe-regex');
|
|
|
11
11
|
module.exports = function(context) {
|
|
12
12
|
|
|
13
13
|
"use strict";
|
|
14
|
-
var getSource = function(token) {
|
|
15
|
-
return token.loc.start.line + ': ' + context.getSourceLines().slice(token.loc.start.line - 1, token.loc.end.line).join('\n\t');
|
|
16
|
-
}
|
|
17
14
|
|
|
18
15
|
return {
|
|
19
16
|
"Literal": function(node) {
|
|
@@ -23,7 +20,14 @@ module.exports = function(context) {
|
|
|
23
20
|
|
|
24
21
|
if (nodeType === "RegularExpression") {
|
|
25
22
|
if (!safe(nodeValue)) {
|
|
26
|
-
context.report(node, "Unsafe Regular Expression
|
|
23
|
+
context.report(node, "Unsafe Regular Expression");
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
"NewExpression": function(node) {
|
|
28
|
+
if (node.callee.name == "RegExp" && node.arguments && node.arguments.length > 0 && node.arguments[0].type == "Literal") {
|
|
29
|
+
if (!safe(node.arguments[0].value)) {
|
|
30
|
+
context.report(node, "Unsafe Regular Expression (new RegExp)");
|
|
27
31
|
}
|
|
28
32
|
}
|
|
29
33
|
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const RuleTester = require('eslint').RuleTester;
|
|
4
|
+
const tester = new RuleTester();
|
|
5
|
+
|
|
6
|
+
const ruleName = 'detect-buffer-noassert';
|
|
7
|
+
const Rule = require(`../rules/${ruleName}`);
|
|
8
|
+
|
|
9
|
+
const invalid = 'a.readUInt8(0, true);';
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
tester.run(ruleName, Rule, {
|
|
13
|
+
valid: [{ code: 'a.readUInt8(0);' }],
|
|
14
|
+
invalid: [
|
|
15
|
+
{
|
|
16
|
+
code: invalid,
|
|
17
|
+
errors: [{ message: 'Found Buffer.readUInt8 with noAssert flag set true' }]
|
|
18
|
+
}
|
|
19
|
+
]
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
tester.run(`${ruleName} (false)`, Rule, {
|
|
23
|
+
valid: [{ code: 'a.readUInt8(0, false);' }],
|
|
24
|
+
invalid: [
|
|
25
|
+
{
|
|
26
|
+
code: invalid,
|
|
27
|
+
errors: [{ message: 'Found Buffer.readUInt8 with noAssert flag set true' }]
|
|
28
|
+
}
|
|
29
|
+
]
|
|
30
|
+
});
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const RuleTester = require('eslint').RuleTester;
|
|
4
|
+
const tester = new RuleTester();
|
|
5
|
+
|
|
6
|
+
const ruleName = 'detect-child-process';
|
|
7
|
+
const Rule = require(`../rules/${ruleName}`);
|
|
8
|
+
|
|
9
|
+
const valid = 'child_process.exec(\'ls\')';
|
|
10
|
+
const invalidRequire = 'require(\'child_process\')';
|
|
11
|
+
const invalidExec = 'var child = require(\'child_process\'); child.exec(com)';
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
tester.run(`${ruleName} (require("child_process"))`, Rule, {
|
|
15
|
+
valid: [{ code: valid }],
|
|
16
|
+
invalid: [
|
|
17
|
+
{
|
|
18
|
+
code: invalidRequire,
|
|
19
|
+
errors: [{ message: 'Found require("child_process")' }]
|
|
20
|
+
}
|
|
21
|
+
]
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
tester.run(`${ruleName} (child_process.exec() wih non literal 1st arg.)`, Rule, {
|
|
26
|
+
valid: [{ code: valid }],
|
|
27
|
+
invalid: [
|
|
28
|
+
{
|
|
29
|
+
code: invalidExec,
|
|
30
|
+
errors: [
|
|
31
|
+
{ message: 'Found require("child_process")' },
|
|
32
|
+
{ message: 'Found child_process.exec() with non Literal first argument' }]
|
|
33
|
+
}
|
|
34
|
+
]
|
|
35
|
+
});
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const RuleTester = require('eslint').RuleTester;
|
|
4
|
+
const tester = new RuleTester();
|
|
5
|
+
|
|
6
|
+
const ruleName = 'detect-disable-mustache-escape';
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
tester.run(ruleName, require(`../rules/${ruleName}`), {
|
|
10
|
+
valid: [{ code: 'escapeMarkup = false' }],
|
|
11
|
+
invalid: [
|
|
12
|
+
{
|
|
13
|
+
code: 'a.escapeMarkup = false',
|
|
14
|
+
errors: [{ message: 'Markup escaping disabled.' }]
|
|
15
|
+
}
|
|
16
|
+
]
|
|
17
|
+
});
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const RuleTester = require('eslint').RuleTester;
|
|
4
|
+
const tester = new RuleTester();
|
|
5
|
+
|
|
6
|
+
const ruleName = 'detect-eval-with-expression';
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
tester.run(ruleName, require(`../rules/${ruleName}`), {
|
|
10
|
+
valid: [{ code: 'eval(\'alert()\')' }],
|
|
11
|
+
invalid: [
|
|
12
|
+
{
|
|
13
|
+
code: 'eval(a);',
|
|
14
|
+
errors: [{ message: 'eval with argument of type Identifier' }]
|
|
15
|
+
}
|
|
16
|
+
]
|
|
17
|
+
});
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const RuleTester = require('eslint').RuleTester;
|
|
4
|
+
const tester = new RuleTester();
|
|
5
|
+
|
|
6
|
+
const ruleName = 'detect-new-buffer';
|
|
7
|
+
const invalid = 'var a = new Buffer(c)';
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
tester.run(ruleName, require(`../rules/${ruleName}`), {
|
|
11
|
+
valid: [{ code: 'var a = new Buffer(\'test\')' }],
|
|
12
|
+
invalid: [
|
|
13
|
+
{
|
|
14
|
+
code: invalid,
|
|
15
|
+
errors: [{ message: 'Found new Buffer' }]
|
|
16
|
+
}
|
|
17
|
+
]
|
|
18
|
+
});
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const RuleTester = require('eslint').RuleTester;
|
|
4
|
+
const tester = new RuleTester();
|
|
5
|
+
|
|
6
|
+
const ruleName = 'detect-no-csrf-before-method-override';
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
tester.run(ruleName, require(`../rules/${ruleName}`), {
|
|
10
|
+
valid: [{ code: 'express.methodOverride();express.csrf()' }],
|
|
11
|
+
invalid: [
|
|
12
|
+
{
|
|
13
|
+
code: 'express.csrf();express.methodOverride()',
|
|
14
|
+
errors: [{ message: 'express.csrf() middleware found before express.methodOverride()' }]
|
|
15
|
+
}
|
|
16
|
+
]
|
|
17
|
+
});
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const RuleTester = require('eslint').RuleTester;
|
|
4
|
+
const tester = new RuleTester();
|
|
5
|
+
|
|
6
|
+
const invalid = 'var a = fs.open(c)';
|
|
7
|
+
|
|
8
|
+
const ruleName = 'detect-non-literal-fs-filename';
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
tester.run(ruleName, require(`../rules/${ruleName}`), {
|
|
12
|
+
valid: [{ code: 'var a = fs.open(\'test\')' }],
|
|
13
|
+
invalid: [
|
|
14
|
+
{
|
|
15
|
+
code: invalid,
|
|
16
|
+
errors: [{ message: 'Found fs.open with non literal argument at index 0' }]
|
|
17
|
+
}
|
|
18
|
+
]
|
|
19
|
+
});
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const RuleTester = require('eslint').RuleTester;
|
|
4
|
+
const tester = new RuleTester();
|
|
5
|
+
|
|
6
|
+
const ruleName = 'detect-non-literal-regexp';
|
|
7
|
+
const invalid = 'var a = new RegExp(c, \'i\')';
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
tester.run(ruleName, require(`../rules/${ruleName}`), {
|
|
11
|
+
valid: [{ code: 'var a = new RegExp(\'ab+c\', \'i\')' }],
|
|
12
|
+
invalid: [
|
|
13
|
+
{
|
|
14
|
+
code: invalid,
|
|
15
|
+
errors: [{ message: 'Found non-literal argument to RegExp Constructor' }]
|
|
16
|
+
}
|
|
17
|
+
]
|
|
18
|
+
});
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const RuleTester = require('eslint').RuleTester;
|
|
4
|
+
const tester = new RuleTester();
|
|
5
|
+
|
|
6
|
+
const ruleName = 'detect-non-literal-require';
|
|
7
|
+
const invalid = 'var a = require(c)';
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
tester.run(ruleName, require(`../rules/${ruleName}`), {
|
|
11
|
+
valid: [{ code: 'var a = require(\'b\')' }],
|
|
12
|
+
invalid: [
|
|
13
|
+
{
|
|
14
|
+
code: invalid,
|
|
15
|
+
errors: [{ message: 'Found non-literal argument in require' }]
|
|
16
|
+
}
|
|
17
|
+
]
|
|
18
|
+
});
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const RuleTester = require('eslint').RuleTester;
|
|
4
|
+
const tester = new RuleTester();
|
|
5
|
+
|
|
6
|
+
const ruleName = 'detect-object-injection';
|
|
7
|
+
|
|
8
|
+
const Rule = require(`../rules/${ruleName}`);
|
|
9
|
+
|
|
10
|
+
const valid = 'var a = {};';
|
|
11
|
+
// const invalidVariable = "TODO";
|
|
12
|
+
// const invalidFunction = "TODO";
|
|
13
|
+
const invalidGeneric = 'var a = {}; a[b] = 4';
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
// TODO
|
|
17
|
+
// tester.run(`${ruleName} (Variable Assigned to)`, Rule, {
|
|
18
|
+
// valid: [{ code: valid }],
|
|
19
|
+
// invalid: [
|
|
20
|
+
// {
|
|
21
|
+
// code: invalidVariable,
|
|
22
|
+
// errors: [{ message: 'Variable Assigned to Object Injection Sink' }]
|
|
23
|
+
// }
|
|
24
|
+
// ]
|
|
25
|
+
// });
|
|
26
|
+
//
|
|
27
|
+
//
|
|
28
|
+
// tester.run(`${ruleName} (Function)`, Rule, {
|
|
29
|
+
// valid: [{ code: valid }],
|
|
30
|
+
// invalid: [
|
|
31
|
+
// {
|
|
32
|
+
// code: invalidFunction,
|
|
33
|
+
// errors: [{ message: `Variable Assigned to Object Injection Sink: <input>: 1\n\t${invalidFunction}\n\n` }]
|
|
34
|
+
// }
|
|
35
|
+
// ]
|
|
36
|
+
// });
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
tester.run(`${ruleName} (Generic)`, Rule, {
|
|
40
|
+
valid: [{ code: valid }],
|
|
41
|
+
invalid: [
|
|
42
|
+
{
|
|
43
|
+
code: invalidGeneric,
|
|
44
|
+
errors: [{ message: 'Generic Object Injection Sink' }]
|
|
45
|
+
}
|
|
46
|
+
]
|
|
47
|
+
});
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const RuleTester = require('eslint').RuleTester;
|
|
4
|
+
const tester = new RuleTester();
|
|
5
|
+
|
|
6
|
+
const ruleName = 'detect-possible-timing-attacks';
|
|
7
|
+
const Rule = require(`../rules/${ruleName}`);
|
|
8
|
+
|
|
9
|
+
const valid = 'if (age === 5) {}';
|
|
10
|
+
const invalidLeft = 'if (password === \'mypass\') {}';
|
|
11
|
+
const invalidRigth = 'if (\'mypass\' === password) {}';
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
// We only check with one string "password" and operator "==="
|
|
15
|
+
// to KISS.
|
|
16
|
+
|
|
17
|
+
tester.run(`${ruleName} (left side)`, Rule, {
|
|
18
|
+
valid: [{ code: valid }],
|
|
19
|
+
invalid: [
|
|
20
|
+
{
|
|
21
|
+
code: invalidLeft,
|
|
22
|
+
errors: [{ message: 'Potential timing attack, left side: true' }]
|
|
23
|
+
}
|
|
24
|
+
]
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
tester.run(`${ruleName} (right side)`, Rule, {
|
|
29
|
+
valid: [{ code: valid }],
|
|
30
|
+
invalid: [
|
|
31
|
+
{
|
|
32
|
+
code: invalidRigth,
|
|
33
|
+
errors: [{ message: 'Potential timing attack, right side: true' }]
|
|
34
|
+
}
|
|
35
|
+
]
|
|
36
|
+
});
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const RuleTester = require('eslint').RuleTester;
|
|
4
|
+
const tester = new RuleTester();
|
|
5
|
+
|
|
6
|
+
const ruleName = 'detect-pseudoRandomBytes';
|
|
7
|
+
const invalid = 'crypto.pseudoRandomBytes';
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
tester.run(ruleName, require(`../rules/${ruleName}`), {
|
|
11
|
+
valid: [{ code: 'crypto.randomBytes' }],
|
|
12
|
+
invalid: [
|
|
13
|
+
{
|
|
14
|
+
code: invalid,
|
|
15
|
+
errors: [{ message: 'Found crypto.pseudoRandomBytes which does not produce cryptographically strong numbers' }]
|
|
16
|
+
}
|
|
17
|
+
]
|
|
18
|
+
});
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const RuleTester = require('eslint').RuleTester;
|
|
4
|
+
const tester = new RuleTester();
|
|
5
|
+
|
|
6
|
+
const ruleName = 'detect-unsafe-regex';
|
|
7
|
+
const Rule = require(`../rules/${ruleName}`);
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
tester.run(ruleName, Rule, {
|
|
11
|
+
valid: [{ code: '/^\d+1337\d+$/' }],
|
|
12
|
+
invalid: [
|
|
13
|
+
{
|
|
14
|
+
code: '/(x+x+)+y/',
|
|
15
|
+
errors: [{ message: 'Unsafe Regular Expression' }]
|
|
16
|
+
}
|
|
17
|
+
]
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
tester.run(`${ruleName} (new RegExp)`, Rule, {
|
|
22
|
+
valid: [{ code: 'new RegExp(\'^\d+1337\d+$\')' }],
|
|
23
|
+
invalid: [
|
|
24
|
+
{
|
|
25
|
+
code: 'new RegExp(\'x+x+)+y\')',
|
|
26
|
+
errors: [{ message: 'Unsafe Regular Expression (new RegExp)' }]
|
|
27
|
+
}
|
|
28
|
+
]
|
|
29
|
+
});
|