@sun-asterisk/sunlint 1.3.0 → 1.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 (124) hide show
  1. package/CHANGELOG.md +115 -1
  2. package/CONTRIBUTING.md +249 -605
  3. package/README.md +3 -4
  4. package/config/ci-cd.json +54 -0
  5. package/config/development.json +56 -0
  6. package/config/large-project.json +143 -0
  7. package/config/presets/all.json +0 -1
  8. package/config/release.json +70 -0
  9. package/config/rule-analysis-strategies.js +38 -3
  10. package/config/rules/enhanced-rules-registry.json +474 -1179
  11. package/config/rules/rules-registry-generated.json +3 -3
  12. package/core/cli-action-handler.js +24 -30
  13. package/core/cli-program.js +11 -3
  14. package/core/config-merger.js +29 -2
  15. package/core/enhanced-rules-registry.js +3 -2
  16. package/core/semantic-engine.js +129 -19
  17. package/core/semantic-rule-base.js +4 -2
  18. package/core/unified-rule-registry.js +1 -1
  19. package/docs/COMMAND-EXAMPLES.md +134 -0
  20. package/docs/LARGE-PROJECT-GUIDE.md +324 -0
  21. package/engines/heuristic-engine.js +135 -16
  22. package/integrations/eslint/plugin/index.js +0 -2
  23. package/integrations/eslint/plugin/rules/common/c003-no-vague-abbreviations.js +59 -1
  24. package/integrations/eslint/plugin/rules/common/c006-function-name-verb-noun.js +26 -1
  25. package/integrations/eslint/plugin/rules/common/c030-use-custom-error-classes.js +54 -19
  26. package/origin-rules/common-en.md +19 -15
  27. package/package.json +1 -1
  28. package/rules/common/C002_no_duplicate_code/analyzer.js +334 -36
  29. package/rules/common/C003_no_vague_abbreviations/analyzer.js +220 -35
  30. package/rules/common/C006_function_naming/analyzer.js +29 -3
  31. package/rules/common/C010_limit_block_nesting/analyzer.js +181 -337
  32. package/rules/common/C010_limit_block_nesting/config.json +64 -0
  33. package/rules/common/C010_limit_block_nesting/regex-based-analyzer.js +379 -0
  34. package/rules/common/C010_limit_block_nesting/symbol-based-analyzer.js +231 -0
  35. package/rules/common/C013_no_dead_code/analyzer.js +75 -177
  36. package/rules/common/C013_no_dead_code/config.json +61 -0
  37. package/rules/common/C013_no_dead_code/regex-based-analyzer.js +345 -0
  38. package/rules/common/C013_no_dead_code/symbol-based-analyzer.js +640 -0
  39. package/rules/common/C014_dependency_injection/analyzer.js +48 -313
  40. package/rules/common/C014_dependency_injection/config.json +26 -0
  41. package/rules/common/C014_dependency_injection/symbol-based-analyzer.js +751 -0
  42. package/rules/common/C017_constructor_logic/analyzer.js +254 -17
  43. package/rules/common/C017_constructor_logic/semantic-analyzer.js +340 -0
  44. package/rules/common/C018_no_throw_generic_error/analyzer.js +232 -0
  45. package/rules/common/C018_no_throw_generic_error/config.json +50 -0
  46. package/rules/common/C018_no_throw_generic_error/regex-based-analyzer.js +387 -0
  47. package/rules/common/C018_no_throw_generic_error/symbol-based-analyzer.js +314 -0
  48. package/rules/common/C019_log_level_usage/analyzer.js +110 -317
  49. package/rules/common/C019_log_level_usage/pattern-analyzer.js +88 -0
  50. package/rules/common/C019_log_level_usage/system-log-analyzer.js +1267 -0
  51. package/rules/common/C023_no_duplicate_variable/analyzer.js +180 -0
  52. package/rules/common/C023_no_duplicate_variable/config.json +50 -0
  53. package/rules/common/C023_no_duplicate_variable/symbol-based-analyzer.js +158 -0
  54. package/rules/common/C024_no_scatter_hardcoded_constants/analyzer.js +180 -0
  55. package/rules/common/C024_no_scatter_hardcoded_constants/config.json +50 -0
  56. package/rules/common/C024_no_scatter_hardcoded_constants/symbol-based-analyzer.js +181 -0
  57. package/rules/common/C030_use_custom_error_classes/analyzer.js +200 -0
  58. package/rules/common/C033_separate_service_repository/README.md +78 -0
  59. package/rules/common/C033_separate_service_repository/analyzer.js +160 -0
  60. package/rules/common/C033_separate_service_repository/config.json +50 -0
  61. package/rules/common/C033_separate_service_repository/regex-based-analyzer.js +585 -0
  62. package/rules/common/C033_separate_service_repository/symbol-based-analyzer.js +368 -0
  63. package/rules/common/C035_error_logging_context/STRATEGY.md +99 -0
  64. package/rules/common/C035_error_logging_context/analyzer.js +232 -0
  65. package/rules/common/C035_error_logging_context/config.json +54 -0
  66. package/rules/common/C035_error_logging_context/regex-based-analyzer.js +299 -0
  67. package/rules/common/C035_error_logging_context/symbol-based-analyzer.js +454 -0
  68. package/rules/common/C040_centralized_validation/analyzer.js +165 -0
  69. package/rules/common/C040_centralized_validation/config.json +46 -0
  70. package/rules/common/C040_centralized_validation/regex-based-analyzer.js +243 -0
  71. package/rules/common/C040_centralized_validation/symbol-based-analyzer.js +416 -0
  72. package/rules/common/{C076_single_test_behavior → C072_single_test_behavior}/analyzer.js +6 -6
  73. package/rules/common/C076_explicit_function_types/README.md +30 -0
  74. package/rules/common/C076_explicit_function_types/analyzer.js +172 -0
  75. package/rules/common/C076_explicit_function_types/config.json +15 -0
  76. package/rules/common/C076_explicit_function_types/semantic-analyzer.js +341 -0
  77. package/rules/index.js +6 -1
  78. package/rules/parser/rule-parser.js +13 -2
  79. package/rules/security/S005_no_origin_auth/README.md +226 -0
  80. package/rules/security/S005_no_origin_auth/analyzer.js +184 -0
  81. package/rules/security/S005_no_origin_auth/ast-analyzer.js +406 -0
  82. package/rules/security/S005_no_origin_auth/config.json +85 -0
  83. package/rules/security/S006_no_plaintext_recovery_codes/README.md +139 -0
  84. package/rules/security/S006_no_plaintext_recovery_codes/analyzer.js +306 -0
  85. package/rules/security/S006_no_plaintext_recovery_codes/config.json +48 -0
  86. package/rules/security/S007_no_plaintext_otp/README.md +198 -0
  87. package/rules/security/S007_no_plaintext_otp/analyzer.js +406 -0
  88. package/rules/security/S007_no_plaintext_otp/config.json +79 -0
  89. package/rules/security/S007_no_plaintext_otp/semantic-analyzer.js +609 -0
  90. package/rules/security/S007_no_plaintext_otp/semantic-config.json +195 -0
  91. package/rules/security/S007_no_plaintext_otp/semantic-wrapper.js +280 -0
  92. package/rules/security/S009_no_insecure_encryption/README.md +158 -0
  93. package/rules/security/S009_no_insecure_encryption/analyzer.js +319 -0
  94. package/rules/security/S009_no_insecure_encryption/config.json +55 -0
  95. package/rules/security/S010_no_insecure_encryption/README.md +224 -0
  96. package/rules/security/S010_no_insecure_encryption/analyzer.js +493 -0
  97. package/rules/security/S010_no_insecure_encryption/config.json +48 -0
  98. package/rules/security/S016_no_sensitive_querystring/STRATEGY.md +149 -0
  99. package/rules/security/S016_no_sensitive_querystring/analyzer.js +276 -0
  100. package/rules/security/S016_no_sensitive_querystring/config.json +127 -0
  101. package/rules/security/S016_no_sensitive_querystring/regex-based-analyzer.js +258 -0
  102. package/rules/security/S016_no_sensitive_querystring/symbol-based-analyzer.js +495 -0
  103. package/rules/security/S027_no_hardcoded_secrets/analyzer.js +180 -366
  104. package/rules/security/S027_no_hardcoded_secrets/categories.json +153 -0
  105. package/rules/security/S027_no_hardcoded_secrets/categorized-analyzer.js +250 -0
  106. package/rules/security/S048_no_current_password_in_reset/README.md +222 -0
  107. package/rules/security/S048_no_current_password_in_reset/analyzer.js +366 -0
  108. package/rules/security/S048_no_current_password_in_reset/config.json +48 -0
  109. package/rules/security/S055_content_type_validation/README.md +176 -0
  110. package/rules/security/S055_content_type_validation/analyzer.js +312 -0
  111. package/rules/security/S055_content_type_validation/config.json +48 -0
  112. package/rules/utils/rule-helpers.js +140 -1
  113. package/scripts/consolidate-config.js +116 -0
  114. package/scripts/prepare-release.sh +1 -1
  115. package/config/rules/rules-registry.json +0 -765
  116. package/docs/ESLINT-INTEGRATION-STRATEGY.md +0 -392
  117. package/docs/FUTURE_PACKAGES.md +0 -83
  118. package/docs/HEURISTIC_VS_AI.md +0 -113
  119. package/docs/PRODUCTION_DEPLOYMENT_ANALYSIS.md +0 -112
  120. package/docs/PRODUCTION_SIZE_IMPACT.md +0 -183
  121. package/docs/RELEASE_GUIDE.md +0 -230
  122. package/docs/STANDARDIZED-CATEGORY-FILTERING.md +0 -156
  123. package/integrations/eslint/plugin/rules/common/c076-single-behavior-per-test.js +0 -254
  124. package/rules/common/C006_function_naming/smart-analyzer.js +0 -503
@@ -0,0 +1,609 @@
1
+ /**
2
+ * S007 Semantic Analyzer - No Plaintext OTP
3
+ * Semantic analysis for detecting plaintext OTP storage/transmission using Symbol Table
4
+ *
5
+ * Advantages over regex:
6
+ * - Context-aware analysis (knows function scopes, variable relationships)
7
+ * - Cross-file dependency tracking
8
+ * - Type information and data flow analysis
9
+ * - Reduced false positives through semantic understanding
10
+ */
11
+
12
+ const SemanticRuleBase = require('../../../core/semantic-rule-base');
13
+
14
+ class S007SemanticAnalyzer extends SemanticRuleBase {
15
+ constructor(ruleId = 'S007') {
16
+ super(ruleId, {
17
+ category: 'security',
18
+ severity: 'error',
19
+ description: 'Detects plaintext OTP storage and transmission using semantic analysis',
20
+ crossFileAnalysis: true,
21
+ requiresTypeChecker: false,
22
+ cacheResults: true
23
+ });
24
+
25
+ // OTP-related patterns for semantic analysis
26
+ this.otpPatterns = {
27
+ // Variable names that suggest OTP usage
28
+ variableNames: [
29
+ /^(otp|totp|hotp)$/i,
30
+ /^(one_?time|onetime)_?(password|pass|code)$/i,
31
+ /^(auth|verification|sms|temp|security|access|login)_?code$/i,
32
+ /^(two_?factor|2fa|mfa)_?(token|code)$/i,
33
+ /^pin_?code$/i
34
+ ],
35
+
36
+ // Function names that handle OTP
37
+ functionNames: [
38
+ /^(generate|create|store|save|send|validate|verify)_?otp$/i,
39
+ /^otp_?(generate|create|store|save|send|validate|verify)$/i,
40
+ /^(generate|create|store|save|send|validate|verify)_?(auth|verification|sms|temp|security|access|login)_?code$/i,
41
+ /^send_?(sms|email|auth)_?code$/i
42
+ ],
43
+
44
+ // Method calls that suggest OTP operations
45
+ methodNames: [
46
+ 'sendOtp', 'storeOtp', 'saveOtp', 'generateOtp', 'verifyOtp',
47
+ 'sendSmsCode', 'sendAuthCode', 'storeAuthCode', 'saveAuthCode'
48
+ ]
49
+ };
50
+
51
+ // Dangerous storage/transmission patterns
52
+ this.dangerousOperations = {
53
+ // Database storage methods
54
+ storage: [
55
+ 'save', 'store', 'insert', 'update', 'create', 'persist',
56
+ 'collection.insertOne', 'collection.updateOne', 'db.save',
57
+ 'redis.set', 'redis.hset', 'cache.put', 'cache.set'
58
+ ],
59
+
60
+ // Network transmission methods
61
+ transmission: [
62
+ 'send', 'emit', 'post', 'put', 'response.json', 'res.send',
63
+ 'email.send', 'sms.send', 'notify', 'broadcast'
64
+ ],
65
+
66
+ // Browser storage
67
+ browserStorage: [
68
+ 'localStorage.setItem', 'sessionStorage.setItem',
69
+ 'localStorage.set', 'sessionStorage.set'
70
+ ],
71
+
72
+ // Logging (usually unsafe for OTP)
73
+ logging: [
74
+ 'console.log', 'console.debug', 'console.info',
75
+ 'logger.info', 'logger.debug', 'log.info'
76
+ ]
77
+ };
78
+
79
+ // Safe operations that encrypt/hash
80
+ this.safeOperations = [
81
+ 'bcrypt.hash', 'crypto.createHash', 'crypto.createHmac',
82
+ 'hash', 'encrypt', 'cipher', 'secure', 'pbkdf2',
83
+ 'scrypt', 'argon2'
84
+ ];
85
+ }
86
+
87
+ /**
88
+ * Main file analysis using Symbol Table
89
+ */
90
+ async analyzeFile(filePath, options = {}) {
91
+ try {
92
+ if (options.verbose) {
93
+ console.log('🧠 Analyzing file:', filePath);
94
+ }
95
+ const symbolTable = await this.getSymbolTable(filePath);
96
+ if (!symbolTable) {
97
+ if (options.verbose) {
98
+ console.warn(`⚠️ ${this.ruleId}: No symbol table available for ${filePath}`);
99
+ }
100
+ return;
101
+ }
102
+
103
+ if (options.verbose) {
104
+ console.log(`🧠 ${this.ruleId}: Analyzing ${filePath} with Symbol Table`);
105
+ }
106
+
107
+ // Analyze different aspects using semantic information with error handling
108
+ try {
109
+ await this.analyzeVariableUsage(symbolTable, filePath);
110
+ } catch (error) {
111
+ if (options.verbose) {
112
+ console.warn(`⚠️ ${this.ruleId}: Variable analysis failed for ${filePath}: ${error.message}`);
113
+ }
114
+ }
115
+
116
+ try {
117
+ await this.analyzeFunctionCalls(symbolTable, filePath);
118
+ } catch (error) {
119
+ if (options.verbose) {
120
+ console.warn(`⚠️ ${this.ruleId}: Function call analysis failed for ${filePath}: ${error.message}`);
121
+ }
122
+ }
123
+
124
+ try {
125
+ await this.analyzeMethodChains(symbolTable, filePath);
126
+ } catch (error) {
127
+ if (options.verbose) {
128
+ console.warn(`⚠️ ${this.ruleId}: Method chain analysis failed for ${filePath}: ${error.message}`);
129
+ }
130
+ }
131
+
132
+ try {
133
+ await this.analyzeDataFlow(symbolTable, filePath);
134
+ } catch (error) {
135
+ if (options.verbose) {
136
+ console.warn(`⚠️ ${this.ruleId}: Data flow analysis failed for ${filePath}: ${error.message}`);
137
+ }
138
+ }
139
+
140
+ // Cross-file analysis if enabled
141
+ if (this.config.crossFileAnalysis) {
142
+ try {
143
+ await this.analyzeCrossFileReferences(symbolTable, filePath);
144
+ } catch (error) {
145
+ if (options.verbose) {
146
+ console.warn(`⚠️ ${this.ruleId}: Cross-file analysis failed for ${filePath}: ${error.message}`);
147
+ }
148
+ }
149
+ }
150
+
151
+ } catch (error) {
152
+ console.error(`❌ ${this.ruleId}: Semantic analysis failed for ${filePath}: ${error.message}`);
153
+ // Don't throw - let the wrapper handle fallback
154
+ }
155
+ }
156
+
157
+ /**
158
+ * Analyze variable declarations and assignments for OTP patterns
159
+ */
160
+ async analyzeVariableUsage(symbolTable, filePath) {
161
+ // Check variable declarations with safe array handling
162
+ const variables = symbolTable.variables || [];
163
+ const constants = symbolTable.constants || [];
164
+ const allVariables = [...variables, ...constants];
165
+
166
+ for (const variable of allVariables) {
167
+ if (this.isOtpVariable(variable.name)) {
168
+ // Check if this OTP variable is used in dangerous contexts
169
+ const dangerousUsages = await this.findDangerousVariableUsages(
170
+ symbolTable,
171
+ variable.name,
172
+ variable.line
173
+ );
174
+
175
+ for (const usage of dangerousUsages) {
176
+ this.addViolation({
177
+ filePath,
178
+ line: usage.line,
179
+ column: usage.column || 1,
180
+ message: `OTP variable '${variable.name}' is used in ${usage.context} without encryption`,
181
+ type: 'semantic_otp_variable_usage',
182
+ severity: this.determineSeverity(usage.context),
183
+ symbolContext: {
184
+ variableName: variable.name,
185
+ variableType: variable.type,
186
+ usageContext: usage.context,
187
+ operation: usage.operation
188
+ },
189
+ suggestion: this.generateSecuritySuggestion(usage.context),
190
+ codeSnippet: usage.codeSnippet
191
+ });
192
+ }
193
+ }
194
+ }
195
+ }
196
+
197
+ /**
198
+ * Analyze function calls for OTP-related operations
199
+ */
200
+ async analyzeFunctionCalls(symbolTable, filePath) {
201
+ const functionCalls = symbolTable.functionCalls || [];
202
+ for (const functionCall of functionCalls) {
203
+ // Check if function name suggests OTP handling
204
+ if (this.isOtpFunction(functionCall.functionName)) {
205
+ const context = await this.analyzeFunctionContext(symbolTable, functionCall);
206
+
207
+ if (context.isDangerous && !context.isSecure) {
208
+ this.addViolation({
209
+ filePath,
210
+ line: functionCall.line,
211
+ column: functionCall.column || 1,
212
+ message: `Function '${functionCall.functionName}' may handle OTP in plaintext`,
213
+ type: 'semantic_otp_function_call',
214
+ severity: 'warning',
215
+ symbolContext: {
216
+ functionName: functionCall.functionName,
217
+ arguments: functionCall.arguments,
218
+ returnType: functionCall.returnType,
219
+ context: context
220
+ },
221
+ suggestion: 'Ensure OTP values are encrypted before processing'
222
+ });
223
+ }
224
+ }
225
+
226
+ // Check for dangerous operations with OTP arguments
227
+ if (this.isDangerousOperation(functionCall.functionName)) {
228
+ const hasOtpArguments = await this.checkOtpArguments(symbolTable, functionCall);
229
+
230
+ if (hasOtpArguments.length > 0) {
231
+ this.addViolation({
232
+ filePath,
233
+ line: functionCall.line,
234
+ column: functionCall.column || 1,
235
+ message: `Potential plaintext OTP passed to ${functionCall.functionName}`,
236
+ type: 'semantic_plaintext_otp_argument',
237
+ severity: 'error',
238
+ symbolContext: {
239
+ functionName: functionCall.functionName,
240
+ otpArguments: hasOtpArguments,
241
+ operation: this.categorizeDangerousOperation(functionCall.functionName)
242
+ },
243
+ suggestion: 'Encrypt or hash OTP values before storage/transmission'
244
+ });
245
+ }
246
+ }
247
+ }
248
+ }
249
+
250
+ /**
251
+ * Analyze method chains for OTP operations
252
+ */
253
+ async analyzeMethodChains(symbolTable, filePath) {
254
+ const methodCalls = symbolTable.methodCalls || [];
255
+ for (const methodCall of methodCalls) {
256
+ // Look for patterns like: otpCode.save() or user.storeOtp()
257
+ if (this.isOtpRelatedMethodCall(methodCall)) {
258
+ const chainContext = await this.analyzeMethodChain(symbolTable, methodCall);
259
+
260
+ if (chainContext.isUnsafe) {
261
+ this.addViolation({
262
+ filePath,
263
+ line: methodCall.line,
264
+ column: methodCall.column || 1,
265
+ message: `Method chain exposes OTP in plaintext: ${chainContext.chain}`,
266
+ type: 'semantic_method_chain_otp',
267
+ severity: 'error',
268
+ symbolContext: {
269
+ methodChain: chainContext.chain,
270
+ objectName: methodCall.objectName,
271
+ methodName: methodCall.methodName,
272
+ dataFlow: chainContext.dataFlow
273
+ },
274
+ suggestion: 'Use secure methods for OTP handling in method chains'
275
+ });
276
+ }
277
+ }
278
+ }
279
+ }
280
+
281
+ /**
282
+ * Analyze data flow to track OTP values through the code
283
+ */
284
+ async analyzeDataFlow(symbolTable, filePath) {
285
+ // Find all OTP-related variables and track their usage
286
+ const variables = symbolTable.variables || [];
287
+ const otpVariables = variables.filter(v => this.isOtpVariable(v.name));
288
+
289
+ for (const otpVar of otpVariables) {
290
+ const dataFlow = await this.traceDataFlow(symbolTable, otpVar.name);
291
+
292
+ // Check if OTP flows to dangerous sinks
293
+ const dangerousSinks = dataFlow.filter(flow =>
294
+ this.isDangerousOperation(flow.operation) &&
295
+ !this.isSafeOperation(flow.operation)
296
+ );
297
+
298
+ for (const sink of dangerousSinks) {
299
+ this.addViolation({
300
+ filePath,
301
+ line: sink.line,
302
+ column: sink.column || 1,
303
+ message: `OTP variable '${otpVar.name}' flows to unsafe operation '${sink.operation}'`,
304
+ type: 'semantic_data_flow_violation',
305
+ severity: 'error',
306
+ symbolContext: {
307
+ sourceVariable: otpVar.name,
308
+ dataFlowPath: dataFlow,
309
+ dangerousSink: sink,
310
+ flowDistance: sink.distance
311
+ },
312
+ suggestion: 'Ensure OTP is encrypted before reaching this operation'
313
+ });
314
+ }
315
+ }
316
+ }
317
+
318
+ /**
319
+ * Cross-file analysis for OTP security
320
+ */
321
+ async analyzeCrossFileReferences(symbolTable, filePath) {
322
+ // Find OTP-related exports
323
+ const exports = symbolTable.exports || [];
324
+ const otpExports = exports.filter(exp =>
325
+ this.isOtpFunction(exp.name) || this.isOtpVariable(exp.name)
326
+ );
327
+
328
+ for (const otpExport of otpExports) {
329
+ // Find where this OTP symbol is imported and used
330
+ const crossFileUsages = await this.findCrossFileUsages(otpExport.name, [filePath]);
331
+
332
+ for (const usage of crossFileUsages) {
333
+ const usageSymbolTable = await this.getSymbolTable(usage.filePath);
334
+ if (!usageSymbolTable) continue;
335
+
336
+ // Check if the imported OTP symbol is used safely
337
+ const dangerousUsage = await this.analyzeImportedOtpUsage(
338
+ usageSymbolTable,
339
+ otpExport.name,
340
+ usage
341
+ );
342
+
343
+ if (dangerousUsage) {
344
+ this.addViolation({
345
+ filePath: usage.filePath,
346
+ line: usage.line,
347
+ column: usage.column || 1,
348
+ message: `Imported OTP symbol '${otpExport.name}' used unsafely`,
349
+ type: 'semantic_cross_file_otp_violation',
350
+ severity: 'warning',
351
+ symbolContext: {
352
+ exportedFrom: filePath,
353
+ importedSymbol: otpExport.name,
354
+ usageContext: dangerousUsage
355
+ },
356
+ crossFileReferences: [{
357
+ sourceFile: filePath,
358
+ targetFile: usage.filePath,
359
+ symbol: otpExport.name,
360
+ usageType: dangerousUsage.type
361
+ }],
362
+ suggestion: 'Ensure consistent OTP security across file boundaries'
363
+ });
364
+ }
365
+ }
366
+ }
367
+ }
368
+
369
+ /**
370
+ * Helper methods for semantic analysis
371
+ */
372
+
373
+ isOtpVariable(variableName) {
374
+ return this.otpPatterns.variableNames.some(pattern => pattern.test(variableName));
375
+ }
376
+
377
+ isOtpFunction(functionName) {
378
+ return this.otpPatterns.functionNames.some(pattern => pattern.test(functionName)) ||
379
+ this.otpPatterns.methodNames.includes(functionName);
380
+ }
381
+
382
+ isDangerousOperation(operationName) {
383
+ return Object.values(this.dangerousOperations).flat().some(op =>
384
+ operationName.includes(op) || operationName === op
385
+ );
386
+ }
387
+
388
+ isSafeOperation(operationName) {
389
+ return this.safeOperations.some(op => operationName.includes(op));
390
+ }
391
+
392
+ async findDangerousVariableUsages(symbolTable, variableName, declarationLine) {
393
+ const usages = [];
394
+
395
+ // Check function calls that use this variable with safe array access
396
+ const functionCalls = symbolTable.functionCalls || [];
397
+ const relatedCalls = functionCalls.filter(call =>
398
+ call.arguments && call.arguments.some(arg => arg && arg.includes && arg.includes(variableName))
399
+ );
400
+
401
+ for (const call of relatedCalls) {
402
+ if (this.isDangerousOperation(call.functionName)) {
403
+ usages.push({
404
+ line: call.line,
405
+ column: call.column,
406
+ context: this.categorizeDangerousOperation(call.functionName),
407
+ operation: call.functionName,
408
+ codeSnippet: `${call.functionName}(${call.arguments.join(', ')})`
409
+ });
410
+ }
411
+ }
412
+
413
+ return usages;
414
+ }
415
+
416
+ categorizeDangerousOperation(operationName) {
417
+ if (this.dangerousOperations.storage.some(op => operationName.includes(op))) {
418
+ return 'storage';
419
+ }
420
+ if (this.dangerousOperations.transmission.some(op => operationName.includes(op))) {
421
+ return 'transmission';
422
+ }
423
+ if (this.dangerousOperations.browserStorage.some(op => operationName.includes(op))) {
424
+ return 'browser_storage';
425
+ }
426
+ if (this.dangerousOperations.logging.some(op => operationName.includes(op))) {
427
+ return 'logging';
428
+ }
429
+ return 'unknown_dangerous';
430
+ }
431
+
432
+ determineSeverity(context) {
433
+ const severityMap = {
434
+ 'storage': 'error',
435
+ 'transmission': 'error',
436
+ 'browser_storage': 'warning',
437
+ 'logging': 'warning',
438
+ 'unknown_dangerous': 'info'
439
+ };
440
+ return severityMap[context] || 'warning';
441
+ }
442
+
443
+ generateSecuritySuggestion(context) {
444
+ const suggestions = {
445
+ 'storage': 'Use bcrypt.hash() or crypto.createHash() before storing OTP',
446
+ 'transmission': 'Encrypt OTP before transmission or use secure channels',
447
+ 'browser_storage': 'Avoid storing OTP in browser storage, use secure tokens instead',
448
+ 'logging': 'Never log actual OTP values, log only metadata like generation time',
449
+ 'unknown_dangerous': 'Review this operation for OTP security best practices'
450
+ };
451
+ return suggestions[context] || 'Ensure OTP security best practices';
452
+ }
453
+
454
+ async analyzeFunctionContext(symbolTable, functionCall) {
455
+ // Analyze the context around the function call
456
+ const context = {
457
+ isDangerous: false,
458
+ isSecure: false,
459
+ securityMeasures: []
460
+ };
461
+
462
+ // Check if function is called within a security context
463
+ const nearbyLines = this.getNearbyLines(symbolTable, functionCall.line, 3);
464
+
465
+ for (const line of nearbyLines) {
466
+ if (this.safeOperations.some(op => line.text.includes(op))) {
467
+ context.isSecure = true;
468
+ context.securityMeasures.push(line.text);
469
+ }
470
+
471
+ if (this.isDangerousOperation(line.text)) {
472
+ context.isDangerous = true;
473
+ }
474
+ }
475
+
476
+ return context;
477
+ }
478
+
479
+ async checkOtpArguments(symbolTable, functionCall) {
480
+ const otpArguments = [];
481
+
482
+ if (!functionCall.arguments) return otpArguments;
483
+
484
+ for (const arg of functionCall.arguments) {
485
+ // Check if argument looks like OTP variable
486
+ if (this.isOtpVariable(arg)) {
487
+ otpArguments.push({
488
+ value: arg,
489
+ type: 'variable',
490
+ confidence: 'high'
491
+ });
492
+ }
493
+
494
+ // Check if argument is a string that looks like OTP
495
+ if (this.looksLikeOtpValue(arg)) {
496
+ otpArguments.push({
497
+ value: arg,
498
+ type: 'literal',
499
+ confidence: 'medium'
500
+ });
501
+ }
502
+ }
503
+
504
+ return otpArguments;
505
+ }
506
+
507
+ looksLikeOtpValue(value) {
508
+ // Patterns that suggest OTP values
509
+ const otpValuePatterns = [
510
+ /^['"`]\d{4,8}['"`]$/, // 4-8 digit codes in quotes
511
+ /^['"`][A-Z0-9]{4,8}['"`]$/i, // 4-8 alphanumeric codes
512
+ /\$\{.*otp.*\}/i, // Template strings with otp
513
+ /\+.*otp/i // String concatenation with otp
514
+ ];
515
+
516
+ return otpValuePatterns.some(pattern => pattern.test(value));
517
+ }
518
+
519
+ isOtpRelatedMethodCall(methodCall) {
520
+ return this.isOtpVariable(methodCall.objectName) ||
521
+ this.isOtpFunction(methodCall.methodName) ||
522
+ (methodCall.objectName && methodCall.objectName.toLowerCase().includes('otp'));
523
+ }
524
+
525
+ async analyzeMethodChain(symbolTable, methodCall) {
526
+ const chain = `${methodCall.objectName}.${methodCall.methodName}()`;
527
+ const isUnsafe = this.isDangerousOperation(methodCall.methodName) &&
528
+ !this.isSafeOperation(methodCall.methodName);
529
+
530
+ return {
531
+ chain,
532
+ isUnsafe,
533
+ dataFlow: await this.traceMethodChainDataFlow(symbolTable, methodCall)
534
+ };
535
+ }
536
+
537
+ async traceDataFlow(symbolTable, variableName) {
538
+ const dataFlow = [];
539
+
540
+ // Find all usages of this variable in function calls
541
+ const functionCalls = symbolTable.functionCalls || [];
542
+ for (const call of functionCalls) {
543
+ if (call.arguments && call.arguments.some(arg => arg && arg.includes && arg.includes(variableName))) {
544
+ dataFlow.push({
545
+ line: call.line,
546
+ operation: call.functionName,
547
+ type: 'function_call',
548
+ distance: Math.abs(call.line - this.getVariableDeclarationLine(symbolTable, variableName))
549
+ });
550
+ }
551
+ }
552
+
553
+ // Find all usages in method calls
554
+ const methodCalls = symbolTable.methodCalls || [];
555
+ for (const call of methodCalls) {
556
+ if (call.arguments && call.arguments.some(arg => arg && arg.includes && arg.includes(variableName))) {
557
+ dataFlow.push({
558
+ line: call.line,
559
+ operation: `${call.objectName}.${call.methodName}`,
560
+ type: 'method_call',
561
+ distance: Math.abs(call.line - this.getVariableDeclarationLine(symbolTable, variableName))
562
+ });
563
+ }
564
+ }
565
+
566
+ return dataFlow.sort((a, b) => a.line - b.line);
567
+ }
568
+
569
+ getVariableDeclarationLine(symbolTable, variableName) {
570
+ const variables = symbolTable.variables || [];
571
+ const constants = symbolTable.constants || [];
572
+ const variable = variables.find(v => v.name === variableName) ||
573
+ constants.find(v => v.name === variableName);
574
+ return variable ? variable.line : 1;
575
+ }
576
+
577
+ async traceMethodChainDataFlow(symbolTable, methodCall) {
578
+ // Simplified data flow analysis for method chains
579
+ return {
580
+ source: methodCall.objectName,
581
+ operation: methodCall.methodName,
582
+ line: methodCall.line
583
+ };
584
+ }
585
+
586
+ async analyzeImportedOtpUsage(symbolTable, symbolName, usage) {
587
+ // Check how the imported OTP symbol is used
588
+ const functionCalls = symbolTable.functionCalls || [];
589
+ const usages = functionCalls.filter(call =>
590
+ call.functionName === symbolName ||
591
+ (call.arguments && call.arguments.includes(symbolName))
592
+ );
593
+
594
+ for (const use of usages) {
595
+ if (this.isDangerousOperation(use.functionName) && !this.isSafeOperation(use.functionName)) {
596
+ return {
597
+ type: 'dangerous_usage',
598
+ operation: use.functionName,
599
+ line: use.line,
600
+ context: this.categorizeDangerousOperation(use.functionName)
601
+ };
602
+ }
603
+ }
604
+
605
+ return null;
606
+ }
607
+ }
608
+
609
+ module.exports = S007SemanticAnalyzer;