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
@@ -1,98 +0,0 @@
1
- /**
2
- * Copyright (c) 2025 Ofri Peretz
3
- * Licensed under the MIT License. Use of this source code is governed by the
4
- * MIT license that can be found in the LICENSE file.
5
- */
6
- /**
7
- * Shared utilities for JWT security rules
8
- *
9
- * Library detection, pattern matching, and common helpers.
10
- */
11
- import type { TSESTree } from '@interlace/eslint-devkit';
12
- /**
13
- * Supported JWT libraries
14
- */
15
- export declare const JWT_LIBRARIES: {
16
- readonly JSONWEBTOKEN: "jsonwebtoken";
17
- readonly JOSE: "jose";
18
- readonly EXPRESS_JWT: "express-jwt";
19
- readonly NESTJS_JWT: "@nestjs/jwt";
20
- readonly JWKS_RSA: "jwks-rsa";
21
- readonly JWT_DECODE: "jwt-decode";
22
- };
23
- export type JwtLibrary = (typeof JWT_LIBRARIES)[keyof typeof JWT_LIBRARIES];
24
- /**
25
- * Insecure algorithms that should be flagged
26
- */
27
- export declare const INSECURE_ALGORITHMS: Set<string>;
28
- /**
29
- * Algorithms vulnerable to confusion attacks when used with asymmetric keys
30
- */
31
- export declare const SYMMETRIC_ALGORITHMS: Set<string>;
32
- /**
33
- * Recommended secure algorithms
34
- */
35
- export declare const SECURE_ALGORITHMS: Set<string>;
36
- /**
37
- * Sensitive field names that should not be in JWT payload
38
- * All lowercase for case-insensitive matching
39
- */
40
- export declare const SENSITIVE_PAYLOAD_FIELDS: Set<string>;
41
- /**
42
- * JWT method patterns for different operations
43
- */
44
- export declare const JWT_METHODS: {
45
- readonly SIGN: Set<string>;
46
- readonly VERIFY: Set<string>;
47
- readonly DECODE: Set<string>;
48
- };
49
- /**
50
- * Check if a node represents a call to a JWT library method
51
- */
52
- export declare function isJwtLibraryCall(node: TSESTree.CallExpression, targetMethods: Set<string>): boolean;
53
- /**
54
- * Check if a node is a string literal with a specific value
55
- */
56
- export declare function isStringLiteral(node: TSESTree.Node, value?: string): node is TSESTree.Literal;
57
- /**
58
- * Extract algorithm from options object
59
- */
60
- export declare function extractAlgorithms(optionsNode: TSESTree.ObjectExpression): string[];
61
- /**
62
- * Check if options object has a specific property set
63
- */
64
- export declare function hasOption(optionsNode: TSESTree.ObjectExpression, optionName: string): boolean;
65
- /**
66
- * Get the value of a specific option from options object
67
- */
68
- export declare function getOptionValue(optionsNode: TSESTree.ObjectExpression, optionName: string): TSESTree.Node | undefined;
69
- /**
70
- * Check if a literal appears to be a weak secret (short string)
71
- */
72
- export declare function isWeakSecret(node: TSESTree.Node, minLength?: number): boolean;
73
- /**
74
- * Check if a node is an environment variable access (safe pattern)
75
- */
76
- export declare function isEnvVariable(node: TSESTree.Node): boolean;
77
- /**
78
- * Check if this call looks like a JWT sign operation
79
- */
80
- export declare function isSignOperation(node: TSESTree.CallExpression): boolean;
81
- /**
82
- * Check if this call looks like a JWT verify operation
83
- */
84
- export declare function isVerifyOperation(node: TSESTree.CallExpression): boolean;
85
- /**
86
- * Check if this call looks like a JWT decode operation (no verification)
87
- */
88
- export declare function isDecodeOperation(node: TSESTree.CallExpression): boolean;
89
- /**
90
- * Get the options argument from a JWT call
91
- * For jwt.verify(token, secret, options) -> returns options
92
- * For jwt.sign(payload, secret, options) -> returns options
93
- */
94
- export declare function getOptionsArgument(node: TSESTree.CallExpression, optionsIndex?: number): TSESTree.ObjectExpression | undefined;
95
- /**
96
- * Check if file is a test file
97
- */
98
- export declare function isTestFile(filename: string): boolean;
@@ -1,192 +0,0 @@
1
- /**
2
- * JWT Ecosystem Interface Compatibility Tests
3
- *
4
- * Verifies that JWT-related packages export the interfaces our ESLint rules depend on.
5
- *
6
- * @sdk jsonwebtoken, jose, express-jwt, jwks-rsa, jwt-decode
7
- * @last-updated 2026-01-02
8
- */
9
-
10
- import { describe, it, expect, beforeAll } from 'vitest';
11
-
12
- // Use unknown types to avoid ESM/CJS type conflicts at compile time
13
- // Runtime checks handle the actual exports
14
- let jsonwebtokenModule: unknown;
15
- let joseModule: unknown;
16
- let jwtDecodeModule: unknown;
17
-
18
- // Helper to safely access properties on unknown types
19
- const getProp = (obj: unknown, key: string): unknown => {
20
- if (obj && typeof obj === 'object' && key in obj) {
21
- return (obj as Record<string, unknown>)[key];
22
- }
23
- return undefined;
24
- };
25
-
26
- beforeAll(async () => {
27
- try {
28
- jsonwebtokenModule = await import('jsonwebtoken');
29
- } catch {
30
- console.warn('jsonwebtoken not installed');
31
- }
32
- try {
33
- joseModule = await import('jose');
34
- } catch {
35
- console.warn('jose not installed');
36
- }
37
- try {
38
- jwtDecodeModule = await import('jwt-decode');
39
- } catch {
40
- console.warn('jwt-decode not installed');
41
- }
42
- }, 30_000); // native-addon packages can take >10s to load cold
43
-
44
- // ═══════════════════════════════════════════════════════════════════════════════
45
- // JSONWEBTOKEN INTERFACE
46
- // Primary package for JWT operations in Node.js
47
- // ═══════════════════════════════════════════════════════════════════════════════
48
-
49
- describe('jsonwebtoken Interface Compatibility', () => {
50
- describe('Core Exports', () => {
51
- it('exports sign function', () => {
52
- const sign = getProp(jsonwebtokenModule, 'sign');
53
- expect(sign).toBeDefined();
54
- expect(typeof sign).toBe('function');
55
- });
56
-
57
- it('exports verify function', () => {
58
- const verify = getProp(jsonwebtokenModule, 'verify');
59
- expect(verify).toBeDefined();
60
- expect(typeof verify).toBe('function');
61
- });
62
-
63
- it('exports decode function', () => {
64
- const decode = getProp(jsonwebtokenModule, 'decode');
65
- expect(decode).toBeDefined();
66
- expect(typeof decode).toBe('function');
67
- });
68
- });
69
-
70
- describe('Sign Options (Critical for Security Rules)', () => {
71
- it('sign accepts algorithm option', () => {
72
- // Rules check: no-algorithm-none, require-algorithm-whitelist
73
- expect(getProp(jsonwebtokenModule, 'sign')).toBeDefined();
74
- });
75
-
76
- it('sign accepts expiresIn option', () => {
77
- // Rules check: require-expiration
78
- expect(getProp(jsonwebtokenModule, 'sign')).toBeDefined();
79
- });
80
-
81
- it('sign accepts audience option', () => {
82
- // Rules check: require-audience
83
- expect(getProp(jsonwebtokenModule, 'sign')).toBeDefined();
84
- });
85
-
86
- it('sign accepts issuer option', () => {
87
- // Rules check: require-issuer
88
- expect(getProp(jsonwebtokenModule, 'sign')).toBeDefined();
89
- });
90
- });
91
-
92
- describe('Verify Options (Critical for Security Rules)', () => {
93
- it('verify accepts algorithms option (array)', () => {
94
- // Rules check: verify-algorithm-explicit
95
- expect(getProp(jsonwebtokenModule, 'verify')).toBeDefined();
96
- });
97
-
98
- it('verify accepts complete option', () => {
99
- // Returns full decoded token with header
100
- expect(getProp(jsonwebtokenModule, 'verify')).toBeDefined();
101
- });
102
- });
103
- });
104
-
105
- // ═══════════════════════════════════════════════════════════════════════════════
106
- // JOSE INTERFACE
107
- // Modern JWT library with async/await support
108
- // ═══════════════════════════════════════════════════════════════════════════════
109
-
110
- describe('jose Interface Compatibility', () => {
111
- describe('Core Exports', () => {
112
- it('exports SignJWT class', () => {
113
- expect(getProp(joseModule, 'SignJWT')).toBeDefined();
114
- });
115
-
116
- it('exports jwtVerify function', () => {
117
- const jwtVerify = getProp(joseModule, 'jwtVerify');
118
- expect(jwtVerify).toBeDefined();
119
- expect(typeof jwtVerify).toBe('function');
120
- });
121
-
122
- it('exports jwtDecrypt function', () => {
123
- const jwtDecrypt = getProp(joseModule, 'jwtDecrypt');
124
- expect(jwtDecrypt).toBeDefined();
125
- expect(typeof jwtDecrypt).toBe('function');
126
- });
127
-
128
- it('exports createRemoteJWKSet function', () => {
129
- // Used for JWKS validation
130
- const createRemoteJWKSet = getProp(joseModule, 'createRemoteJWKSet');
131
- expect(createRemoteJWKSet).toBeDefined();
132
- expect(typeof createRemoteJWKSet).toBe('function');
133
- });
134
- });
135
-
136
- describe('Key Generation', () => {
137
- it('exports generateKeyPair function', () => {
138
- expect(getProp(joseModule, 'generateKeyPair')).toBeDefined();
139
- });
140
-
141
- it('exports generateSecret function', () => {
142
- expect(getProp(joseModule, 'generateSecret')).toBeDefined();
143
- });
144
- });
145
- });
146
-
147
- // ═══════════════════════════════════════════════════════════════════════════════
148
- // JWT-DECODE INTERFACE
149
- // Client-side JWT decoding (no verification)
150
- // ═══════════════════════════════════════════════════════════════════════════════
151
-
152
- describe('jwt-decode Interface Compatibility', () => {
153
- describe('Core Exports', () => {
154
- it('exports jwtDecode function', () => {
155
- // Can be either named export or default export depending on version
156
- const jwtDecode = getProp(jwtDecodeModule, 'jwtDecode') ?? getProp(jwtDecodeModule, 'default');
157
- expect(jwtDecode).toBeDefined();
158
- });
159
-
160
- it('exports InvalidTokenError', () => {
161
- expect(getProp(jwtDecodeModule, 'InvalidTokenError')).toBeDefined();
162
- });
163
- });
164
- });
165
-
166
- // ═══════════════════════════════════════════════════════════════════════════════
167
- // PACKAGE METADATA
168
- // ═══════════════════════════════════════════════════════════════════════════════
169
-
170
- describe('Package Metadata', () => {
171
- it('jsonwebtoken has discoverable version', async () => {
172
- try {
173
- const pkgPath = require.resolve('jsonwebtoken/package.json');
174
- const pkg = await import(pkgPath, { with: { type: 'json' } }).then(m => m.default);
175
- expect(pkg.version).toBeDefined();
176
- console.log(`📦 jsonwebtoken version: ${pkg.version}`);
177
- } catch {
178
- console.log('📦 jsonwebtoken not installed');
179
- }
180
- });
181
-
182
- it('jose has discoverable version', async () => {
183
- try {
184
- const pkgPath = require.resolve('jose/package.json');
185
- const pkg = await import(pkgPath, { with: { type: 'json' } }).then(m => m.default);
186
- expect(pkg.version).toBeDefined();
187
- console.log(`📦 jose version: ${pkg.version}`);
188
- } catch {
189
- console.log('📦 jose not installed');
190
- }
191
- });
192
- });
package/src/index.test.ts DELETED
@@ -1,47 +0,0 @@
1
- import { describe, it, expect } from 'vitest';
2
- import plugin from './index';
3
- import { rules, configs } from './index';
4
-
5
- describe('eslint-plugin-jwt-security plugin interface', () => {
6
- it('should export correct meta information', () => {
7
- expect(plugin.meta).toBeDefined();
8
- expect(plugin.meta?.name).toBe('eslint-plugin-jwt-security');
9
- expect(plugin.meta?.version).toBeDefined();
10
- });
11
-
12
- it('should export all JWT security rules', () => {
13
- expect(plugin.rules).toBeDefined();
14
- const ruleNames = Object.keys(plugin.rules || {});
15
- expect(ruleNames).toContain('no-algorithm-none');
16
- expect(ruleNames).toContain('no-algorithm-confusion');
17
- expect(ruleNames).toContain('no-timestamp-manipulation');
18
- expect(ruleNames.length).toBe(13);
19
- });
20
-
21
- describe('configurations', () => {
22
- it('should provide recommended configuration', () => {
23
- expect(configs.recommended).toBeDefined();
24
- expect(configs.recommended.plugins?.jwt).toBeDefined();
25
-
26
- const recommendedRules = configs.recommended.rules || {};
27
- Object.keys(recommendedRules).forEach(ruleName => {
28
- expect(ruleName).toMatch(/^jwt\//);
29
- });
30
-
31
- expect(recommendedRules['jwt/no-algorithm-none']).toBe('error');
32
- });
33
-
34
- it('should provide strict configuration', () => {
35
- expect(configs.strict).toBeDefined();
36
- expect(configs.strict.plugins?.jwt).toBeDefined();
37
-
38
- const strictRules = configs.strict.rules || {};
39
- Object.keys(strictRules).forEach(ruleName => {
40
- expect(ruleName).toMatch(/^jwt\//);
41
- });
42
-
43
- expect(strictRules['jwt/no-algorithm-none']).toBe('error');
44
- expect(Object.keys(strictRules).length).toBe(Object.keys(rules).length);
45
- });
46
- });
47
- });
package/src/index.ts DELETED
@@ -1,209 +0,0 @@
1
- /**
2
- * Copyright (c) 2025 Ofri Peretz
3
- * Licensed under the MIT License. Use of this source code is governed by the
4
- * MIT license that can be found in the LICENSE file.
5
- */
6
-
7
- /**
8
- * eslint-plugin-jwt-security
9
- *
10
- * Comprehensive JWT security ESLint plugin for Node.js applications.
11
- * Detects vulnerabilities across jsonwebtoken, jose, express-jwt,
12
- * @nestjs/jwt, jwks-rsa, and jwt-decode libraries.
13
- *
14
- * Features:
15
- * - Algorithm confusion attack detection (CVE-2022-23540)
16
- * - "Back to the Future" replay attack prevention (LightSEC 2025)
17
- * - Signature verification enforcement
18
- * - Weak secret detection
19
- * - Expiration and claim validation
20
- * - AI-assisted fix guidance with CWE references
21
- *
22
- * @see https://tools.ietf.org/html/rfc8725
23
- * @see https://securitypattern.com/post/jwt-back-to-the-future
24
- */
25
- import type { TSESLint } from '@interlace/eslint-devkit';
26
-
27
- // Import rules - Core Security
28
- import { noAlgorithmNone } from './rules/no-algorithm-none';
29
- import { noAlgorithmConfusion } from './rules/no-algorithm-confusion';
30
- import { requireAlgorithmWhitelist } from './rules/require-algorithm-whitelist';
31
- import { noDecodeWithoutVerify } from './rules/no-decode-without-verify';
32
- import { noWeakSecret } from './rules/no-weak-secret';
33
- import { requireExpiration } from './rules/require-expiration';
34
- import { noHardcodedSecret } from './rules/no-hardcoded-secret';
35
-
36
- // Import rules - 2025 Vulnerability Research
37
- import { requireIssuedAt } from './rules/require-issued-at';
38
- import { requireIssuerValidation } from './rules/require-issuer-validation';
39
- import { requireAudienceValidation } from './rules/require-audience-validation';
40
- import { noTimestampManipulation } from './rules/no-timestamp-manipulation';
41
- import { requireMaxAge } from './rules/require-max-age';
42
- import { noSensitivePayload } from './rules/no-sensitive-payload';
43
-
44
- /**
45
- * Collection of all JWT security ESLint rules (13 rules)
46
- */
47
- export const rules: Record<string, TSESLint.RuleModule<string, readonly unknown[]>> = {
48
- // Core Security Rules
49
- 'no-algorithm-none': noAlgorithmNone,
50
- 'no-algorithm-confusion': noAlgorithmConfusion,
51
- 'require-algorithm-whitelist': requireAlgorithmWhitelist,
52
- 'no-decode-without-verify': noDecodeWithoutVerify,
53
- 'no-weak-secret': noWeakSecret,
54
- 'require-expiration': requireExpiration,
55
- 'no-hardcoded-secret': noHardcodedSecret,
56
-
57
- // 2025 Vulnerability Research - "Back to the Future" & Replay Prevention
58
- 'require-issued-at': requireIssuedAt,
59
- 'require-issuer-validation': requireIssuerValidation,
60
- 'require-audience-validation': requireAudienceValidation,
61
- 'no-timestamp-manipulation': noTimestampManipulation,
62
- 'require-max-age': requireMaxAge,
63
- 'no-sensitive-payload': noSensitivePayload,
64
- } satisfies Record<string, TSESLint.RuleModule<string, readonly unknown[]>>;
65
-
66
- /**
67
- * ESLint Plugin object
68
- */
69
- export const plugin: TSESLint.FlatConfig.Plugin = {
70
- meta: {
71
- name: 'eslint-plugin-jwt-security',
72
- version: '2.2.9',
73
- },
74
- rules,
75
- } satisfies TSESLint.FlatConfig.Plugin;
76
-
77
- /**
78
- * Recommended configuration - baseline security
79
- * Critical rules as errors, high as warnings
80
- */
81
- const recommendedRules: Record<string, TSESLint.FlatConfig.RuleEntry> = {
82
- // CRITICAL - Algorithm attacks
83
- 'jwt/no-algorithm-none': 'error',
84
- 'jwt/no-algorithm-confusion': 'error',
85
-
86
- // HIGH - Verification and secrets
87
- 'jwt/require-algorithm-whitelist': 'error',
88
- 'jwt/no-decode-without-verify': 'error',
89
- 'jwt/no-weak-secret': 'error',
90
- 'jwt/no-hardcoded-secret': 'error',
91
- 'jwt/no-timestamp-manipulation': 'error', // LightSEC 2025
92
-
93
- // MEDIUM - Best practices
94
- 'jwt/require-expiration': 'error',
95
- 'jwt/no-sensitive-payload': 'warn',
96
- };
97
-
98
- /**
99
- * Strict configuration - maximum security (2025 research)
100
- * All rules as errors - includes replay attack prevention
101
- */
102
- const strictRules: Record<string, TSESLint.FlatConfig.RuleEntry> = {
103
- // All core rules as errors
104
- 'jwt/no-algorithm-none': 'error',
105
- 'jwt/no-algorithm-confusion': 'error',
106
- 'jwt/require-algorithm-whitelist': 'error',
107
- 'jwt/no-decode-without-verify': 'error',
108
- 'jwt/no-weak-secret': 'error',
109
- 'jwt/no-hardcoded-secret': 'error',
110
- 'jwt/require-expiration': 'error',
111
-
112
- // 2025 Research - Full replay attack prevention
113
- 'jwt/require-issued-at': 'error',
114
- 'jwt/require-issuer-validation': 'error',
115
- 'jwt/require-audience-validation': 'error',
116
- 'jwt/no-timestamp-manipulation': 'error',
117
- 'jwt/require-max-age': 'error',
118
- 'jwt/no-sensitive-payload': 'error',
119
- };
120
-
121
- /**
122
- * Legacy configuration - migration mode
123
- * Only critical rules enabled
124
- */
125
- const legacyRules: Record<string, TSESLint.FlatConfig.RuleEntry> = {
126
- // Only most critical rules
127
- 'jwt/no-algorithm-none': 'error',
128
- 'jwt/no-algorithm-confusion': 'error',
129
- 'jwt/no-hardcoded-secret': 'warn',
130
- };
131
-
132
- /**
133
- * Configuration presets for flat config
134
- */
135
- export const configs = {
136
- /**
137
- * Flagship preset — the rule from this plugin in the ecosystem-wide
138
- * flagship list (`.agent/flagship-rules.md`). Use this when you want
139
- * the highest-signal JWT subset shippable in CI gates.
140
- */
141
- flagship: {
142
- plugins: {
143
- jwt: plugin,
144
- },
145
- rules: {
146
- 'jwt/no-algorithm-none': 'error',
147
- },
148
- } satisfies TSESLint.FlatConfig.Config,
149
-
150
- /**
151
- * Recommended preset - balanced security
152
- */
153
- recommended: {
154
- plugins: {
155
- jwt: plugin,
156
- },
157
- rules: recommendedRules,
158
- } satisfies TSESLint.FlatConfig.Config,
159
-
160
- /**
161
- * Strict preset - maximum security (includes 2025 research)
162
- */
163
- strict: {
164
- plugins: {
165
- jwt: plugin,
166
- },
167
- rules: strictRules,
168
- } satisfies TSESLint.FlatConfig.Config,
169
-
170
- /**
171
- * Legacy preset - migration mode
172
- */
173
- legacy: {
174
- plugins: {
175
- jwt: plugin,
176
- },
177
- rules: legacyRules,
178
- } satisfies TSESLint.FlatConfig.Config,
179
-
180
- /**
181
- * All rules preset
182
- */
183
- all: {
184
- plugins: {
185
- jwt: plugin,
186
- },
187
- rules: strictRules,
188
- } satisfies TSESLint.FlatConfig.Config,
189
- };
190
-
191
- // Default export for flat config usage
192
- export default plugin;
193
-
194
- // Named exports for flexibility
195
- export {
196
- noAlgorithmNone,
197
- noAlgorithmConfusion,
198
- requireAlgorithmWhitelist,
199
- noDecodeWithoutVerify,
200
- noWeakSecret,
201
- requireExpiration,
202
- noHardcodedSecret,
203
- requireIssuedAt,
204
- requireIssuerValidation,
205
- requireAudienceValidation,
206
- noTimestampManipulation,
207
- requireMaxAge,
208
- noSensitivePayload,
209
- };
@@ -1,61 +0,0 @@
1
- /**
2
- * Copyright (c) 2025 Ofri Peretz
3
- * Licensed under the MIT License. Use of this source code is governed by the
4
- * MIT license that can be found in the LICENSE file.
5
- */
6
-
7
- import { describe, it, expect } from 'vitest';
8
- import * as fs from 'node:fs';
9
- import * as path from 'node:path';
10
- import { plugin as mainPlugin } from './index';
11
-
12
- /**
13
- * Smoke test for the `./oxlint` sub-export.
14
- *
15
- * Runtime shape: `oxlint.ts` uses `export = plugin`, which compiles to
16
- * `module.exports = plugin`. From an ESM dynamic `import('./oxlint')` view,
17
- * Node's CJS interop surfaces that value at `.default`. From oxlint's
18
- * `require('eslint-plugin-jwt-security/oxlint')` view, it sits at the top of
19
- * `module.exports` — exactly the `{ meta, rules }` shape oxlint's JS plugin
20
- * loader expects.
21
- */
22
-
23
- type Plugin = typeof mainPlugin;
24
-
25
- describe('eslint-plugin-jwt-security/oxlint sub-export', () => {
26
- it('declares the ./oxlint sub-export in package.json', () => {
27
- const pkgJson = JSON.parse(
28
- fs.readFileSync(path.resolve(__dirname, '..', 'package.json'), 'utf8'),
29
- );
30
- expect(pkgJson.exports['./oxlint']).toEqual({
31
- types: './src/oxlint.d.ts',
32
- default: './src/oxlint.js',
33
- });
34
- });
35
-
36
- it('re-exports the plugin object (meta + rules at top level)', async () => {
37
- const oxlintModule = await import('./oxlint.js');
38
- const oxlintPlugin = (oxlintModule as unknown as { default: Plugin }).default;
39
- expect(oxlintPlugin).toBeDefined();
40
- expect(oxlintPlugin.meta?.name).toBe('eslint-plugin-jwt-security');
41
- expect(oxlintPlugin.rules).toBeDefined();
42
- });
43
-
44
- it('exposes the same rule names as the main entry (no rules dropped)', async () => {
45
- const oxlintModule = await import('./oxlint.js');
46
- const oxlintPlugin = (oxlintModule as unknown as { default: Plugin }).default;
47
- expect(Object.keys(oxlintPlugin.rules || {}).toSorted()).toEqual(
48
- Object.keys(mainPlugin.rules || {}).toSorted(),
49
- );
50
- });
51
-
52
- it('exposes the same rule references (pass-through, not a copy)', async () => {
53
- const oxlintModule = await import('./oxlint.js');
54
- const oxlintPlugin = (oxlintModule as unknown as { default: Plugin }).default;
55
- for (const ruleName of Object.keys(mainPlugin.rules || {})) {
56
- expect(oxlintPlugin.rules?.[ruleName]).toBe(
57
- (mainPlugin.rules as Record<string, unknown>)[ruleName],
58
- );
59
- }
60
- });
61
- });
package/src/oxlint.ts DELETED
@@ -1,23 +0,0 @@
1
- /**
2
- * Copyright (c) 2025 Ofri Peretz
3
- * Licensed under the MIT License. Use of this source code is governed by the
4
- * MIT license that can be found in the LICENSE file.
5
- */
6
-
7
- /**
8
- * oxlint sub-export.
9
- *
10
- * Consumers wire oxlint to this entry via:
11
- * { "jsPlugins": ["eslint-plugin-jwt-security/oxlint"] }
12
- *
13
- * oxlint's JS plugin loader does `require('eslint-plugin-jwt-security/oxlint')` and reads
14
- * `.rules` off the result. The compiled output of `export = plugin` is
15
- * `module.exports = plugin`, which gives oxlint the plugin object directly
16
- * (`{ meta, rules }`) — no wrapper, no `.default` indirection.
17
- *
18
- * Generated by scripts/generate-oxlint-shims.mjs. Do not edit by hand.
19
- */
20
-
21
- import { plugin } from './index';
22
-
23
- export = plugin;