eslint-plugin-security 1.7.1 → 2.1.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 +1 -0
- package/.github/workflows/ci.yml +4 -4
- package/CHANGELOG.md +18 -0
- package/README.md +15 -3
- package/docs/the-dangers-of-square-bracket-notation.md +1 -1
- package/eslint.config.js +43 -0
- package/index.js +39 -21
- package/package.json +4 -3
- package/test/configs/index.js +16 -0
- package/test/{detect-bidi-characters.js → rules/detect-bidi-characters.js} +2 -2
- package/test/{detect-buffer-noassert.js → rules/detect-buffer-noassert.js} +1 -1
- package/test/{detect-child-process.js → rules/detect-child-process.js} +1 -1
- package/test/{detect-disable-mustache-escape.js → rules/detect-disable-mustache-escape.js} +1 -1
- package/test/{detect-eval-with-expression.js → rules/detect-eval-with-expression.js} +1 -1
- package/test/{detect-new-buffer.js → rules/detect-new-buffer.js} +1 -1
- package/test/{detect-no-csrf-before-method-override.js → rules/detect-no-csrf-before-method-override.js} +1 -1
- package/test/{detect-non-literal-fs-filename.js → rules/detect-non-literal-fs-filename.js} +2 -2
- package/test/{detect-non-literal-regexp.js → rules/detect-non-literal-regexp.js} +1 -1
- package/test/{detect-non-literal-require.js → rules/detect-non-literal-require.js} +1 -1
- package/test/{detect-object-injection.js → rules/detect-object-injection.js} +1 -1
- package/test/{detect-possible-timing-attacks.js → rules/detect-possible-timing-attacks.js} +1 -1
- package/test/{detect-pseudoRandomBytes.js → rules/detect-pseudoRandomBytes.js} +1 -1
- package/test/{detect-unsafe-regexp.js → rules/detect-unsafe-regexp.js} +1 -1
- package/.eslintrc +0 -31
package/.github/workflows/ci.yml
CHANGED
|
@@ -17,7 +17,7 @@ jobs:
|
|
|
17
17
|
persist-credentials: false
|
|
18
18
|
- uses: actions/setup-node@v3
|
|
19
19
|
with:
|
|
20
|
-
node-version:
|
|
20
|
+
node-version: 18
|
|
21
21
|
|
|
22
22
|
- name: Install Packages
|
|
23
23
|
run: npm install
|
|
@@ -30,12 +30,12 @@ jobs:
|
|
|
30
30
|
strategy:
|
|
31
31
|
matrix:
|
|
32
32
|
os: [ubuntu-latest]
|
|
33
|
-
node: [
|
|
33
|
+
node: [12.22.0, 12, 14, 16, 18, 20]
|
|
34
34
|
include:
|
|
35
35
|
- os: windows-latest
|
|
36
|
-
node:
|
|
36
|
+
node: 18
|
|
37
37
|
- os: macOS-latest
|
|
38
|
-
node:
|
|
38
|
+
node: 18
|
|
39
39
|
runs-on: ${{ matrix.os }}
|
|
40
40
|
permissions:
|
|
41
41
|
contents: read
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,23 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [2.1.0](https://www.github.com/eslint-community/eslint-plugin-security/compare/v2.0.0...v2.1.0) (2023-12-15)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Features
|
|
7
|
+
|
|
8
|
+
* add config recommended-legacy ([#132](https://www.github.com/eslint-community/eslint-plugin-security/issues/132)) ([13d3f2f](https://www.github.com/eslint-community/eslint-plugin-security/commit/13d3f2fc6ba327c894959db30462f3fda0272f0c))
|
|
9
|
+
|
|
10
|
+
## [2.0.0](https://www.github.com/eslint-community/eslint-plugin-security/compare/v1.7.1...v2.0.0) (2023-10-17)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
### ⚠ BREAKING CHANGES
|
|
14
|
+
|
|
15
|
+
* switch the recommended config to flat (#118)
|
|
16
|
+
|
|
17
|
+
### Features
|
|
18
|
+
|
|
19
|
+
* switch the recommended config to flat ([#118](https://www.github.com/eslint-community/eslint-plugin-security/issues/118)) ([e20a366](https://www.github.com/eslint-community/eslint-plugin-security/commit/e20a3664c2f638466286ae9a97515722fc98f97c))
|
|
20
|
+
|
|
3
21
|
### [1.7.1](https://www.github.com/eslint-community/eslint-plugin-security/compare/v1.7.0...v1.7.1) (2023-02-02)
|
|
4
22
|
|
|
5
23
|
|
package/README.md
CHANGED
|
@@ -20,12 +20,24 @@ yarn add --dev eslint-plugin-security
|
|
|
20
20
|
|
|
21
21
|
## Usage
|
|
22
22
|
|
|
23
|
+
### Flat config (requires eslint >= v8.23.0)
|
|
24
|
+
|
|
25
|
+
Add the following to your `eslint.config.js` file:
|
|
26
|
+
|
|
27
|
+
```js
|
|
28
|
+
const pluginSecurity = require('eslint-plugin-security');
|
|
29
|
+
|
|
30
|
+
module.exports = [pluginSecurity.configs.recommended];
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
### eslintrc config (deprecated)
|
|
34
|
+
|
|
23
35
|
Add the following to your `.eslintrc` file:
|
|
24
36
|
|
|
25
37
|
```js
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
38
|
+
module.exports = {
|
|
39
|
+
extends: ['plugin:security/recommended-legacy'],
|
|
40
|
+
};
|
|
29
41
|
```
|
|
30
42
|
|
|
31
43
|
## Developer guide
|
|
@@ -94,7 +94,7 @@ Well, yes and no. Is this particular vector a widespread problem? No, because cu
|
|
|
94
94
|
|
|
95
95
|
Yes, we are talking about some fairly extreme edge cases, but don't make the assumption that your code doesn't have problems because of that - I have seen this issue in production code with some regularity. And, for the majority of node developers, a large portion of application code was not written by them, but rather included through required modules which may contain peculiar flaws like this one.
|
|
96
96
|
|
|
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
|
|
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 the code you're requiring.
|
|
98
98
|
|
|
99
99
|
## How do I fix it?
|
|
100
100
|
|
package/eslint.config.js
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const jsPlugin = require('@eslint/js');
|
|
4
|
+
const prettierConfig = require('eslint-config-prettier');
|
|
5
|
+
const eslintPluginRecommendedConfig = require('eslint-plugin-eslint-plugin/configs/recommended');
|
|
6
|
+
|
|
7
|
+
const eslintPluginConfigs = [
|
|
8
|
+
eslintPluginRecommendedConfig,
|
|
9
|
+
{
|
|
10
|
+
rules: {
|
|
11
|
+
'eslint-plugin/prefer-message-ids': 'off', // TODO: enable
|
|
12
|
+
'eslint-plugin/require-meta-docs-description': ['error', { pattern: '^(Detects|Enforces|Requires|Disallows) .+\\.$' }],
|
|
13
|
+
'eslint-plugin/require-meta-docs-url': [
|
|
14
|
+
'error',
|
|
15
|
+
{
|
|
16
|
+
pattern: 'https://github.com/eslint-community/eslint-plugin-security/blob/main/docs/rules/{{name}}.md',
|
|
17
|
+
},
|
|
18
|
+
],
|
|
19
|
+
'eslint-plugin/require-meta-schema': 'off', // TODO: enable
|
|
20
|
+
'eslint-plugin/require-meta-type': 'off', // TODO: enable
|
|
21
|
+
},
|
|
22
|
+
},
|
|
23
|
+
];
|
|
24
|
+
|
|
25
|
+
module.exports = [
|
|
26
|
+
jsPlugin.configs.recommended,
|
|
27
|
+
prettierConfig,
|
|
28
|
+
...eslintPluginConfigs,
|
|
29
|
+
{
|
|
30
|
+
languageOptions: {
|
|
31
|
+
sourceType: 'commonjs',
|
|
32
|
+
},
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
files: ['test/**/*.js'],
|
|
36
|
+
languageOptions: {
|
|
37
|
+
globals: {
|
|
38
|
+
describe: 'readonly',
|
|
39
|
+
it: 'readonly',
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
},
|
|
43
|
+
];
|
package/index.js
CHANGED
|
@@ -4,7 +4,13 @@
|
|
|
4
4
|
|
|
5
5
|
'use strict';
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
const pkg = require('./package.json');
|
|
8
|
+
|
|
9
|
+
const plugin = {
|
|
10
|
+
meta: {
|
|
11
|
+
name: pkg.name,
|
|
12
|
+
version: pkg.version,
|
|
13
|
+
},
|
|
8
14
|
rules: {
|
|
9
15
|
'detect-unsafe-regex': require('./rules/detect-unsafe-regex'),
|
|
10
16
|
'detect-non-literal-regexp': require('./rules/detect-non-literal-regexp'),
|
|
@@ -37,25 +43,37 @@ module.exports = {
|
|
|
37
43
|
'detect-new-buffer': 0,
|
|
38
44
|
'detect-bidi-characters': 0,
|
|
39
45
|
},
|
|
40
|
-
configs: {
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
46
|
+
configs: {}, // was assigned later so we can reference `plugin`
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
const recommended = {
|
|
50
|
+
plugins: { security: plugin },
|
|
51
|
+
rules: {
|
|
52
|
+
'security/detect-buffer-noassert': 'warn',
|
|
53
|
+
'security/detect-child-process': 'warn',
|
|
54
|
+
'security/detect-disable-mustache-escape': 'warn',
|
|
55
|
+
'security/detect-eval-with-expression': 'warn',
|
|
56
|
+
'security/detect-new-buffer': 'warn',
|
|
57
|
+
'security/detect-no-csrf-before-method-override': 'warn',
|
|
58
|
+
'security/detect-non-literal-fs-filename': 'warn',
|
|
59
|
+
'security/detect-non-literal-regexp': 'warn',
|
|
60
|
+
'security/detect-non-literal-require': 'warn',
|
|
61
|
+
'security/detect-object-injection': 'warn',
|
|
62
|
+
'security/detect-possible-timing-attacks': 'warn',
|
|
63
|
+
'security/detect-pseudoRandomBytes': 'warn',
|
|
64
|
+
'security/detect-unsafe-regex': 'warn',
|
|
65
|
+
'security/detect-bidi-characters': 'warn',
|
|
60
66
|
},
|
|
61
67
|
};
|
|
68
|
+
|
|
69
|
+
const recommendedLegacy = {
|
|
70
|
+
plugins: ['security'],
|
|
71
|
+
rules: recommended.rules,
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
Object.assign(plugin.configs, {
|
|
75
|
+
recommended,
|
|
76
|
+
'recommended-legacy': recommendedLegacy
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
module.exports = plugin;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "eslint-plugin-security",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "2.1.0",
|
|
4
4
|
"description": "Security rules for eslint",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -46,12 +46,13 @@
|
|
|
46
46
|
"safe-regex": "^2.1.1"
|
|
47
47
|
},
|
|
48
48
|
"devDependencies": {
|
|
49
|
+
"@eslint/js": "^8.51.0",
|
|
49
50
|
"changelog": "1.3.0",
|
|
50
|
-
"eslint": "^8.
|
|
51
|
+
"eslint": "^8.51.0",
|
|
51
52
|
"eslint-config-nodesecurity": "^1.3.1",
|
|
52
53
|
"eslint-config-prettier": "^8.5.0",
|
|
53
54
|
"eslint-doc-generator": "^1.0.2",
|
|
54
|
-
"eslint-plugin-eslint-plugin": "^5.
|
|
55
|
+
"eslint-plugin-eslint-plugin": "^5.1.1",
|
|
55
56
|
"lint-staged": "^12.3.7",
|
|
56
57
|
"markdownlint-cli": "^0.32.2",
|
|
57
58
|
"mocha": "^9.2.2",
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
const plugin = require('../../index.js');
|
|
3
|
+
const assert = require('assert').strict;
|
|
4
|
+
|
|
5
|
+
describe('export plugin object', () => {
|
|
6
|
+
it('should export rules', () => {
|
|
7
|
+
assert(plugin.rules);
|
|
8
|
+
assert(typeof plugin.rules['detect-unsafe-regex'] === 'object');
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
it('should export configs', () => {
|
|
12
|
+
assert(plugin.configs);
|
|
13
|
+
assert(plugin.configs['recommended']);
|
|
14
|
+
assert(plugin.configs['recommended-legacy']);
|
|
15
|
+
});
|
|
16
|
+
});
|
|
@@ -4,7 +4,7 @@ const RuleTester = require('eslint').RuleTester;
|
|
|
4
4
|
const tester = new RuleTester();
|
|
5
5
|
|
|
6
6
|
const ruleName = 'detect-bidi-characters';
|
|
7
|
-
const Rule = require(
|
|
7
|
+
const Rule = require(`../../rules/${ruleName}`);
|
|
8
8
|
|
|
9
9
|
tester.run(ruleName, Rule, {
|
|
10
10
|
valid: [
|
|
@@ -54,7 +54,7 @@ tester.run(`${ruleName} in comment-line`, Rule, {
|
|
|
54
54
|
console.log("You are an admin.");
|
|
55
55
|
/* end admins only
|
|
56
56
|
*/
|
|
57
|
-
/* end admins only
|
|
57
|
+
/* end admins only
|
|
58
58
|
{ */
|
|
59
59
|
`,
|
|
60
60
|
errors: [
|
|
@@ -4,7 +4,7 @@ const RuleTester = require('eslint').RuleTester;
|
|
|
4
4
|
const tester = new RuleTester();
|
|
5
5
|
|
|
6
6
|
const ruleName = 'detect-buffer-noassert';
|
|
7
|
-
const rule = require(
|
|
7
|
+
const rule = require(`../../rules/${ruleName}`);
|
|
8
8
|
|
|
9
9
|
const allMethodNames = [...rule.meta.__methodsToCheck.read, ...rule.meta.__methodsToCheck.write];
|
|
10
10
|
|
|
@@ -5,7 +5,7 @@ const tester = new RuleTester();
|
|
|
5
5
|
|
|
6
6
|
const ruleName = 'detect-disable-mustache-escape';
|
|
7
7
|
|
|
8
|
-
tester.run(ruleName, require(
|
|
8
|
+
tester.run(ruleName, require(`../../rules/${ruleName}`), {
|
|
9
9
|
valid: [{ code: 'escapeMarkup = false' }],
|
|
10
10
|
invalid: [
|
|
11
11
|
{
|
|
@@ -6,7 +6,7 @@ const tester = new RuleTester();
|
|
|
6
6
|
const ruleName = 'detect-new-buffer';
|
|
7
7
|
const invalid = 'var a = new Buffer(c)';
|
|
8
8
|
|
|
9
|
-
tester.run(ruleName, require(
|
|
9
|
+
tester.run(ruleName, require(`../../rules/${ruleName}`), {
|
|
10
10
|
valid: [{ code: "var a = new Buffer('test')" }],
|
|
11
11
|
invalid: [
|
|
12
12
|
{
|
|
@@ -5,7 +5,7 @@ const tester = new RuleTester();
|
|
|
5
5
|
|
|
6
6
|
const ruleName = 'detect-no-csrf-before-method-override';
|
|
7
7
|
|
|
8
|
-
tester.run(ruleName, require(
|
|
8
|
+
tester.run(ruleName, require(`../../rules/${ruleName}`), {
|
|
9
9
|
valid: [{ code: 'express.methodOverride();express.csrf()' }],
|
|
10
10
|
invalid: [
|
|
11
11
|
{
|
|
@@ -10,7 +10,7 @@ const tester = new RuleTester({
|
|
|
10
10
|
|
|
11
11
|
const ruleName = 'detect-non-literal-fs-filename';
|
|
12
12
|
|
|
13
|
-
tester.run(ruleName, require(
|
|
13
|
+
tester.run(ruleName, require(`../../rules/${ruleName}`), {
|
|
14
14
|
valid: [
|
|
15
15
|
{
|
|
16
16
|
code: `var fs = require('fs');
|
|
@@ -29,7 +29,7 @@ tester.run(ruleName, require(`../rules/${ruleName}`), {
|
|
|
29
29
|
import { promises as fsp } from 'fs';
|
|
30
30
|
import fs from 'fs';
|
|
31
31
|
import path from 'path';
|
|
32
|
-
|
|
32
|
+
|
|
33
33
|
const index = await fsp.readFile(path.resolve(__dirname, './index.html'), 'utf-8');
|
|
34
34
|
const key = fs.readFileSync(path.join(__dirname, './ssl.key'));
|
|
35
35
|
await fsp.writeFile(path.resolve(__dirname, './sitemap.xml'), sitemap);`,
|
|
@@ -6,7 +6,7 @@ const tester = new RuleTester();
|
|
|
6
6
|
const ruleName = 'detect-non-literal-regexp';
|
|
7
7
|
const invalid = "var a = new RegExp(c, 'i')";
|
|
8
8
|
|
|
9
|
-
tester.run(ruleName, require(
|
|
9
|
+
tester.run(ruleName, require(`../../rules/${ruleName}`), {
|
|
10
10
|
valid: [
|
|
11
11
|
{ code: "var a = new RegExp('ab+c', 'i')" },
|
|
12
12
|
{
|
|
@@ -6,7 +6,7 @@ const tester = new RuleTester({ parserOptions: { ecmaVersion: 6 } });
|
|
|
6
6
|
|
|
7
7
|
const ruleName = 'detect-non-literal-require';
|
|
8
8
|
|
|
9
|
-
tester.run(ruleName, require(
|
|
9
|
+
tester.run(ruleName, require(`../../rules/${ruleName}`), {
|
|
10
10
|
valid: [
|
|
11
11
|
{ code: "var a = require('b')" },
|
|
12
12
|
{ code: 'var a = require(`b`)' },
|
|
@@ -4,7 +4,7 @@ const RuleTester = require('eslint').RuleTester;
|
|
|
4
4
|
const tester = new RuleTester();
|
|
5
5
|
|
|
6
6
|
const ruleName = 'detect-possible-timing-attacks';
|
|
7
|
-
const Rule = require(
|
|
7
|
+
const Rule = require(`../../rules/${ruleName}`);
|
|
8
8
|
|
|
9
9
|
const valid = 'if (age === 5) {}';
|
|
10
10
|
const invalidLeft = "if (password === 'mypass') {}";
|
|
@@ -6,7 +6,7 @@ const tester = new RuleTester();
|
|
|
6
6
|
const ruleName = 'detect-pseudoRandomBytes';
|
|
7
7
|
const invalid = 'crypto.pseudoRandomBytes';
|
|
8
8
|
|
|
9
|
-
tester.run(ruleName, require(
|
|
9
|
+
tester.run(ruleName, require(`../../rules/${ruleName}`), {
|
|
10
10
|
valid: [{ code: 'crypto.randomBytes' }],
|
|
11
11
|
invalid: [
|
|
12
12
|
{
|
|
@@ -4,7 +4,7 @@ const RuleTester = require('eslint').RuleTester;
|
|
|
4
4
|
const tester = new RuleTester();
|
|
5
5
|
|
|
6
6
|
const ruleName = 'detect-unsafe-regex';
|
|
7
|
-
const Rule = require(
|
|
7
|
+
const Rule = require(`../../rules/${ruleName}`);
|
|
8
8
|
|
|
9
9
|
tester.run(ruleName, Rule, {
|
|
10
10
|
valid: [{ code: '/^d+1337d+$/' }],
|
package/.eslintrc
DELETED
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"extends": ["eslint:recommended", "prettier", "plugin:eslint-plugin/recommended"],
|
|
3
|
-
"parserOptions": {
|
|
4
|
-
"ecmaVersion": "latest"
|
|
5
|
-
},
|
|
6
|
-
"env": {
|
|
7
|
-
"node": true,
|
|
8
|
-
"es2020": true
|
|
9
|
-
},
|
|
10
|
-
"rules": {
|
|
11
|
-
"eslint-plugin/prefer-message-ids": "off", // TODO: enable
|
|
12
|
-
"eslint-plugin/require-meta-docs-description": ["error", { "pattern": "^(Detects|Enforces|Requires|Disallows) .+\\.$" }],
|
|
13
|
-
"eslint-plugin/require-meta-docs-url": [
|
|
14
|
-
"error",
|
|
15
|
-
{
|
|
16
|
-
"pattern": "https://github.com/eslint-community/eslint-plugin-security/blob/main/docs/rules/{{name}}.md"
|
|
17
|
-
}
|
|
18
|
-
],
|
|
19
|
-
"eslint-plugin/require-meta-schema": "off", // TODO: enable
|
|
20
|
-
"eslint-plugin/require-meta-type": "off" // TODO: enable
|
|
21
|
-
},
|
|
22
|
-
"overrides": [
|
|
23
|
-
{
|
|
24
|
-
"files": ["test/**/*.js"],
|
|
25
|
-
"globals": {
|
|
26
|
-
"describe": "readonly",
|
|
27
|
-
"it": "readonly"
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
]
|
|
31
|
-
}
|