eslint-plugin-jwt-security 2.3.0 → 2.3.2

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 (97) hide show
  1. package/README.md +5 -5
  2. package/package.json +16 -24
  3. package/src/index.d.ts +17 -0
  4. package/src/index.js +1 -0
  5. package/src/oxlint.d.ts +17 -0
  6. package/src/oxlint.js +1 -0
  7. package/src/rules/no-algorithm-confusion/index.js +1 -0
  8. package/src/rules/no-algorithm-none/index.js +1 -0
  9. package/src/rules/no-decode-without-verify/index.js +1 -0
  10. package/src/rules/no-hardcoded-secret/index.js +1 -0
  11. package/src/rules/no-sensitive-payload/index.js +1 -0
  12. package/src/rules/no-timestamp-manipulation/index.js +1 -0
  13. package/src/rules/no-weak-secret/index.js +1 -0
  14. package/src/rules/require-algorithm-whitelist/index.js +1 -0
  15. package/src/rules/require-audience-validation/index.js +1 -0
  16. package/src/rules/require-expiration/index.js +1 -0
  17. package/src/rules/require-issued-at/index.js +1 -0
  18. package/src/rules/require-issuer-validation/index.js +1 -0
  19. package/src/rules/require-max-age/index.js +1 -0
  20. package/src/types/index.js +1 -0
  21. package/src/utils/index.js +1 -0
  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/index.js +0 -171
  31. package/dist/src/oxlint.d.ts +0 -20
  32. package/dist/src/oxlint.js +0 -21
  33. package/dist/src/rules/no-algorithm-confusion/index.d.ts +0 -24
  34. package/dist/src/rules/no-algorithm-confusion/index.js +0 -165
  35. package/dist/src/rules/no-algorithm-none/index.d.ts +0 -24
  36. package/dist/src/rules/no-algorithm-none/index.js +0 -222
  37. package/dist/src/rules/no-decode-without-verify/index.d.ts +0 -23
  38. package/dist/src/rules/no-decode-without-verify/index.js +0 -131
  39. package/dist/src/rules/no-hardcoded-secret/index.d.ts +0 -23
  40. package/dist/src/rules/no-hardcoded-secret/index.js +0 -189
  41. package/dist/src/rules/no-sensitive-payload/index.d.ts +0 -20
  42. package/dist/src/rules/no-sensitive-payload/index.js +0 -103
  43. package/dist/src/rules/no-timestamp-manipulation/index.d.ts +0 -23
  44. package/dist/src/rules/no-timestamp-manipulation/index.js +0 -97
  45. package/dist/src/rules/no-weak-secret/index.d.ts +0 -23
  46. package/dist/src/rules/no-weak-secret/index.js +0 -165
  47. package/dist/src/rules/require-algorithm-whitelist/index.d.ts +0 -24
  48. package/dist/src/rules/require-algorithm-whitelist/index.js +0 -113
  49. package/dist/src/rules/require-audience-validation/index.d.ts +0 -20
  50. package/dist/src/rules/require-audience-validation/index.js +0 -100
  51. package/dist/src/rules/require-expiration/index.d.ts +0 -23
  52. package/dist/src/rules/require-expiration/index.js +0 -136
  53. package/dist/src/rules/require-issued-at/index.d.ts +0 -23
  54. package/dist/src/rules/require-issued-at/index.js +0 -113
  55. package/dist/src/rules/require-issuer-validation/index.d.ts +0 -20
  56. package/dist/src/rules/require-issuer-validation/index.js +0 -100
  57. package/dist/src/rules/require-max-age/index.d.ts +0 -24
  58. package/dist/src/rules/require-max-age/index.js +0 -93
  59. package/dist/src/types/index.js +0 -7
  60. package/dist/src/utils/index.d.ts +0 -98
  61. package/dist/src/utils/index.js +0 -238
  62. package/src/__compatibility__/jwt-interface.spec.ts +0 -192
  63. package/src/index.test.ts +0 -47
  64. package/src/index.ts +0 -209
  65. package/src/oxlint.test.ts +0 -61
  66. package/src/oxlint.ts +0 -23
  67. package/src/rules/no-algorithm-confusion/index.ts +0 -208
  68. package/src/rules/no-algorithm-confusion/no-algorithm-confusion.test.ts +0 -106
  69. package/src/rules/no-algorithm-none/index.ts +0 -270
  70. package/src/rules/no-algorithm-none/no-algorithm-none.test.ts +0 -146
  71. package/src/rules/no-decode-without-verify/index.ts +0 -162
  72. package/src/rules/no-decode-without-verify/no-decode-without-verify.test.ts +0 -76
  73. package/src/rules/no-hardcoded-secret/index.ts +0 -230
  74. package/src/rules/no-hardcoded-secret/no-hardcoded-secret.test.ts +0 -97
  75. package/src/rules/no-sensitive-payload/index.ts +0 -123
  76. package/src/rules/no-sensitive-payload/no-sensitive-payload.test.ts +0 -157
  77. package/src/rules/no-timestamp-manipulation/index.ts +0 -118
  78. package/src/rules/no-timestamp-manipulation/no-timestamp-manipulation.test.ts +0 -54
  79. package/src/rules/no-weak-secret/index.ts +0 -204
  80. package/src/rules/no-weak-secret/no-weak-secret.test.ts +0 -114
  81. package/src/rules/require-algorithm-whitelist/index.ts +0 -145
  82. package/src/rules/require-algorithm-whitelist/require-algorithm-whitelist.test.ts +0 -72
  83. package/src/rules/require-audience-validation/index.ts +0 -120
  84. package/src/rules/require-audience-validation/require-audience-validation.test.ts +0 -68
  85. package/src/rules/require-expiration/index.ts +0 -170
  86. package/src/rules/require-expiration/require-expiration.test.ts +0 -135
  87. package/src/rules/require-issued-at/index.ts +0 -143
  88. package/src/rules/require-issued-at/require-issued-at.test.ts +0 -64
  89. package/src/rules/require-issuer-validation/index.ts +0 -120
  90. package/src/rules/require-issuer-validation/require-issuer-validation.test.ts +0 -68
  91. package/src/rules/require-max-age/index.ts +0 -117
  92. package/src/rules/require-max-age/require-max-age.test.ts +0 -69
  93. package/src/tests/coverage-dual-layer.test.ts +0 -323
  94. package/src/types/index.ts +0 -107
  95. package/src/utils/index.ts +0 -285
  96. package/src/utils/utils.test.ts +0 -324
  97. /package/{dist/src → src}/types/index.d.ts +0 -0
@@ -1,285 +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
- * Shared utilities for JWT security rules
9
- *
10
- * Library detection, pattern matching, and common helpers.
11
- */
12
- import type { TSESTree } from '@interlace/eslint-devkit';
13
-
14
- /**
15
- * Supported JWT libraries
16
- */
17
- export const JWT_LIBRARIES = {
18
- JSONWEBTOKEN: 'jsonwebtoken',
19
- JOSE: 'jose',
20
- EXPRESS_JWT: 'express-jwt',
21
- NESTJS_JWT: '@nestjs/jwt',
22
- JWKS_RSA: 'jwks-rsa',
23
- JWT_DECODE: 'jwt-decode',
24
- } as const;
25
-
26
- export type JwtLibrary = (typeof JWT_LIBRARIES)[keyof typeof JWT_LIBRARIES];
27
-
28
- /**
29
- * Insecure algorithms that should be flagged
30
- */
31
- export const INSECURE_ALGORITHMS = new Set([
32
- 'none',
33
- 'None',
34
- 'NONE',
35
- 'HS256', // Only insecure when used with public keys
36
- 'HS384',
37
- 'HS512',
38
- ]);
39
-
40
- /**
41
- * Algorithms vulnerable to confusion attacks when used with asymmetric keys
42
- */
43
- export const SYMMETRIC_ALGORITHMS = new Set(['HS256', 'HS384', 'HS512']);
44
-
45
- /**
46
- * Recommended secure algorithms
47
- */
48
- export const SECURE_ALGORITHMS = new Set([
49
- 'RS256',
50
- 'RS384',
51
- 'RS512',
52
- 'ES256',
53
- 'ES384',
54
- 'ES512',
55
- 'PS256',
56
- 'PS384',
57
- 'PS512',
58
- 'EdDSA',
59
- ]);
60
-
61
- /**
62
- * Sensitive field names that should not be in JWT payload
63
- * All lowercase for case-insensitive matching
64
- */
65
- export const SENSITIVE_PAYLOAD_FIELDS = new Set([
66
- // Passwords
67
- 'password', 'passwd', 'pwd', 'pass', 'secret',
68
- // API Keys
69
- 'apikey', 'api_key', 'api-key',
70
- 'apisecret', 'api_secret', 'api-secret',
71
- // Tokens
72
- 'accesstoken', 'access_token', 'access-token',
73
- 'refreshtoken', 'refresh_token', 'refresh-token',
74
- 'bearertoken', 'bearer_token', 'bearer-token',
75
- // Keys
76
- 'privatekey', 'private_key', 'private-key',
77
- 'secretkey', 'secret_key', 'secret-key',
78
- // PII - Personal Identifiable Information
79
- 'email', 'emailaddress', 'email_address', 'email-address',
80
- 'phone', 'phonenumber', 'phone_number', 'phone-number',
81
- 'ssn', 'socialsecuritynumber', 'social_security_number',
82
- 'dob', 'dateofbirth', 'date_of_birth', 'birthdate',
83
- 'address', 'streetaddress', 'street_address',
84
- // Financial
85
- 'creditcard', 'credit_card', 'credit-card',
86
- 'cardnumber', 'card_number', 'card-number',
87
- 'cvv', 'cvc', 'securitycode', 'security_code',
88
- 'pin', 'pincode', 'pin_code',
89
- 'bankaccount', 'bank_account', 'bank-account',
90
- 'accountnumber', 'account_number', 'account-number',
91
- 'routingnumber', 'routing_number', 'routing-number',
92
- ]);
93
-
94
- /**
95
- * JWT method patterns for different operations
96
- */
97
- export const JWT_METHODS = {
98
- SIGN: new Set(['sign', 'signJWT', 'SignJWT']),
99
- VERIFY: new Set(['verify', 'verifyJWT', 'jwtVerify']),
100
- DECODE: new Set(['decode', 'jwtDecode', 'decodeJWT']),
101
- } as const;
102
-
103
- /**
104
- * Check if a node represents a call to a JWT library method
105
- */
106
- export function isJwtLibraryCall(
107
- node: TSESTree.CallExpression,
108
- targetMethods: Set<string>
109
- ): boolean {
110
- // Check member expression: jwt.verify(), jose.jwtVerify()
111
- if (node.callee.type === 'MemberExpression') {
112
- const property = node.callee.property;
113
- if (property.type === 'Identifier') {
114
- return targetMethods.has(property.name);
115
- }
116
- }
117
-
118
- // Check direct calls: jwtVerify(), jwtDecode()
119
- if (node.callee.type === 'Identifier') {
120
- return targetMethods.has(node.callee.name);
121
- }
122
-
123
- return false;
124
- }
125
-
126
- /**
127
- * Check if a node is a string literal with a specific value
128
- */
129
- export function isStringLiteral(
130
- node: TSESTree.Node,
131
- value?: string
132
- ): node is TSESTree.Literal {
133
- if (node.type !== 'Literal' || typeof node.value !== 'string') {
134
- return false;
135
- }
136
- return value === undefined || node.value === value;
137
- }
138
-
139
- /**
140
- * Extract algorithm from options object
141
- */
142
- export function extractAlgorithms(
143
- optionsNode: TSESTree.ObjectExpression
144
- ): string[] {
145
- const algorithms: string[] = [];
146
-
147
- for (const prop of optionsNode.properties) {
148
- if (prop.type !== 'Property' || prop.key.type !== 'Identifier') {
149
- continue;
150
- }
151
-
152
- const keyName = prop.key.name;
153
- if (keyName !== 'algorithms' && keyName !== 'algorithm' && keyName !== 'alg') {
154
- continue;
155
- }
156
-
157
- // Single algorithm: { algorithm: 'HS256' }
158
- if (prop.value.type === 'Literal' && typeof prop.value.value === 'string') {
159
- algorithms.push(prop.value.value);
160
- }
161
-
162
- // Array of algorithms: { algorithms: ['RS256', 'ES256'] }
163
- if (prop.value.type === 'ArrayExpression') {
164
- for (const elem of prop.value.elements) {
165
- if (elem && elem.type === 'Literal' && typeof elem.value === 'string') {
166
- algorithms.push(elem.value);
167
- }
168
- }
169
- }
170
- }
171
-
172
- return algorithms;
173
- }
174
-
175
- /**
176
- * Check if options object has a specific property set
177
- */
178
- export function hasOption(
179
- optionsNode: TSESTree.ObjectExpression,
180
- optionName: string
181
- ): boolean {
182
- return optionsNode.properties.some(
183
- (prop): prop is TSESTree.Property =>
184
- prop.type === 'Property' &&
185
- prop.key.type === 'Identifier' &&
186
- prop.key.name === optionName
187
- );
188
- }
189
-
190
- /**
191
- * Get the value of a specific option from options object
192
- */
193
- export function getOptionValue(
194
- optionsNode: TSESTree.ObjectExpression,
195
- optionName: string
196
- ): TSESTree.Node | undefined {
197
- for (const prop of optionsNode.properties) {
198
- if (
199
- prop.type === 'Property' &&
200
- prop.key.type === 'Identifier' &&
201
- prop.key.name === optionName
202
- ) {
203
- return prop.value;
204
- }
205
- }
206
- return undefined;
207
- }
208
-
209
- /**
210
- * Check if a literal appears to be a weak secret (short string)
211
- */
212
- export function isWeakSecret(
213
- node: TSESTree.Node,
214
- minLength = 32
215
- ): boolean {
216
- if (node.type === 'Literal' && typeof node.value === 'string') {
217
- return node.value.length < minLength;
218
- }
219
- return false;
220
- }
221
-
222
- /**
223
- * Check if a node is an environment variable access (safe pattern)
224
- */
225
- export function isEnvVariable(node: TSESTree.Node): boolean {
226
- // process.env.JWT_SECRET
227
- if (
228
- node.type === 'MemberExpression' &&
229
- node.object.type === 'MemberExpression' &&
230
- node.object.object.type === 'Identifier' &&
231
- node.object.object.name === 'process' &&
232
- node.object.property.type === 'Identifier' &&
233
- node.object.property.name === 'env'
234
- ) {
235
- return true;
236
- }
237
-
238
- return false;
239
- }
240
-
241
- /**
242
- * Check if this call looks like a JWT sign operation
243
- */
244
- export function isSignOperation(node: TSESTree.CallExpression): boolean {
245
- return isJwtLibraryCall(node, JWT_METHODS.SIGN);
246
- }
247
-
248
- /**
249
- * Check if this call looks like a JWT verify operation
250
- */
251
- export function isVerifyOperation(node: TSESTree.CallExpression): boolean {
252
- return isJwtLibraryCall(node, JWT_METHODS.VERIFY);
253
- }
254
-
255
- /**
256
- * Check if this call looks like a JWT decode operation (no verification)
257
- */
258
- export function isDecodeOperation(node: TSESTree.CallExpression): boolean {
259
- return isJwtLibraryCall(node, JWT_METHODS.DECODE);
260
- }
261
-
262
- /**
263
- * Get the options argument from a JWT call
264
- * For jwt.verify(token, secret, options) -> returns options
265
- * For jwt.sign(payload, secret, options) -> returns options
266
- */
267
- export function getOptionsArgument(
268
- node: TSESTree.CallExpression,
269
- optionsIndex = 2
270
- ): TSESTree.ObjectExpression | undefined {
271
- const arg = node.arguments[optionsIndex];
272
- if (arg && arg.type === 'ObjectExpression') {
273
- return arg;
274
- }
275
- return undefined;
276
- }
277
-
278
- /**
279
- * Check if file is a test file
280
- */
281
- export function isTestFile(filename: string): boolean {
282
- return /\.(test|spec)\.[jt]sx?$/.test(filename) ||
283
- /__(tests?|mocks?)__/.test(filename) ||
284
- /\/(tests?|specs?|__tests__)\//.test(filename);
285
- }
@@ -1,324 +0,0 @@
1
- /**
2
- * Tests for JWT utilities
3
- */
4
- import { describe, it, expect } from 'vitest';
5
- import {
6
- isJwtLibraryCall,
7
- isStringLiteral,
8
- extractAlgorithms,
9
- hasOption,
10
- getOptionValue,
11
- isWeakSecret,
12
- isEnvVariable,
13
- isSignOperation,
14
- isVerifyOperation,
15
- isDecodeOperation,
16
- getOptionsArgument,
17
- isTestFile,
18
- SENSITIVE_PAYLOAD_FIELDS,
19
- SYMMETRIC_ALGORITHMS,
20
- SECURE_ALGORITHMS,
21
- INSECURE_ALGORITHMS,
22
- JWT_LIBRARIES,
23
- } from './index';
24
- import type { TSESTree } from '@interlace/eslint-devkit';
25
-
26
- // Helper to create mock nodes
27
- function mockCallExpression(calleeName: string): TSESTree.CallExpression {
28
- return {
29
- type: 'CallExpression',
30
- callee: { type: 'Identifier', name: calleeName } as TSESTree.Identifier,
31
- arguments: [],
32
- } as unknown as TSESTree.CallExpression;
33
- }
34
-
35
- function mockMemberCallExpression(objectName: string, methodName: string): TSESTree.CallExpression {
36
- return {
37
- type: 'CallExpression',
38
- callee: {
39
- type: 'MemberExpression',
40
- object: { type: 'Identifier', name: objectName } as TSESTree.Identifier,
41
- property: { type: 'Identifier', name: methodName } as TSESTree.Identifier,
42
- },
43
- arguments: [],
44
- } as unknown as TSESTree.CallExpression;
45
- }
46
-
47
- function mockLiteral(value: string | number | boolean): TSESTree.Literal {
48
- return {
49
- type: 'Literal',
50
- value,
51
- } as TSESTree.Literal;
52
- }
53
-
54
- function mockObjectExpression(props: Record<string, TSESTree.Node>): TSESTree.ObjectExpression {
55
- return {
56
- type: 'ObjectExpression',
57
- properties: Object.entries(props).map(([key, value]) => ({
58
- type: 'Property',
59
- key: { type: 'Identifier', name: key } as TSESTree.Identifier,
60
- value,
61
- })),
62
- } as unknown as TSESTree.ObjectExpression;
63
- }
64
-
65
- describe('JWT Utils', () => {
66
- describe('isJwtLibraryCall', () => {
67
- it('should detect direct calls', () => {
68
- const call = mockCallExpression('verify');
69
- expect(isJwtLibraryCall(call, new Set(['verify']))).toBe(true);
70
- });
71
-
72
- it('should detect member calls', () => {
73
- const call = mockMemberCallExpression('jwt', 'verify');
74
- expect(isJwtLibraryCall(call, new Set(['verify']))).toBe(true);
75
- });
76
-
77
- it('should return false for non-matching calls', () => {
78
- const call = mockCallExpression('somethingElse');
79
- expect(isJwtLibraryCall(call, new Set(['verify']))).toBe(false);
80
- });
81
-
82
- it('should return false for non-identifier/member callee (line 117)', () => {
83
- // This covers the `return false` at line 117 for CallExpression, ArrowFunction, etc.
84
- const call = {
85
- type: 'CallExpression',
86
- callee: { type: 'ArrowFunctionExpression' }, // Not Identifier or MemberExpression
87
- arguments: [],
88
- } as unknown as TSESTree.CallExpression;
89
- expect(isJwtLibraryCall(call, new Set(['verify']))).toBe(false);
90
- });
91
-
92
- it('should return false for member with non-identifier property', () => {
93
- const call = {
94
- type: 'CallExpression',
95
- callee: {
96
- type: 'MemberExpression',
97
- object: { type: 'Identifier', name: 'jwt' },
98
- property: { type: 'Literal', value: 'verify' }, // Literal, not Identifier
99
- },
100
- arguments: [],
101
- } as unknown as TSESTree.CallExpression;
102
- expect(isJwtLibraryCall(call, new Set(['verify']))).toBe(false);
103
- });
104
- });
105
-
106
- describe('isStringLiteral', () => {
107
- it('should return true for string literals', () => {
108
- const literal = mockLiteral('hello');
109
- expect(isStringLiteral(literal)).toBe(true);
110
- });
111
-
112
- it('should return true when value matches', () => {
113
- const literal = mockLiteral('test');
114
- expect(isStringLiteral(literal, 'test')).toBe(true);
115
- });
116
-
117
- it('should return false when value does not match', () => {
118
- const literal = mockLiteral('test');
119
- expect(isStringLiteral(literal, 'other')).toBe(false);
120
- });
121
-
122
- it('should return false for non-string literals', () => {
123
- const literal = mockLiteral(123);
124
- expect(isStringLiteral(literal)).toBe(false);
125
- });
126
- });
127
-
128
- describe('extractAlgorithms', () => {
129
- it('should extract single algorithm', () => {
130
- const obj = mockObjectExpression({
131
- algorithm: mockLiteral('HS256'),
132
- });
133
- expect(extractAlgorithms(obj)).toEqual(['HS256']);
134
- });
135
-
136
- it('should extract algorithms from array', () => {
137
- const obj = {
138
- type: 'ObjectExpression',
139
- properties: [{
140
- type: 'Property',
141
- key: { type: 'Identifier', name: 'algorithms' },
142
- value: {
143
- type: 'ArrayExpression',
144
- elements: [mockLiteral('RS256'), mockLiteral('ES256')],
145
- },
146
- }],
147
- } as unknown as TSESTree.ObjectExpression;
148
- expect(extractAlgorithms(obj)).toEqual(['RS256', 'ES256']);
149
- });
150
-
151
- it('should return empty array for no algorithms', () => {
152
- const obj = mockObjectExpression({ other: mockLiteral('value') });
153
- expect(extractAlgorithms(obj)).toEqual([]);
154
- });
155
-
156
- it('should handle alg key', () => {
157
- const obj = mockObjectExpression({ alg: mockLiteral('ES512') });
158
- expect(extractAlgorithms(obj)).toEqual(['ES512']);
159
- });
160
-
161
- it('should skip SpreadElement properties (line 143)', () => {
162
- // This covers the `continue` at line 143 for non-Property types
163
- const obj = {
164
- type: 'ObjectExpression',
165
- properties: [
166
- { type: 'SpreadElement', argument: { type: 'Identifier', name: 'opts' } },
167
- {
168
- type: 'Property',
169
- key: { type: 'Identifier', name: 'algorithm' },
170
- value: mockLiteral('RS256'),
171
- },
172
- ],
173
- } as unknown as TSESTree.ObjectExpression;
174
- expect(extractAlgorithms(obj)).toEqual(['RS256']);
175
- });
176
-
177
- it('should skip properties with non-identifier keys', () => {
178
- // This also covers line 142 - when key.type !== 'Identifier'
179
- const obj = {
180
- type: 'ObjectExpression',
181
- properties: [{
182
- type: 'Property',
183
- key: { type: 'Literal', value: 'algorithm' }, // Literal key, not Identifier
184
- value: mockLiteral('RS256'),
185
- }],
186
- } as unknown as TSESTree.ObjectExpression;
187
- expect(extractAlgorithms(obj)).toEqual([]);
188
- });
189
- });
190
-
191
- describe('hasOption', () => {
192
- it('should return true when option exists', () => {
193
- const obj = mockObjectExpression({ expiresIn: mockLiteral('1h') });
194
- expect(hasOption(obj, 'expiresIn')).toBe(true);
195
- });
196
-
197
- it('should return false when option missing', () => {
198
- const obj = mockObjectExpression({});
199
- expect(hasOption(obj, 'expiresIn')).toBe(false);
200
- });
201
- });
202
-
203
- describe('getOptionValue', () => {
204
- it('should return option value', () => {
205
- const value = mockLiteral('1h');
206
- const obj = mockObjectExpression({ expiresIn: value });
207
- expect(getOptionValue(obj, 'expiresIn')).toBe(value);
208
- });
209
-
210
- it('should return undefined for missing option', () => {
211
- const obj = mockObjectExpression({});
212
- expect(getOptionValue(obj, 'expiresIn')).toBeUndefined();
213
- });
214
- });
215
-
216
- describe('isWeakSecret', () => {
217
- it('should return true for short secrets', () => {
218
- const node = mockLiteral('short');
219
- expect(isWeakSecret(node, 32)).toBe(true);
220
- });
221
-
222
- it('should return false for long secrets', () => {
223
- const node = mockLiteral('a'.repeat(64));
224
- expect(isWeakSecret(node, 32)).toBe(false);
225
- });
226
-
227
- it('should return false for non-literals', () => {
228
- const node = { type: 'Identifier', name: 'secret' } as TSESTree.Identifier;
229
- expect(isWeakSecret(node)).toBe(false);
230
- });
231
- });
232
-
233
- describe('isEnvVariable', () => {
234
- it('should return true for process.env.X', () => {
235
- const node = {
236
- type: 'MemberExpression',
237
- object: {
238
- type: 'MemberExpression',
239
- object: { type: 'Identifier', name: 'process' },
240
- property: { type: 'Identifier', name: 'env' },
241
- },
242
- property: { type: 'Identifier', name: 'JWT_SECRET' },
243
- } as unknown as TSESTree.MemberExpression;
244
- expect(isEnvVariable(node)).toBe(true);
245
- });
246
-
247
- it('should return false for other nodes', () => {
248
- const node = { type: 'Identifier', name: 'secret' } as TSESTree.Identifier;
249
- expect(isEnvVariable(node)).toBe(false);
250
- });
251
- });
252
-
253
- describe('operation checkers', () => {
254
- it('isSignOperation detects sign calls', () => {
255
- expect(isSignOperation(mockCallExpression('sign'))).toBe(true);
256
- expect(isSignOperation(mockCallExpression('other'))).toBe(false);
257
- });
258
-
259
- it('isVerifyOperation detects verify calls', () => {
260
- expect(isVerifyOperation(mockCallExpression('verify'))).toBe(true);
261
- expect(isVerifyOperation(mockCallExpression('other'))).toBe(false);
262
- });
263
-
264
- it('isDecodeOperation detects decode calls', () => {
265
- expect(isDecodeOperation(mockCallExpression('decode'))).toBe(true);
266
- expect(isDecodeOperation(mockCallExpression('other'))).toBe(false);
267
- });
268
- });
269
-
270
- describe('getOptionsArgument', () => {
271
- it('should return options object at default index', () => {
272
- const options = mockObjectExpression({});
273
- const call = {
274
- ...mockCallExpression('verify'),
275
- arguments: [mockLiteral('token'), mockLiteral('secret'), options],
276
- } as unknown as TSESTree.CallExpression;
277
- expect(getOptionsArgument(call)).toBe(options);
278
- });
279
-
280
- it('should return undefined if no options', () => {
281
- const call = mockCallExpression('verify');
282
- expect(getOptionsArgument(call)).toBeUndefined();
283
- });
284
- });
285
-
286
- describe('isTestFile', () => {
287
- it('should return true for test files', () => {
288
- expect(isTestFile('file.test.ts')).toBe(true);
289
- expect(isTestFile('file.spec.js')).toBe(true);
290
- expect(isTestFile('/path/__tests__/file.ts')).toBe(true);
291
- });
292
-
293
- it('should return false for non-test files', () => {
294
- expect(isTestFile('index.ts')).toBe(false);
295
- expect(isTestFile('utils.js')).toBe(false);
296
- });
297
- });
298
-
299
- describe('constants', () => {
300
- it('should have sensitive fields', () => {
301
- expect(SENSITIVE_PAYLOAD_FIELDS.has('password')).toBe(true);
302
- expect(SENSITIVE_PAYLOAD_FIELDS.has('ssn')).toBe(true);
303
- expect(SENSITIVE_PAYLOAD_FIELDS.has('api_key')).toBe(true);
304
- });
305
-
306
- it('should have symmetric algorithms', () => {
307
- expect(SYMMETRIC_ALGORITHMS.has('HS256')).toBe(true);
308
- });
309
-
310
- it('should have secure algorithms', () => {
311
- expect(SECURE_ALGORITHMS.has('RS256')).toBe(true);
312
- expect(SECURE_ALGORITHMS.has('ES256')).toBe(true);
313
- });
314
-
315
- it('should have insecure algorithms', () => {
316
- expect(INSECURE_ALGORITHMS.has('none')).toBe(true);
317
- });
318
-
319
- it('should have JWT libraries', () => {
320
- expect(JWT_LIBRARIES.JSONWEBTOKEN).toBe('jsonwebtoken');
321
- expect(JWT_LIBRARIES.JOSE).toBe('jose');
322
- });
323
- });
324
- });
File without changes