@sun-asterisk/sunlint 1.3.1 → 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 (66) hide show
  1. package/CHANGELOG.md +47 -0
  2. package/CONTRIBUTING.md +210 -1691
  3. package/config/rule-analysis-strategies.js +17 -1
  4. package/config/rules/enhanced-rules-registry.json +369 -1135
  5. package/config/rules/rules-registry-generated.json +1 -1
  6. package/core/enhanced-rules-registry.js +2 -1
  7. package/core/semantic-engine.js +15 -3
  8. package/core/semantic-rule-base.js +4 -2
  9. package/engines/heuristic-engine.js +65 -4
  10. package/integrations/eslint/plugin/rules/common/c003-no-vague-abbreviations.js +59 -1
  11. package/integrations/eslint/plugin/rules/common/c006-function-name-verb-noun.js +26 -1
  12. package/integrations/eslint/plugin/rules/common/c030-use-custom-error-classes.js +54 -19
  13. package/origin-rules/common-en.md +11 -7
  14. package/package.json +1 -1
  15. package/rules/common/C002_no_duplicate_code/analyzer.js +334 -36
  16. package/rules/common/C003_no_vague_abbreviations/analyzer.js +220 -35
  17. package/rules/common/C006_function_naming/analyzer.js +29 -3
  18. package/rules/common/C010_limit_block_nesting/analyzer.js +181 -337
  19. package/rules/common/C010_limit_block_nesting/config.json +64 -0
  20. package/rules/common/C010_limit_block_nesting/regex-based-analyzer.js +379 -0
  21. package/rules/common/C010_limit_block_nesting/symbol-based-analyzer.js +231 -0
  22. package/rules/common/C013_no_dead_code/analyzer.js +75 -177
  23. package/rules/common/C013_no_dead_code/config.json +61 -0
  24. package/rules/common/C013_no_dead_code/regex-based-analyzer.js +345 -0
  25. package/rules/common/C013_no_dead_code/symbol-based-analyzer.js +640 -0
  26. package/rules/common/C014_dependency_injection/analyzer.js +48 -313
  27. package/rules/common/C014_dependency_injection/config.json +26 -0
  28. package/rules/common/C014_dependency_injection/symbol-based-analyzer.js +751 -0
  29. package/rules/common/C018_no_throw_generic_error/analyzer.js +232 -0
  30. package/rules/common/C018_no_throw_generic_error/config.json +50 -0
  31. package/rules/common/C018_no_throw_generic_error/regex-based-analyzer.js +387 -0
  32. package/rules/common/C018_no_throw_generic_error/symbol-based-analyzer.js +314 -0
  33. package/rules/common/C019_log_level_usage/analyzer.js +110 -317
  34. package/rules/common/C019_log_level_usage/pattern-analyzer.js +88 -0
  35. package/rules/common/C019_log_level_usage/system-log-analyzer.js +1267 -0
  36. package/rules/common/C023_no_duplicate_variable/analyzer.js +180 -0
  37. package/rules/common/C023_no_duplicate_variable/config.json +50 -0
  38. package/rules/common/C023_no_duplicate_variable/symbol-based-analyzer.js +158 -0
  39. package/rules/common/C024_no_scatter_hardcoded_constants/analyzer.js +180 -0
  40. package/rules/common/C024_no_scatter_hardcoded_constants/config.json +50 -0
  41. package/rules/common/C024_no_scatter_hardcoded_constants/symbol-based-analyzer.js +181 -0
  42. package/rules/common/C030_use_custom_error_classes/analyzer.js +200 -0
  43. package/rules/common/C035_error_logging_context/analyzer.js +3 -1
  44. package/rules/index.js +5 -1
  45. package/rules/security/S009_no_insecure_encryption/README.md +158 -0
  46. package/rules/security/S009_no_insecure_encryption/analyzer.js +319 -0
  47. package/rules/security/S009_no_insecure_encryption/config.json +55 -0
  48. package/rules/security/S010_no_insecure_encryption/README.md +224 -0
  49. package/rules/security/S010_no_insecure_encryption/analyzer.js +493 -0
  50. package/rules/security/S010_no_insecure_encryption/config.json +48 -0
  51. package/rules/security/S016_no_sensitive_querystring/STRATEGY.md +149 -0
  52. package/rules/security/S016_no_sensitive_querystring/analyzer.js +276 -0
  53. package/rules/security/S016_no_sensitive_querystring/config.json +127 -0
  54. package/rules/security/S016_no_sensitive_querystring/regex-based-analyzer.js +258 -0
  55. package/rules/security/S016_no_sensitive_querystring/symbol-based-analyzer.js +495 -0
  56. package/rules/security/S048_no_current_password_in_reset/README.md +222 -0
  57. package/rules/security/S048_no_current_password_in_reset/analyzer.js +366 -0
  58. package/rules/security/S048_no_current_password_in_reset/config.json +48 -0
  59. package/rules/security/S055_content_type_validation/README.md +176 -0
  60. package/rules/security/S055_content_type_validation/analyzer.js +312 -0
  61. package/rules/security/S055_content_type_validation/config.json +48 -0
  62. package/rules/utils/rule-helpers.js +140 -1
  63. package/scripts/consolidate-config.js +116 -0
  64. package/config/rules/S027-categories.json +0 -122
  65. package/config/rules/rules-registry.json +0 -777
  66. package/rules/common/C006_function_naming/smart-analyzer.js +0 -503
@@ -441,7 +441,7 @@
441
441
  },
442
442
  "C019": {
443
443
  "name": "Do not use `error` log level for non-critical issues",
444
- "description": "Avoid noisy logs and false alarms; ensure meaningful log levels.",
444
+ "description": "Prevent noisy logs and false alarms; ensure consistent and meaningful log levels across the system.",
445
445
  "category": "Common",
446
446
  "severity": "major",
447
447
  "languages": [
@@ -100,6 +100,7 @@ class EnhancedRulesRegistry {
100
100
  'C006': ['eslint', 'heuristic', 'openai'],
101
101
  'C007': ['eslint', 'heuristic', 'openai'],
102
102
  'C014': ['eslint', 'heuristic', 'openai'],
103
+ 'C018': ['heuristic', 'eslint'],
103
104
  'C033': ['heuristic', 'eslint'],
104
105
  'C035': ['heuristic', 'eslint'],
105
106
  'C040': ['eslint', 'heuristic'],
@@ -328,4 +329,4 @@ class EnhancedRulesRegistry {
328
329
  }
329
330
  }
330
331
 
331
- module.exports = EnhancedRulesRegistry;
332
+ module.exports = EnhancedRulesRegistry;
@@ -82,9 +82,13 @@ class SemanticEngine {
82
82
  libFolderPath: undefined, // Don't load TypeScript libs
83
83
  };
84
84
 
85
- // Only use tsconfig when auto-discovering files (no targetFiles)
86
- if (!targetFiles && tsConfigPath) {
87
- projectOptions.tsConfigFilePath = tsConfigPath;
85
+ // NEVER use project tsconfig.json to avoid file resolution issues
86
+ // Instead, load files explicitly to ensure they can be found
87
+ if (this.options.verbose) {
88
+ console.log(`🔧 SemanticEngine: Skipping project tsconfig.json to avoid file resolution issues`);
89
+ if (tsConfigPath) {
90
+ console.log(` 📋 Found tsconfig: ${tsConfigPath} (ignored for better compatibility)`);
91
+ }
88
92
  }
89
93
 
90
94
  this.project = new Project(projectOptions);
@@ -653,6 +657,14 @@ class SemanticEngine {
653
657
  getParentContext(callExpr) { return null; }
654
658
  isKnownHook(functionName) { return false; }
655
659
  findSymbolUsages(sourceFile, namedImports) { return []; }
660
+
661
+ /**
662
+ * Check if symbol engine is ready for symbol-based analysis
663
+ * @returns {boolean} true if project is initialized and ready
664
+ */
665
+ isSymbolEngineReady() {
666
+ return this.initialized && this.project !== null;
667
+ }
656
668
  }
657
669
 
658
670
  module.exports = SemanticEngine;
@@ -41,14 +41,16 @@ class SemanticRuleBase {
41
41
  /**
42
42
  * Initialize rule with SemanticEngine instance
43
43
  */
44
- initialize(semanticEngine) {
44
+ initialize(semanticEngine, options = {}) {
45
45
  this.semanticEngine = semanticEngine;
46
46
 
47
47
  if (!this.semanticEngine || !this.semanticEngine.initialized) {
48
48
  throw new Error(`${this.ruleId}: SemanticEngine is required and must be initialized`);
49
49
  }
50
50
 
51
- console.log(`🔧 Rule ${this.ruleId} initialized with semantic analysis`);
51
+ if (options?.verbose) {
52
+ console.log(`🔧 Rule ${this.ruleId} initialized with semantic analysis`);
53
+ }
52
54
  }
53
55
 
54
56
  /**
@@ -56,8 +56,13 @@ class HeuristicEngine extends AnalysisEngineInterface {
56
56
  // Initialize rule adapter
57
57
  await this.ruleAdapter.initialize();
58
58
 
59
- // Load available rules from unified registry
60
- await this.loadRulesFromRegistry(config);
59
+ // Load available rules from unified registry (OPTIMIZED: skip for performance)
60
+ // Rules will be loaded on-demand in analyze() method
61
+ if (config.loadAllRules) {
62
+ await this.loadRulesFromRegistry(config);
63
+ } else if (this.verbose) {
64
+ console.log(`⚡ [HeuristicEngine] Skipping bulk rule loading for performance - will load on-demand`);
65
+ }
61
66
 
62
67
  this.initialized = true;
63
68
  if (this.verbose) {
@@ -89,6 +94,8 @@ class HeuristicEngine extends AnalysisEngineInterface {
89
94
  };
90
95
 
91
96
  this.semanticEngine = new SemanticEngine(semanticOptions);
97
+ // Pass verbose option to semantic engine
98
+ this.semanticEngine.verbose = this.verbose;
92
99
 
93
100
  // ts-morph is now a core dependency - but optimized for targeted files
94
101
  const success = await this.semanticEngine.initialize(projectPath, config?.targetFiles);
@@ -287,6 +294,43 @@ class HeuristicEngine extends AnalysisEngineInterface {
287
294
  }
288
295
  }
289
296
 
297
+ /**
298
+ * Lazy load a single rule on-demand
299
+ * @param {string} ruleId - Rule ID to load
300
+ * @param {Object} options - Loading options
301
+ */
302
+ async lazyLoadRule(ruleId, options = {}) {
303
+ try {
304
+ const ruleDefinition = this.unifiedRegistry.getRuleDefinition(ruleId);
305
+
306
+ if (!ruleDefinition) {
307
+ if (options.verbose) {
308
+ console.warn(`⚠️ [HeuristicEngine] Rule definition not found for ${ruleId}`);
309
+ }
310
+ return;
311
+ }
312
+
313
+ // Check if rule supports heuristic engine
314
+ if (!this.unifiedRegistry.isRuleSupported(ruleId, 'heuristic')) {
315
+ if (options.verbose) {
316
+ console.warn(`⚠️ [HeuristicEngine] Rule ${ruleId} not supported by heuristic engine`);
317
+ }
318
+ return;
319
+ }
320
+
321
+ if (options.verbose) {
322
+ console.log(`🔄 [HeuristicEngine] Lazy loading rule ${ruleId}...`);
323
+ }
324
+
325
+ await this.loadRuleFromDefinition(ruleDefinition);
326
+
327
+ } catch (error) {
328
+ if (options.verbose) {
329
+ console.warn(`⚠️ [HeuristicEngine] Failed to lazy load rule ${ruleId}:`, error.message);
330
+ }
331
+ }
332
+ }
333
+
290
334
  /**
291
335
  * Manually load C047 semantic rule (special case)
292
336
  */
@@ -453,7 +497,7 @@ class HeuristicEngine extends AnalysisEngineInterface {
453
497
 
454
498
  try {
455
499
  const instance = new ruleEntry.analyzerClass(ruleId);
456
- instance.initialize(this.semanticEngine);
500
+ instance.initialize(this.semanticEngine, { verbose: this.verbose });
457
501
 
458
502
  // Update entry with initialized instance
459
503
  ruleEntry.instance = instance;
@@ -602,6 +646,14 @@ class HeuristicEngine extends AnalysisEngineInterface {
602
646
  await this.manuallyLoadC047();
603
647
  }
604
648
 
649
+ // Lazy load rule if not already loaded
650
+ if (!this.isRuleSupported(rule.id)) {
651
+ if (options.verbose) {
652
+ console.log(`🔄 [HeuristicEngine] Lazy loading rule ${rule.id}...`);
653
+ }
654
+ await this.lazyLoadRule(rule.id, options);
655
+ }
656
+
605
657
  if (!this.isRuleSupported(rule.id)) {
606
658
  if (options.verbose) {
607
659
  console.warn(`⚠️ Rule ${rule.id} not supported by Heuristic engine, skipping...`);
@@ -722,6 +774,15 @@ class HeuristicEngine extends AnalysisEngineInterface {
722
774
  async analyzeRule(rule, filesByLanguage, options) {
723
775
  // Get full rule ID (C029 -> C029_catch_block_logging)
724
776
  const fullRuleId = this.getFullRuleId(rule.id);
777
+
778
+ // Lazy load rule if not already loaded
779
+ if (!this.ruleAnalyzers.has(fullRuleId)) {
780
+ if (options.verbose) {
781
+ console.log(`🔄 [HeuristicEngine] Lazy loading rule ${rule.id} for analysis...`);
782
+ }
783
+ await this.lazyLoadRule(rule.id, options);
784
+ }
785
+
725
786
  const analyzerInfo = this.ruleAnalyzers.get(fullRuleId);
726
787
 
727
788
  if (!analyzerInfo) {
@@ -788,7 +849,7 @@ class HeuristicEngine extends AnalysisEngineInterface {
788
849
  rule.id,
789
850
  languageFiles,
790
851
  language,
791
- { ...ruleConfig, ...options }
852
+ { ...ruleConfig, ...options, semanticEngine: this.semanticEngine }
792
853
  );
793
854
 
794
855
  allViolations.push(...languageViolations);
@@ -92,6 +92,59 @@ const c003Rule = {
92
92
  return false;
93
93
  }
94
94
 
95
+ function isMathContext(node, name) {
96
+ // Check for math variable patterns
97
+ const mathPatterns = [
98
+ // Coordinate pairs: x1, y1, x2, y2
99
+ /^[xyz][12]$/i,
100
+ // Delta notation: dx, dy, dt, dr
101
+ /^d[xyztr]$/i,
102
+ // Math constants: a, b, c in equations
103
+ /^[abc]$/i,
104
+ // Vector components: vx, vy, vz
105
+ /^v[xyz]$/i,
106
+ // Position/point notation: p1, p2
107
+ /^p\d+$/i
108
+ ];
109
+
110
+ if (mathPatterns.some(pattern => pattern.test(name))) {
111
+ return true;
112
+ }
113
+
114
+ // Check if we're in a math function context
115
+ let parent = node.parent;
116
+ while (parent) {
117
+ if (parent.type === 'FunctionDeclaration' || parent.type === 'FunctionExpression' || parent.type === 'ArrowFunctionExpression') {
118
+ const functionName = parent.id && parent.id.name;
119
+ if (functionName && /^(distance|calculate|compute|solve|formula|algorithm|equation|math)/i.test(functionName)) {
120
+ return true;
121
+ }
122
+ break; // Don't check beyond the immediate function
123
+ }
124
+ parent = parent.parent;
125
+ }
126
+
127
+ // Check if we're in a context with Math operations
128
+ let currentNode = node.parent;
129
+ while (currentNode) {
130
+ if (currentNode.type === 'CallExpression') {
131
+ const callee = currentNode.callee;
132
+ if (callee && callee.object && callee.object.name === 'Math') {
133
+ return true;
134
+ }
135
+ if (callee && callee.name && /^(sqrt|pow|abs|sin|cos|tan|distance|calculate)$/i.test(callee.name)) {
136
+ return true;
137
+ }
138
+ }
139
+ if (currentNode.type === 'BinaryExpression' && ['+', '-', '*', '/'].includes(currentNode.operator)) {
140
+ return true;
141
+ }
142
+ currentNode = currentNode.parent;
143
+ }
144
+
145
+ return false;
146
+ }
147
+
95
148
  function checkVariableName(node, name) {
96
149
  // Safety checks
97
150
  if (!node || !name || typeof name !== 'string') {
@@ -112,7 +165,7 @@ const c003Rule = {
112
165
 
113
166
  // Single character check
114
167
  if (name.length === 1) {
115
- if (!allowedSingleChar.has(name.toLowerCase()) && !isCounterContext(node)) {
168
+ if (!allowedSingleChar.has(name.toLowerCase()) && !isCounterContext(node) && !isMathContext(node, name)) {
116
169
  context.report({
117
170
  node,
118
171
  messageId: "singleChar",
@@ -137,6 +190,11 @@ const c003Rule = {
137
190
  return;
138
191
  }
139
192
 
193
+ // Check for math context before flagging as unclear
194
+ if (isMathContext(node, name)) {
195
+ return;
196
+ }
197
+
140
198
  // Check for unclear/generic names
141
199
  if (unclearNames.has(name.toLowerCase())) {
142
200
  context.report({
@@ -79,6 +79,22 @@ const c006Rule = {
79
79
  ...commonVerbPrefixes,
80
80
  ...(options.allowedVerbs || [])
81
81
  ]);
82
+
83
+ // Generic/vague verbs that should be flagged even if they are technically verbs
84
+ const genericVerbs = new Set([
85
+ 'do', 'handle', 'process', 'manage', 'execute', 'work', 'stuff', 'thing', 'data'
86
+ ]);
87
+
88
+ function isGenericVerbUsage(name) {
89
+ // Check if the function name is exactly a generic verb or starts with generic verb + something generic
90
+ const genericPatterns = [
91
+ /^(do|handle|process|manage|execute)(Something|Stuff|Data|Info|Work|Thing|Items|Objects?)$/i,
92
+ /^(do|handle|process|manage|execute)$/i,
93
+ /^(do|handle|process|manage|execute)[A-Z].*$/i // Any pattern starting with generic verb + capital letter
94
+ ];
95
+
96
+ return genericPatterns.some(pattern => pattern.test(name));
97
+ }
82
98
 
83
99
  const allowConstructors = options.allowConstructors !== false;
84
100
 
@@ -142,7 +158,16 @@ const c006Rule = {
142
158
 
143
159
  // Check if it follows verb-noun pattern
144
160
  if (isVerbNounPattern(name)) {
145
- return; // Good! Follows the pattern
161
+ // But still check if it's using generic verbs that should be flagged
162
+ if (isGenericVerbUsage(name)) {
163
+ context.report({
164
+ node,
165
+ messageId: "notVerbNoun",
166
+ data: { name }
167
+ });
168
+ return;
169
+ }
170
+ return; // Good! Follows the pattern and not generic
146
171
  }
147
172
 
148
173
  // Check if it's likely a noun-only name
@@ -52,6 +52,10 @@ const c030Rule = {
52
52
  messages: {
53
53
  useCustomError: "Use custom error class instead of generic 'Error'. Consider using specific error types like ValidationError, NotFoundError, BusinessRuleError, etc. Vietnamese: 'Dùng custom error class thay vì Error generic'",
54
54
  useSpecificBuiltin: "Consider using a more specific built-in error type like TypeError, RangeError, or a custom error class. Vietnamese: 'Cân nhắc dùng built-in error cụ thể hơn hoặc custom error class'",
55
+ throwStringLiteral: "Use custom error classes instead of throwing string literals",
56
+ throwTemplateLiteral: "Use custom error classes instead of throwing template literals",
57
+ throwNumber: "Use custom error classes instead of throwing numbers",
58
+ throwVariable: "Use custom error classes instead of throwing variables",
55
59
  missingErrorCode: "Custom error class should include an error code property. Vietnamese: 'Custom error class nên có thuộc tính error code'",
56
60
  missingStatusCode: "HTTP-related error class should include a status code property. Vietnamese: 'Error class liên quan HTTP nên có thuộc tính status code'",
57
61
  preferCustomError: "Prefer custom error classes for better error categorization and handling. Vietnamese: 'Ưu tiên custom error classes để phân loại và xử lý lỗi tốt hơn'"
@@ -223,35 +227,66 @@ const c030Rule = {
223
227
  // Skip rethrow statements if allowed
224
228
  if (isRethrowStatement(node)) return;
225
229
 
226
- const errorClassName = getErrorClassName(node);
230
+ // Handle different throw argument types
231
+ if (node.argument) {
232
+ // Check for new Error(...) constructors
233
+ if (node.argument.type === 'NewExpression' &&
234
+ node.argument.callee &&
235
+ node.argument.callee.name === 'Error') {
236
+ context.report({
237
+ node: node.argument,
238
+ messageId: "useCustomError"
239
+ });
240
+ return;
241
+ }
227
242
 
228
- // Check for generic Error usage
229
- if (errorClassName === 'Error') {
230
- context.report({
231
- node: node.argument,
232
- messageId: "useCustomError"
233
- });
234
- return;
235
- }
243
+ // Check for other built-in error constructors
244
+ if (node.argument.type === 'NewExpression' &&
245
+ node.argument.callee &&
246
+ allowedBuiltinErrors.has(node.argument.callee.name)) {
247
+ if (['TypeError', 'RangeError'].includes(node.argument.callee.name)) {
248
+ context.report({
249
+ node: node.argument,
250
+ messageId: "useSpecificBuiltin"
251
+ });
252
+ }
253
+ return;
254
+ }
236
255
 
237
- // In strict mode, only custom errors are allowed
238
- if (strictMode && errorClassName) {
239
- if (!isCustomErrorClass(errorClassName) && !allowedBuiltinErrors.has(errorClassName)) {
256
+ // Check for throwing string literals
257
+ if (node.argument.type === 'Literal' && typeof node.argument.value === 'string') {
240
258
  context.report({
241
259
  node: node.argument,
242
- messageId: "preferCustomError"
260
+ messageId: "throwStringLiteral"
243
261
  });
262
+ return;
263
+ }
264
+
265
+ // Check for throwing template literals
266
+ if (node.argument.type === 'TemplateLiteral') {
267
+ context.report({
268
+ node: node.argument,
269
+ messageId: "throwTemplateLiteral"
270
+ });
271
+ return;
272
+ }
273
+
274
+ // Check for throwing numbers
275
+ if (node.argument.type === 'Literal' && typeof node.argument.value === 'number') {
276
+ context.report({
277
+ node: node.argument,
278
+ messageId: "throwNumber"
279
+ });
280
+ return;
244
281
  }
245
- }
246
282
 
247
- // Check for other built-in errors that could be more specific
248
- if (allowedBuiltinErrors.has(errorClassName) && !strictMode) {
249
- // Only suggest if it's a generic built-in error
250
- if (['TypeError', 'RangeError'].includes(errorClassName)) {
283
+ // Check for throwing variables (identifiers)
284
+ if (node.argument.type === 'Identifier') {
251
285
  context.report({
252
286
  node: node.argument,
253
- messageId: "useSpecificBuiltin"
287
+ messageId: "throwVariable"
254
288
  });
289
+ return;
255
290
  }
256
291
  }
257
292
  },
@@ -269,13 +269,17 @@
269
269
 
270
270
  ### 📘 Rule C019 – Do not use `error` log level for non-critical issues
271
271
 
272
- - **Objective**: Avoid noisy logs and false alarms; ensure meaningful log levels.
273
- - **Details**:
274
- - Use `info` or `warn` for recoverable or normal issues
275
- - Reserve `error` for critical failures that require immediate attention
276
- - Use `warn` for potential issues that don't crash the system
277
- - Use `info` for normal business flow events
278
- - Use `debug` for detailed information when troubleshooting
272
+ - **Objective**: Prevent noisy logs and false alarms; ensure consistent and meaningful log levels across the system.
273
+ - **Details**:
274
+ - Reserve `error` for critical failures that require immediate attention or system intervention.
275
+ - Use `warn` for potential issues that may affect functionality but don’t crash the system (e.g., retryable errors).
276
+ - Use `info` for normal business events (e.g., login, order success, expected validation failures).
277
+ - Use `debug` for detailed troubleshooting information; avoid excessive debug logs in production.
278
+ - Avoid using `error` for:
279
+ - Expected business cases (e.g., wrong password, expired card).
280
+ - Normal validation failures.
281
+ - Temporary, recoverable conditions (e.g., network retry).
282
+ - Additional goal: Ensure **logs exist at the right places with the right severity level**, avoiding both over-logging and missing critical logs.
279
283
  - **Applies to**: All languages
280
284
  - **Tools**: Log linter / Custom rule
281
285
  - **Principles**: CODE_QUALITY
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sun-asterisk/sunlint",
3
- "version": "1.3.1",
3
+ "version": "1.3.2",
4
4
  "description": "☀️ SunLint - Multi-language static analysis tool for code quality and security | Sun* Engineering Standards",
5
5
  "main": "cli.js",
6
6
  "bin": {