eslint-plugin-jwt-security 2.3.0 → 2.3.1

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 (82) hide show
  1. package/README.md +5 -5
  2. package/package.json +16 -24
  3. package/src/index.d.ts +17 -0
  4. package/{dist/src → src}/index.js +16 -69
  5. package/src/oxlint.d.ts +17 -0
  6. package/src/oxlint.js +3 -0
  7. package/{dist/src → src}/rules/no-algorithm-confusion/index.js +0 -20
  8. package/{dist/src → src}/rules/no-algorithm-none/index.js +0 -38
  9. package/{dist/src → src}/rules/no-decode-without-verify/index.js +0 -14
  10. package/{dist/src → src}/rules/no-hardcoded-secret/index.js +0 -39
  11. package/{dist/src → src}/rules/no-sensitive-payload/index.js +0 -6
  12. package/{dist/src → src}/rules/no-timestamp-manipulation/index.js +0 -6
  13. package/{dist/src → src}/rules/no-weak-secret/index.js +0 -19
  14. package/{dist/src → src}/rules/require-algorithm-whitelist/index.js +0 -9
  15. package/{dist/src → src}/rules/require-audience-validation/index.js +0 -7
  16. package/{dist/src → src}/rules/require-expiration/index.js +0 -16
  17. package/{dist/src → src}/rules/require-issued-at/index.js +0 -21
  18. package/{dist/src → src}/rules/require-issuer-validation/index.js +0 -7
  19. package/{dist/src → src}/rules/require-max-age/index.js +0 -7
  20. package/src/types/index.js +2 -0
  21. package/{dist/src → src}/utils/index.js +1 -74
  22. package/AGENTS.md +0 -102
  23. package/CHANGELOG.md +0 -161
  24. package/dist/AGENTS.md +0 -102
  25. package/dist/CHANGELOG.md +0 -161
  26. package/dist/LICENSE +0 -23
  27. package/dist/README.md +0 -158
  28. package/dist/package.json +0 -125
  29. package/dist/src/index.d.ts +0 -101
  30. package/dist/src/oxlint.d.ts +0 -20
  31. package/dist/src/oxlint.js +0 -21
  32. package/dist/src/rules/no-algorithm-confusion/index.d.ts +0 -24
  33. package/dist/src/rules/no-algorithm-none/index.d.ts +0 -24
  34. package/dist/src/rules/no-decode-without-verify/index.d.ts +0 -23
  35. package/dist/src/rules/no-hardcoded-secret/index.d.ts +0 -23
  36. package/dist/src/rules/no-sensitive-payload/index.d.ts +0 -20
  37. package/dist/src/rules/no-timestamp-manipulation/index.d.ts +0 -23
  38. package/dist/src/rules/no-weak-secret/index.d.ts +0 -23
  39. package/dist/src/rules/require-algorithm-whitelist/index.d.ts +0 -24
  40. package/dist/src/rules/require-audience-validation/index.d.ts +0 -20
  41. package/dist/src/rules/require-expiration/index.d.ts +0 -23
  42. package/dist/src/rules/require-issued-at/index.d.ts +0 -23
  43. package/dist/src/rules/require-issuer-validation/index.d.ts +0 -20
  44. package/dist/src/rules/require-max-age/index.d.ts +0 -24
  45. package/dist/src/types/index.js +0 -7
  46. package/dist/src/utils/index.d.ts +0 -98
  47. package/src/__compatibility__/jwt-interface.spec.ts +0 -192
  48. package/src/index.test.ts +0 -47
  49. package/src/index.ts +0 -209
  50. package/src/oxlint.test.ts +0 -61
  51. package/src/oxlint.ts +0 -23
  52. package/src/rules/no-algorithm-confusion/index.ts +0 -208
  53. package/src/rules/no-algorithm-confusion/no-algorithm-confusion.test.ts +0 -106
  54. package/src/rules/no-algorithm-none/index.ts +0 -270
  55. package/src/rules/no-algorithm-none/no-algorithm-none.test.ts +0 -146
  56. package/src/rules/no-decode-without-verify/index.ts +0 -162
  57. package/src/rules/no-decode-without-verify/no-decode-without-verify.test.ts +0 -76
  58. package/src/rules/no-hardcoded-secret/index.ts +0 -230
  59. package/src/rules/no-hardcoded-secret/no-hardcoded-secret.test.ts +0 -97
  60. package/src/rules/no-sensitive-payload/index.ts +0 -123
  61. package/src/rules/no-sensitive-payload/no-sensitive-payload.test.ts +0 -157
  62. package/src/rules/no-timestamp-manipulation/index.ts +0 -118
  63. package/src/rules/no-timestamp-manipulation/no-timestamp-manipulation.test.ts +0 -54
  64. package/src/rules/no-weak-secret/index.ts +0 -204
  65. package/src/rules/no-weak-secret/no-weak-secret.test.ts +0 -114
  66. package/src/rules/require-algorithm-whitelist/index.ts +0 -145
  67. package/src/rules/require-algorithm-whitelist/require-algorithm-whitelist.test.ts +0 -72
  68. package/src/rules/require-audience-validation/index.ts +0 -120
  69. package/src/rules/require-audience-validation/require-audience-validation.test.ts +0 -68
  70. package/src/rules/require-expiration/index.ts +0 -170
  71. package/src/rules/require-expiration/require-expiration.test.ts +0 -135
  72. package/src/rules/require-issued-at/index.ts +0 -143
  73. package/src/rules/require-issued-at/require-issued-at.test.ts +0 -64
  74. package/src/rules/require-issuer-validation/index.ts +0 -120
  75. package/src/rules/require-issuer-validation/require-issuer-validation.test.ts +0 -68
  76. package/src/rules/require-max-age/index.ts +0 -117
  77. package/src/rules/require-max-age/require-max-age.test.ts +0 -69
  78. package/src/tests/coverage-dual-layer.test.ts +0 -323
  79. package/src/types/index.ts +0 -107
  80. package/src/utils/index.ts +0 -285
  81. package/src/utils/utils.test.ts +0 -324
  82. /package/{dist/src → src}/types/index.d.ts +0 -0
package/README.md CHANGED
@@ -1,11 +1,11 @@
1
1
  <p align="center">
2
- <a href="https://eslint.interlace.tools/?utm_source=github&utm_medium=referral&utm_campaign=eslint-plugin-jwt-security" target="blank"><img src="https://eslint.interlace.tools/icon-light.svg" alt="Interlace" height="90" /></a>
2
+ <a href="https://eslint.interlace.tools/?utm_source=github&utm_medium=referral&utm_campaign=eslint-plugin-jwt-security" target="blank"><img src="https://eslint.interlace.tools/logos/interlace.svg" alt="Interlace" height="90" /></a>
3
3
  &nbsp;&nbsp;
4
- <a href="https://eslint.org" target="_blank"><img src="https://eslint.interlace.tools/eslint-logo.svg" alt="ESLint" height="90" /></a>
4
+ <a href="https://jwt.io" target="_blank"><img src="https://eslint.interlace.tools/logos/jwt.svg" alt="JSON Web Tokens" height="90" /></a>
5
5
  &nbsp;&nbsp;
6
- <a href="https://oxc.rs" target="_blank"><img src="https://eslint.interlace.tools/oxlint-logo.svg" alt="oxlint" height="90" /></a>
6
+ <a href="https://oxc.rs" target="_blank"><img src="https://eslint.interlace.tools/logos/oxlint.svg" alt="oxlint" height="90" /></a>
7
7
  &nbsp;&nbsp;
8
- <a href="https://www.npmjs.com/package/jsonwebtoken" target="_blank"><img src="https://eslint.interlace.tools/jwt-logo.svg" alt="JSON Web Tokens" height="90" /></a>
8
+ <a href="https://eslint.org" target="_blank"><img src="https://eslint.interlace.tools/logos/eslint.svg" alt="ESLint" height="90" /></a>
9
9
  </p>
10
10
 
11
11
  <p align="center">
@@ -150,7 +150,7 @@ If this plugin caught a real bug for you, **[star the repo](https://github.com/o
150
150
  MIT © [Ofri Peretz](https://github.com/ofri-peretz)
151
151
 
152
152
  <p align="center">
153
- <a href="https://eslint.interlace.tools/docs/security/plugin-jwt?utm_source=github&utm_medium=referral&utm_campaign=eslint-plugin-jwt-security"><img src="https://eslint.interlace.tools/images/og-jwt.png" alt="ESLint Interlace Plugin" width="100%" /></a>
153
+ <a href="https://eslint.interlace.tools/docs/security/plugin-jwt?utm_source=github&utm_medium=referral&utm_campaign=eslint-plugin-jwt-security"><img src="https://eslint.interlace.tools/images/og-jwt-security.png" alt="ESLint Interlace Plugin" width="100%" /></a>
154
154
  </p>
155
155
 
156
156
  <p align="center">
package/package.json CHANGED
@@ -1,14 +1,14 @@
1
1
  {
2
2
  "name": "eslint-plugin-jwt-security",
3
- "version": "2.3.0",
4
- "description": "ESLint plugin for JWT security \u2014 detects algorithm confusion (CVE-2022-23540), alg:none, weak or hardcoded secrets, and decode-without-verify.",
3
+ "version": "2.3.1",
4
+ "description": "ESLint plugin for JWT security — detects algorithm confusion (CVE-2022-23540), alg:none, weak or hardcoded secrets, and decode-without-verify.",
5
5
  "type": "commonjs",
6
- "main": "./dist/src/index.js",
7
- "types": "./dist/src/index.d.ts",
6
+ "main": "./src/index.js",
7
+ "types": "./src/index.d.ts",
8
8
  "exports": {
9
9
  ".": {
10
- "types": "./dist/src/index.d.ts",
11
- "default": "./dist/src/index.js"
10
+ "types": "./src/index.d.ts",
11
+ "default": "./src/index.js"
12
12
  },
13
13
  "./types": {
14
14
  "types": "./src/types/index.d.ts",
@@ -24,7 +24,7 @@
24
24
  "homepage": "https://eslint.interlace.tools/docs/security/plugin-jwt-security?utm_source=npm&utm_medium=referral&utm_campaign=eslint-plugin-jwt-security-security",
25
25
  "repository": {
26
26
  "type": "git",
27
- "url": "https://github.com/ofri-peretz/eslint",
27
+ "url": "git+https://github.com/ofri-peretz/eslint.git",
28
28
  "directory": "packages/eslint-plugin-jwt-security"
29
29
  },
30
30
  "bugs": {
@@ -35,11 +35,8 @@
35
35
  },
36
36
  "files": [
37
37
  "src/",
38
- "dist/",
39
38
  "README.md",
40
- "LICENSE",
41
- "CHANGELOG.md",
42
- "AGENTS.md"
39
+ "LICENSE"
43
40
  ],
44
41
  "keywords": [
45
42
  "eslint",
@@ -82,11 +79,16 @@
82
79
  "node": ">=18.0.0"
83
80
  },
84
81
  "dependencies": {
85
- "tslib": "^2.3.0",
86
- "@interlace/eslint-devkit": "^1.4.3"
82
+ "@interlace/eslint-devkit": "^1.8.0"
87
83
  },
88
84
  "peerDependencies": {
89
- "eslint": "^8.0.0 || ^9.0.0 || ^10.0.0"
85
+ "eslint": "^8.0.0 || ^9.0.0 || ^10.0.0",
86
+ "jsonwebtoken": "^8.0.0 || ^9.0.0",
87
+ "@nestjs/jwt": "^9.0.0 || ^10.0.0 || ^11.0.0",
88
+ "express-jwt": "^7.0.0 || ^8.0.0",
89
+ "jose": "^4.0.0 || ^5.0.0 || ^6.0.0",
90
+ "jwks-rsa": "^3.0.0 || ^4.0.0",
91
+ "jwt-decode": "^3.0.0 || ^4.0.0"
90
92
  },
91
93
  "peerDependenciesMeta": {
92
94
  "jsonwebtoken": {
@@ -108,16 +110,6 @@
108
110
  "optional": true
109
111
  }
110
112
  },
111
- "devDependencies": {
112
- "@typescript-eslint/parser": "^8.46.2",
113
- "@typescript-eslint/rule-tester": "^8.46.2"
114
- },
115
- "scripts": {
116
- "build": "tsx ../../scripts/build-package.ts",
117
- "test": "vitest run",
118
- "test:coverage": "vitest run --coverage",
119
- "typecheck": "tsc -p tsconfig.lib.json --noEmit"
120
- },
121
113
  "funding": {
122
114
  "type": "github",
123
115
  "url": "https://github.com/ofri-peretz/eslint"
package/src/index.d.ts ADDED
@@ -0,0 +1,17 @@
1
+ // Minimal declaration written by scripts/build-package.ts.
2
+ //
3
+ // tsc's generated entry declaration inlined every inferred rule-option type
4
+ // (up to 166 kB per plugin) for an API no consumer calls directly. This types
5
+ // what a flat config actually touches. Per-rule option types remain available
6
+ // from the "./types" subpath export where a plugin provides one.
7
+ import type { Linter, Rule } from 'eslint';
8
+
9
+ export declare const rules: Record<string, Rule.RuleModule>;
10
+ export declare const configs: Record<string, Linter.Config>;
11
+ export declare const plugin: {
12
+ meta: { name: string; version: string };
13
+ rules: Record<string, Rule.RuleModule>;
14
+ };
15
+
16
+ declare const _default: typeof plugin;
17
+ export default _default;
@@ -1,12 +1,6 @@
1
1
  "use strict";
2
- /**
3
- * Copyright (c) 2025 Ofri Peretz
4
- * Licensed under the MIT License. Use of this source code is governed by the
5
- * MIT license that can be found in the LICENSE file.
6
- */
7
2
  Object.defineProperty(exports, "__esModule", { value: true });
8
3
  exports.noSensitivePayload = exports.requireMaxAge = exports.noTimestampManipulation = exports.requireAudienceValidation = exports.requireIssuerValidation = exports.requireIssuedAt = exports.noHardcodedSecret = exports.requireExpiration = exports.noWeakSecret = exports.noDecodeWithoutVerify = exports.requireAlgorithmWhitelist = exports.noAlgorithmConfusion = exports.noAlgorithmNone = exports.configs = exports.plugin = exports.rules = void 0;
9
- // Import rules - Core Security
10
4
  const no_algorithm_none_1 = require("./rules/no-algorithm-none");
11
5
  Object.defineProperty(exports, "noAlgorithmNone", { enumerable: true, get: function () { return no_algorithm_none_1.noAlgorithmNone; } });
12
6
  const no_algorithm_confusion_1 = require("./rules/no-algorithm-confusion");
@@ -21,7 +15,6 @@ const require_expiration_1 = require("./rules/require-expiration");
21
15
  Object.defineProperty(exports, "requireExpiration", { enumerable: true, get: function () { return require_expiration_1.requireExpiration; } });
22
16
  const no_hardcoded_secret_1 = require("./rules/no-hardcoded-secret");
23
17
  Object.defineProperty(exports, "noHardcodedSecret", { enumerable: true, get: function () { return no_hardcoded_secret_1.noHardcodedSecret; } });
24
- // Import rules - 2025 Vulnerability Research
25
18
  const require_issued_at_1 = require("./rules/require-issued-at");
26
19
  Object.defineProperty(exports, "requireIssuedAt", { enumerable: true, get: function () { return require_issued_at_1.requireIssuedAt; } });
27
20
  const require_issuer_validation_1 = require("./rules/require-issuer-validation");
@@ -34,60 +27,41 @@ const require_max_age_1 = require("./rules/require-max-age");
34
27
  Object.defineProperty(exports, "requireMaxAge", { enumerable: true, get: function () { return require_max_age_1.requireMaxAge; } });
35
28
  const no_sensitive_payload_1 = require("./rules/no-sensitive-payload");
36
29
  Object.defineProperty(exports, "noSensitivePayload", { enumerable: true, get: function () { return no_sensitive_payload_1.noSensitivePayload; } });
37
- /**
38
- * Collection of all JWT security ESLint rules (13 rules)
39
- */
40
30
  exports.rules = {
41
- // Core Security Rules
42
- 'no-algorithm-none': no_algorithm_none_1.noAlgorithmNone,
43
- 'no-algorithm-confusion': no_algorithm_confusion_1.noAlgorithmConfusion,
44
- 'require-algorithm-whitelist': require_algorithm_whitelist_1.requireAlgorithmWhitelist,
45
- 'no-decode-without-verify': no_decode_without_verify_1.noDecodeWithoutVerify,
46
- 'no-weak-secret': no_weak_secret_1.noWeakSecret,
47
- 'require-expiration': require_expiration_1.requireExpiration,
48
- 'no-hardcoded-secret': no_hardcoded_secret_1.noHardcodedSecret,
49
- // 2025 Vulnerability Research - "Back to the Future" & Replay Prevention
50
- 'require-issued-at': require_issued_at_1.requireIssuedAt,
51
- 'require-issuer-validation': require_issuer_validation_1.requireIssuerValidation,
52
- 'require-audience-validation': require_audience_validation_1.requireAudienceValidation,
53
- 'no-timestamp-manipulation': no_timestamp_manipulation_1.noTimestampManipulation,
54
- 'require-max-age': require_max_age_1.requireMaxAge,
55
- 'no-sensitive-payload': no_sensitive_payload_1.noSensitivePayload,
31
+ get 'no-algorithm-none'() { return require("./rules/no-algorithm-none").noAlgorithmNone; },
32
+ get 'no-algorithm-confusion'() { return require("./rules/no-algorithm-confusion").noAlgorithmConfusion; },
33
+ get 'require-algorithm-whitelist'() { return require("./rules/require-algorithm-whitelist").requireAlgorithmWhitelist; },
34
+ get 'no-decode-without-verify'() { return require("./rules/no-decode-without-verify").noDecodeWithoutVerify; },
35
+ get 'no-weak-secret'() { return require("./rules/no-weak-secret").noWeakSecret; },
36
+ get 'require-expiration'() { return require("./rules/require-expiration").requireExpiration; },
37
+ get 'no-hardcoded-secret'() { return require("./rules/no-hardcoded-secret").noHardcodedSecret; },
38
+ get 'require-issued-at'() { return require("./rules/require-issued-at").requireIssuedAt; },
39
+ get 'require-issuer-validation'() { return require("./rules/require-issuer-validation").requireIssuerValidation; },
40
+ get 'require-audience-validation'() { return require("./rules/require-audience-validation").requireAudienceValidation; },
41
+ get 'no-timestamp-manipulation'() { return require("./rules/no-timestamp-manipulation").noTimestampManipulation; },
42
+ get 'require-max-age'() { return require("./rules/require-max-age").requireMaxAge; },
43
+ get 'no-sensitive-payload'() { return require("./rules/no-sensitive-payload").noSensitivePayload; },
44
+
56
45
  };
57
- /**
58
- * ESLint Plugin object
59
- */
60
46
  exports.plugin = {
61
47
  meta: {
62
48
  name: 'eslint-plugin-jwt-security',
63
- version: '2.2.9',
49
+ version: '2.3.1',
64
50
  },
65
51
  rules: exports.rules,
66
52
  };
67
- /**
68
- * Recommended configuration - baseline security
69
- * Critical rules as errors, high as warnings
70
- */
71
53
  const recommendedRules = {
72
- // CRITICAL - Algorithm attacks
73
54
  'jwt/no-algorithm-none': 'error',
74
55
  'jwt/no-algorithm-confusion': 'error',
75
- // HIGH - Verification and secrets
76
56
  'jwt/require-algorithm-whitelist': 'error',
77
57
  'jwt/no-decode-without-verify': 'error',
78
58
  'jwt/no-weak-secret': 'error',
79
59
  'jwt/no-hardcoded-secret': 'error',
80
- 'jwt/no-timestamp-manipulation': 'error', // LightSEC 2025
81
- // MEDIUM - Best practices
60
+ 'jwt/no-timestamp-manipulation': 'error',
82
61
  'jwt/require-expiration': 'error',
83
62
  'jwt/no-sensitive-payload': 'warn',
84
63
  };
85
- /**
86
- * Strict configuration - maximum security (2025 research)
87
- * All rules as errors - includes replay attack prevention
88
- */
89
64
  const strictRules = {
90
- // All core rules as errors
91
65
  'jwt/no-algorithm-none': 'error',
92
66
  'jwt/no-algorithm-confusion': 'error',
93
67
  'jwt/require-algorithm-whitelist': 'error',
@@ -95,7 +69,6 @@ const strictRules = {
95
69
  'jwt/no-weak-secret': 'error',
96
70
  'jwt/no-hardcoded-secret': 'error',
97
71
  'jwt/require-expiration': 'error',
98
- // 2025 Research - Full replay attack prevention
99
72
  'jwt/require-issued-at': 'error',
100
73
  'jwt/require-issuer-validation': 'error',
101
74
  'jwt/require-audience-validation': 'error',
@@ -103,25 +76,12 @@ const strictRules = {
103
76
  'jwt/require-max-age': 'error',
104
77
  'jwt/no-sensitive-payload': 'error',
105
78
  };
106
- /**
107
- * Legacy configuration - migration mode
108
- * Only critical rules enabled
109
- */
110
79
  const legacyRules = {
111
- // Only most critical rules
112
80
  'jwt/no-algorithm-none': 'error',
113
81
  'jwt/no-algorithm-confusion': 'error',
114
82
  'jwt/no-hardcoded-secret': 'warn',
115
83
  };
116
- /**
117
- * Configuration presets for flat config
118
- */
119
84
  exports.configs = {
120
- /**
121
- * Flagship preset — the rule from this plugin in the ecosystem-wide
122
- * flagship list (`.agent/flagship-rules.md`). Use this when you want
123
- * the highest-signal JWT subset shippable in CI gates.
124
- */
125
85
  flagship: {
126
86
  plugins: {
127
87
  jwt: exports.plugin,
@@ -130,36 +90,24 @@ exports.configs = {
130
90
  'jwt/no-algorithm-none': 'error',
131
91
  },
132
92
  },
133
- /**
134
- * Recommended preset - balanced security
135
- */
136
93
  recommended: {
137
94
  plugins: {
138
95
  jwt: exports.plugin,
139
96
  },
140
97
  rules: recommendedRules,
141
98
  },
142
- /**
143
- * Strict preset - maximum security (includes 2025 research)
144
- */
145
99
  strict: {
146
100
  plugins: {
147
101
  jwt: exports.plugin,
148
102
  },
149
103
  rules: strictRules,
150
104
  },
151
- /**
152
- * Legacy preset - migration mode
153
- */
154
105
  legacy: {
155
106
  plugins: {
156
107
  jwt: exports.plugin,
157
108
  },
158
109
  rules: legacyRules,
159
110
  },
160
- /**
161
- * All rules preset
162
- */
163
111
  all: {
164
112
  plugins: {
165
113
  jwt: exports.plugin,
@@ -167,5 +115,4 @@ exports.configs = {
167
115
  rules: strictRules,
168
116
  },
169
117
  };
170
- // Default export for flat config usage
171
118
  exports.default = exports.plugin;
@@ -0,0 +1,17 @@
1
+ // Minimal declaration written by scripts/build-package.ts.
2
+ //
3
+ // tsc's generated entry declaration inlined every inferred rule-option type
4
+ // (up to 166 kB per plugin) for an API no consumer calls directly. This types
5
+ // what a flat config actually touches. Per-rule option types remain available
6
+ // from the "./types" subpath export where a plugin provides one.
7
+ import type { Linter, Rule } from 'eslint';
8
+
9
+ export declare const rules: Record<string, Rule.RuleModule>;
10
+ export declare const configs: Record<string, Linter.Config>;
11
+ export declare const plugin: {
12
+ meta: { name: string; version: string };
13
+ rules: Record<string, Rule.RuleModule>;
14
+ };
15
+
16
+ declare const _default: typeof plugin;
17
+ export default _default;
package/src/oxlint.js ADDED
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ const index_1 = require("./index");
3
+ module.exports = index_1.plugin;
@@ -1,14 +1,8 @@
1
1
  "use strict";
2
- /**
3
- * Copyright (c) 2025 Ofri Peretz
4
- * Licensed under the MIT License. Use of this source code is governed by the
5
- * MIT license that can be found in the LICENSE file.
6
- */
7
2
  Object.defineProperty(exports, "__esModule", { value: true });
8
3
  exports.noAlgorithmConfusion = void 0;
9
4
  const eslint_devkit_1 = require("@interlace/eslint-devkit");
10
5
  const utils_1 = require("../../utils");
11
- // Patterns that indicate a public key
12
6
  const PUBLIC_KEY_PATTERNS = [
13
7
  /public/i,
14
8
  /\.pub$/,
@@ -101,16 +95,10 @@ exports.noAlgorithmConfusion = (0, eslint_devkit_1.createRule)({
101
95
  const options = context.options[0] ?? {};
102
96
  const symmetricAlgSet = new Set(options.symmetricAlgorithms ?? ['HS256', 'HS384', 'HS512']);
103
97
  const sourceCode = context.sourceCode;
104
- /**
105
- * Check if a node looks like a public key reference
106
- */
107
98
  const looksLikePublicKey = (node) => {
108
99
  const text = sourceCode.getText(node);
109
100
  return PUBLIC_KEY_PATTERNS.some((pattern) => pattern.test(text));
110
101
  };
111
- /**
112
- * Check if algorithms include symmetric algorithms
113
- */
114
102
  const hasSymmetricAlgorithm = (algorithms) => {
115
103
  for (const alg of algorithms) {
116
104
  if (symmetricAlgSet.has(alg)) {
@@ -121,26 +109,21 @@ exports.noAlgorithmConfusion = (0, eslint_devkit_1.createRule)({
121
109
  };
122
110
  return {
123
111
  CallExpression(node) {
124
- // Only check verify operations
125
112
  if (!(0, utils_1.isVerifyOperation)(node)) {
126
113
  return;
127
114
  }
128
- // Need at least 2 arguments: token, key
129
115
  if (node.arguments.length < 2) {
130
116
  return;
131
117
  }
132
118
  const keyArg = node.arguments[1];
133
119
  const optionsArg = (0, utils_1.getOptionsArgument)(node, 2);
134
- // Check if key looks like a public key
135
120
  if (!looksLikePublicKey(keyArg)) {
136
121
  return;
137
122
  }
138
- // If there are options, check the algorithms
139
123
  if (optionsArg) {
140
124
  const algorithms = (0, utils_1.extractAlgorithms)(optionsArg);
141
125
  const symmetricAlg = hasSymmetricAlgorithm(algorithms);
142
126
  if (symmetricAlg) {
143
- // Find the algorithm node for precise error location
144
127
  for (const prop of optionsArg.properties) {
145
128
  if (prop.type === 'Property' &&
146
129
  prop.key.type === 'Identifier' &&
@@ -155,9 +138,6 @@ exports.noAlgorithmConfusion = (0, eslint_devkit_1.createRule)({
155
138
  }
156
139
  }
157
140
  }
158
- // If no options specified but key looks like public key,
159
- // and we're in verify, flag as potential issue
160
- // (verification without explicit algorithm with public key is risky)
161
141
  },
162
142
  };
163
143
  },
@@ -1,9 +1,4 @@
1
1
  "use strict";
2
- /**
3
- * Copyright (c) 2025 Ofri Peretz
4
- * Licensed under the MIT License. Use of this source code is governed by the
5
- * MIT license that can be found in the LICENSE file.
6
- */
7
2
  Object.defineProperty(exports, "__esModule", { value: true });
8
3
  exports.noAlgorithmNone = void 0;
9
4
  const eslint_devkit_1 = require("@interlace/eslint-devkit");
@@ -107,20 +102,12 @@ exports.noAlgorithmNone = (0, eslint_devkit_1.createRule)({
107
102
  const options = context.options[0] ?? {};
108
103
  const { allowInTests = false } = options;
109
104
  const filename = context.filename;
110
- // Skip test files if configured
111
105
  if (allowInTests && (0, utils_1.isTestFile)(filename)) {
112
106
  return {};
113
107
  }
114
- /**
115
- * Check if an algorithm value is 'none' (case insensitive)
116
- */
117
- // oxlint-disable-next-line consistent-function-scoping
118
108
  const isNoneAlgorithm = (value) => {
119
109
  return value.toLowerCase() === 'none';
120
110
  };
121
- /**
122
- * Check options object for 'none' algorithm
123
- */
124
111
  const checkOptionsForNone = (optionsNode) => {
125
112
  for (const prop of optionsNode.properties) {
126
113
  if (prop.type !== 'Property' || prop.key.type !== 'Identifier') {
@@ -132,7 +119,6 @@ exports.noAlgorithmNone = (0, eslint_devkit_1.createRule)({
132
119
  keyName !== 'alg') {
133
120
  continue;
134
121
  }
135
- // Single algorithm: { algorithm: 'none' }
136
122
  if (prop.value.type === 'Literal' &&
137
123
  typeof prop.value.value === 'string') {
138
124
  if (isNoneAlgorithm(prop.value.value)) {
@@ -142,9 +128,7 @@ exports.noAlgorithmNone = (0, eslint_devkit_1.createRule)({
142
128
  });
143
129
  }
144
130
  }
145
- // Array of algorithms: { algorithms: ['none'] } or { algorithms: [] }
146
131
  if (prop.value.type === 'ArrayExpression') {
147
- // Check for empty array
148
132
  if (prop.value.elements.length === 0) {
149
133
  context.report({
150
134
  node: prop.value,
@@ -152,7 +136,6 @@ exports.noAlgorithmNone = (0, eslint_devkit_1.createRule)({
152
136
  });
153
137
  continue;
154
138
  }
155
- // Check each element for 'none'
156
139
  for (const elem of prop.value.elements) {
157
140
  if (elem &&
158
141
  elem.type === 'Literal' &&
@@ -167,20 +150,6 @@ exports.noAlgorithmNone = (0, eslint_devkit_1.createRule)({
167
150
  }
168
151
  }
169
152
  };
170
- /**
171
- * Detect `jwt.decode()` calls — closes the audit FN where
172
- * `jwt.decode(token)` followed by trusting the decoded payload was
173
- * silent, despite being functionally equivalent to algorithm:"none".
174
- * See benchmarks/AUDIT_PATTERNS.md §3.8 ("verify-then-trust mismatch").
175
- *
176
- * The check is conservative: we flag every `jwt.decode()` call. In
177
- * the rare case where `decode` is used legitimately (e.g. logging
178
- * the issuer of an inbound token without authentication semantics),
179
- * the user can disable the rule on that line. The default-on
180
- * behaviour is correct — most production uses of `jwt.decode` are
181
- * authentication mistakes.
182
- */
183
- // oxlint-disable-next-line consistent-function-scoping
184
153
  function isDecodeCall(node) {
185
154
  const callee = node.callee;
186
155
  if (callee.type !== 'MemberExpression')
@@ -189,8 +158,6 @@ exports.noAlgorithmNone = (0, eslint_devkit_1.createRule)({
189
158
  return false;
190
159
  if (callee.property.name !== 'decode')
191
160
  return false;
192
- // Object should be `jwt` (or imported as such). Check both
193
- // `jwt.decode` and `JWT.decode` patterns.
194
161
  if (callee.object.type === 'Identifier') {
195
162
  const name = callee.object.name.toLowerCase();
196
163
  return name === 'jwt' || name === 'jsonwebtoken';
@@ -199,18 +166,13 @@ exports.noAlgorithmNone = (0, eslint_devkit_1.createRule)({
199
166
  }
200
167
  return {
201
168
  CallExpression(node) {
202
- // Detect `jwt.decode(...)` — equivalent to algorithm:"none" if
203
- // its result is trusted. We always flag (with critical severity)
204
- // because the FN risk far outweighs the FP risk on real code.
205
169
  if (isDecodeCall(node)) {
206
170
  context.report({ node, messageId: 'decodeWithoutVerify' });
207
171
  return;
208
172
  }
209
- // Check both verify and sign operations
210
173
  if (!(0, utils_1.isVerifyOperation)(node) && !(0, utils_1.isSignOperation)(node)) {
211
174
  return;
212
175
  }
213
- // Get options argument (usually 3rd argument)
214
176
  const optionsArg = (0, utils_1.getOptionsArgument)(node, 2);
215
177
  if (optionsArg) {
216
178
  checkOptionsForNone(optionsArg);
@@ -1,9 +1,4 @@
1
1
  "use strict";
2
- /**
3
- * Copyright (c) 2025 Ofri Peretz
4
- * Licensed under the MIT License. Use of this source code is governed by the
5
- * MIT license that can be found in the LICENSE file.
6
- */
7
2
  Object.defineProperty(exports, "__esModule", { value: true });
8
3
  exports.noDecodeWithoutVerify = void 0;
9
4
  const eslint_devkit_1 = require("@interlace/eslint-devkit");
@@ -87,12 +82,7 @@ exports.noDecodeWithoutVerify = (0, eslint_devkit_1.createRule)({
87
82
  create(context) {
88
83
  const options = context.options[0] ?? {};
89
84
  const { trustedAnnotations = [] } = options;
90
- /**
91
- * Check if this is a jwt-decode import usage
92
- */
93
- // oxlint-disable-next-line consistent-function-scoping
94
85
  const isJwtDecodeLibrary = (node) => {
95
- // Direct call: jwtDecode(token)
96
86
  if (node.callee.type === 'Identifier' &&
97
87
  (node.callee.name === 'jwtDecode' || node.callee.name === 'jwt_decode')) {
98
88
  return true;
@@ -101,9 +91,7 @@ exports.noDecodeWithoutVerify = (0, eslint_devkit_1.createRule)({
101
91
  };
102
92
  return {
103
93
  CallExpression(node) {
104
- // Check for jwt.decode() pattern
105
94
  if ((0, utils_1.isDecodeOperation)(node)) {
106
- // Check for safe annotations
107
95
  if ((0, eslint_devkit_1.hasSafeAnnotation)(node, context, trustedAnnotations)) {
108
96
  return;
109
97
  }
@@ -113,9 +101,7 @@ exports.noDecodeWithoutVerify = (0, eslint_devkit_1.createRule)({
113
101
  });
114
102
  return;
115
103
  }
116
- // Check for jwt-decode library usage
117
104
  if (isJwtDecodeLibrary(node)) {
118
- // Check for safe annotations
119
105
  if ((0, eslint_devkit_1.hasSafeAnnotation)(node, context, trustedAnnotations)) {
120
106
  return;
121
107
  }
@@ -1,9 +1,4 @@
1
1
  "use strict";
2
- /**
3
- * Copyright (c) 2025 Ofri Peretz
4
- * Licensed under the MIT License. Use of this source code is governed by the
5
- * MIT license that can be found in the LICENSE file.
6
- */
7
2
  Object.defineProperty(exports, "__esModule", { value: true });
8
3
  exports.noHardcodedSecret = void 0;
9
4
  const eslint_devkit_1 = require("@interlace/eslint-devkit");
@@ -77,29 +72,16 @@ exports.noHardcodedSecret = (0, eslint_devkit_1.createRule)({
77
72
  },
78
73
  ],
79
74
  create(context) {
80
- /**
81
- * Check if a node is a hardcoded string literal
82
- */
83
- // oxlint-disable-next-line consistent-function-scoping
84
75
  const isHardcodedString = (node) => {
85
- // String literal
86
76
  if (node.type === 'Literal' && typeof node.value === 'string') {
87
77
  return true;
88
78
  }
89
- // Template literal without expressions
90
79
  if (node.type === 'TemplateLiteral' &&
91
80
  node.expressions.length === 0) {
92
81
  return true;
93
82
  }
94
83
  return false;
95
84
  };
96
- /**
97
- * Resolve an Identifier node to its initializer (one frame of indirection).
98
- * Closes the audit FN where `const SECRET = 'x'; jwt.sign(p, SECRET)`
99
- * was treated as safe — the literal-via-const pattern hides the
100
- * hardcoded secret behind one declaration. See benchmarks/AUDIT_PATTERNS.md
101
- * §3.2 ("Indirection through one level of variable").
102
- */
103
85
  const resolveConstLiteralValue = (node) => {
104
86
  if (node.type !== 'Identifier')
105
87
  return null;
@@ -115,45 +97,28 @@ exports.noHardcodedSecret = (0, eslint_devkit_1.createRule)({
115
97
  return null;
116
98
  return def.node.init ?? null;
117
99
  };
118
- /**
119
- * Check if node is a safe key source
120
- */
121
100
  const isSafeKeySource = (node) => {
122
- // Environment variable
123
101
  if ((0, utils_1.isEnvVariable)(node)) {
124
102
  return true;
125
103
  }
126
- // Function call (getSecret(), loadKey(), etc.)
127
104
  if (node.type === 'CallExpression') {
128
105
  return true;
129
106
  }
130
- // await expression (async key loading)
131
107
  if (node.type === 'AwaitExpression') {
132
108
  return true;
133
109
  }
134
- // Variable reference: resolve `const X = ...` one level. If X is a
135
- // hardcoded literal, treat as unsafe — fall through to the
136
- // hardcoded check. Otherwise (env var, call, identifier chain)
137
- // it's safe.
138
110
  if (node.type === 'Identifier') {
139
111
  const init = resolveConstLiteralValue(node);
140
112
  if (init && isHardcodedString(init)) {
141
- // Unsafe — let the caller's `isHardcodedString` (with the
142
- // resolved init) flag it.
143
113
  return false;
144
114
  }
145
115
  return true;
146
116
  }
147
- // Member expression but not literal (config.secret, etc.)
148
117
  if (node.type === 'MemberExpression' && !isHardcodedString(node)) {
149
118
  return true;
150
119
  }
151
120
  return false;
152
121
  };
153
- /**
154
- * Like isHardcodedString but resolves single-level `const X = '...'`
155
- * indirection so the caller fires on `const SECRET = 'x'; jwt.sign(p, SECRET)`.
156
- */
157
122
  const isHardcodedStringOrResolvedConst = (node) => {
158
123
  if (isHardcodedString(node))
159
124
  return true;
@@ -162,20 +127,16 @@ exports.noHardcodedSecret = (0, eslint_devkit_1.createRule)({
162
127
  };
163
128
  return {
164
129
  CallExpression(node) {
165
- // Check both sign and verify operations
166
130
  if (!(0, utils_1.isSignOperation)(node) && !(0, utils_1.isVerifyOperation)(node)) {
167
131
  return;
168
132
  }
169
- // Secret/key is the second argument
170
133
  if (node.arguments.length < 2) {
171
134
  return;
172
135
  }
173
136
  const secretArg = node.arguments[1];
174
- // Skip if safe source
175
137
  if (isSafeKeySource(secretArg)) {
176
138
  return;
177
139
  }
178
- // Flag hardcoded strings (also follows single-frame `const X = '...'`).
179
140
  if (isHardcodedStringOrResolvedConst(secretArg)) {
180
141
  context.report({
181
142
  node: secretArg,
@@ -1,9 +1,4 @@
1
1
  "use strict";
2
- /**
3
- * Copyright (c) 2025 Ofri Peretz
4
- * Licensed under the MIT License. Use of this source code is governed by the
5
- * MIT license that can be found in the LICENSE file.
6
- */
7
2
  Object.defineProperty(exports, "__esModule", { value: true });
8
3
  exports.noSensitivePayload = void 0;
9
4
  const eslint_devkit_1 = require("@interlace/eslint-devkit");
@@ -81,7 +76,6 @@ exports.noSensitivePayload = (0, eslint_devkit_1.createRule)({
81
76
  return;
82
77
  }
83
78
  const payloadArg = node.arguments[0];
84
- // Check object literal payload
85
79
  if (payloadArg.type === 'ObjectExpression') {
86
80
  for (const prop of payloadArg.properties) {
87
81
  if (prop.type === 'Property' && prop.key.type === 'Identifier') {