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.
Files changed (24) hide show
  1. package/.eslint-doc-generatorrc.js +1 -0
  2. package/.github/workflows/ci.yml +4 -4
  3. package/CHANGELOG.md +18 -0
  4. package/README.md +15 -3
  5. package/docs/the-dangers-of-square-bracket-notation.md +1 -1
  6. package/eslint.config.js +43 -0
  7. package/index.js +39 -21
  8. package/package.json +4 -3
  9. package/test/configs/index.js +16 -0
  10. package/test/{detect-bidi-characters.js → rules/detect-bidi-characters.js} +2 -2
  11. package/test/{detect-buffer-noassert.js → rules/detect-buffer-noassert.js} +1 -1
  12. package/test/{detect-child-process.js → rules/detect-child-process.js} +1 -1
  13. package/test/{detect-disable-mustache-escape.js → rules/detect-disable-mustache-escape.js} +1 -1
  14. package/test/{detect-eval-with-expression.js → rules/detect-eval-with-expression.js} +1 -1
  15. package/test/{detect-new-buffer.js → rules/detect-new-buffer.js} +1 -1
  16. package/test/{detect-no-csrf-before-method-override.js → rules/detect-no-csrf-before-method-override.js} +1 -1
  17. package/test/{detect-non-literal-fs-filename.js → rules/detect-non-literal-fs-filename.js} +2 -2
  18. package/test/{detect-non-literal-regexp.js → rules/detect-non-literal-regexp.js} +1 -1
  19. package/test/{detect-non-literal-require.js → rules/detect-non-literal-require.js} +1 -1
  20. package/test/{detect-object-injection.js → rules/detect-object-injection.js} +1 -1
  21. package/test/{detect-possible-timing-attacks.js → rules/detect-possible-timing-attacks.js} +1 -1
  22. package/test/{detect-pseudoRandomBytes.js → rules/detect-pseudoRandomBytes.js} +1 -1
  23. package/test/{detect-unsafe-regexp.js → rules/detect-unsafe-regexp.js} +1 -1
  24. package/.eslintrc +0 -31
@@ -3,6 +3,7 @@ const prettierRC = require('./.prettierrc.json');
3
3
 
4
4
  /** @type {import('eslint-doc-generator').GenerateOptions} */
5
5
  const config = {
6
+ ignoreConfig: ['recommended-legacy'],
6
7
  postprocess: (doc) => format(doc, { ...prettierRC, parser: 'markdown' }),
7
8
  };
8
9
 
@@ -17,7 +17,7 @@ jobs:
17
17
  persist-credentials: false
18
18
  - uses: actions/setup-node@v3
19
19
  with:
20
- node-version: '16.x'
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: [18.x, 16.x, 14.x, 12.x, '12.22.0']
33
+ node: [12.22.0, 12, 14, 16, 18, 20]
34
34
  include:
35
35
  - os: windows-latest
36
- node: '16.x'
36
+ node: 18
37
37
  - os: macOS-latest
38
- node: '16.x'
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
- "extends": [
27
- "plugin:security/recommended"
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 [aware of what you require.](https://requiresafe.com)
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
 
@@ -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
- module.exports = {
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
- recommended: {
42
- plugins: ['security'],
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
- 'security/detect-bidi-characters': 'warn',
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.7.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.11.0",
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.0.2",
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(`../rules/${ruleName}`);
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(`../rules/${ruleName}`);
7
+ const rule = require(`../../rules/${ruleName}`);
8
8
 
9
9
  const allMethodNames = [...rule.meta.__methodsToCheck.read, ...rule.meta.__methodsToCheck.write];
10
10
 
@@ -9,7 +9,7 @@ const tester = new RuleTester({
9
9
  });
10
10
 
11
11
  const ruleName = 'detect-child-process';
12
- const rule = require(`../rules/${ruleName}`);
12
+ const rule = require(`../../rules/${ruleName}`);
13
13
 
14
14
  tester.run(ruleName, rule, {
15
15
  valid: [
@@ -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(`../rules/${ruleName}`), {
8
+ tester.run(ruleName, require(`../../rules/${ruleName}`), {
9
9
  valid: [{ code: 'escapeMarkup = false' }],
10
10
  invalid: [
11
11
  {
@@ -5,7 +5,7 @@ const tester = new RuleTester();
5
5
 
6
6
  const ruleName = 'detect-eval-with-expression';
7
7
 
8
- tester.run(ruleName, require(`../rules/${ruleName}`), {
8
+ tester.run(ruleName, require(`../../rules/${ruleName}`), {
9
9
  valid: [{ code: "eval('alert()')" }],
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(`../rules/${ruleName}`), {
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(`../rules/${ruleName}`), {
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(`../rules/${ruleName}`), {
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(`../rules/${ruleName}`), {
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(`../rules/${ruleName}`), {
9
+ tester.run(ruleName, require(`../../rules/${ruleName}`), {
10
10
  valid: [
11
11
  { code: "var a = require('b')" },
12
12
  { code: 'var a = require(`b`)' },
@@ -5,7 +5,7 @@ const tester = new RuleTester();
5
5
 
6
6
  const ruleName = 'detect-object-injection';
7
7
 
8
- const Rule = require(`../rules/${ruleName}`);
8
+ const Rule = require(`../../rules/${ruleName}`);
9
9
 
10
10
  const valid = 'var a = {};';
11
11
  // const invalidVariable = "TODO";
@@ -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(`../rules/${ruleName}`);
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(`../rules/${ruleName}`), {
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(`../rules/${ruleName}`);
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
- }