@sun-asterisk/sunlint 1.3.28 → 1.3.30

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.
@@ -81,6 +81,20 @@
81
81
  - Use verbs that describe specific actions
82
82
  - Boolean functions should start with is/has/can/should
83
83
  - Avoid generic names that lack context
84
+ - Accepted verbs (reference list):
85
+ - Getters/Queries: `get`, `fetch`, `retrieve`, `find`, `search`, `query`, `load`
86
+ - Setters/Modifiers: `set`, `update`, `modify`, `change`, `edit`, `alter`, `transform`
87
+ - Creation: `create`, `build`, `make`, `generate`, `construct`, `produce`
88
+ - Deletion: `delete`, `remove`, `destroy`, `clean`, `clear`, `reset`
89
+ - Validation: `validate`, `verify`, `check`, `confirm`, `ensure`, `test`, `compare`
90
+ - Computation: `calculate`, `compute`, `parse`, `format`, `convert`
91
+ - Communication: `send`, `receive`, `transmit`, `broadcast`, `emit`, `publish`
92
+ - Collections: `map`, `filter`, `sort`, `group`, `merge`, `split`, `add`, `append`, `insert`
93
+ - State checks: `is`, `has`, `can`, `should`, `will`, `does`
94
+ - UI actions: `show`, `hide`, `display`, `render`, `draw`, `toggle`, `enable`, `disable`
95
+ - Lifecycle: `connect`, `disconnect`, `open`, `close`, `start`, `stop`, `run`, `refresh`
96
+ - Event Handling: `on`, `trigger`, `fire`, `dispatch`, `invoke`, `call`
97
+ - Monitoring: `count`, `measure`, `monitor`, `watch`, `track`, `observe`
84
98
  - **Applies to**: All languages
85
99
  - **Tools**: PR review, AI Suggestion (Copilot Review)
86
100
  - **Principles**: CODE_QUALITY
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sun-asterisk/sunlint",
3
- "version": "1.3.28",
3
+ "version": "1.3.30",
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": {
@@ -277,7 +277,7 @@ class SmartC006Analyzer {
277
277
  'delete', 'remove', 'destroy', 'clean', 'clear', 'reset',
278
278
  'load', 'save', 'fetch', 'retrieve', 'find', 'search', 'query',
279
279
  'validate', 'verify', 'check', 'confirm', 'ensure', 'test',
280
- 'calculate', 'compute', 'parse', 'format', 'convert',
280
+ 'calculate', 'compute', 'compare', 'parse', 'format', 'convert',
281
281
  'send', 'receive', 'transmit', 'broadcast', 'emit', 'publish',
282
282
  'map', 'filter', 'sort', 'group', 'merge', 'split',
283
283
  'connect', 'disconnect', 'open', 'close', 'start', 'stop', 'run',
@@ -428,10 +428,13 @@ class SmartC006Analyzer {
428
428
 
429
429
  let reason = `Function '${functionName}' should follow verb-noun naming pattern`;
430
430
  let suggestion = this.generateSmartSuggestion(functionName, semanticIntent, architecturalContext);
431
-
431
+
432
432
  if (architecturalContext.layer !== 'UNKNOWN') {
433
433
  reason += ` (${architecturalContext.layer} layer)`;
434
434
  }
435
+
436
+ // Add helpful note about accepted verbs
437
+ reason += `. Accepted verbs: get, set, create, update, delete, validate, check, compare, etc. See: https://coding-standards.sun-asterisk.vn/rules/rule/C006/`;
435
438
 
436
439
  return {
437
440
  isViolation: true,
@@ -29,7 +29,7 @@ class S041Analyzer {
29
29
  // Configuration
30
30
  this.config = {
31
31
  useSymbolBased: true, // Primary approach
32
- fallbackToRegex: true, // Secondary approach
32
+ fallbackToRegex: false, // Secondary approach
33
33
  regexBasedOnly: false, // Can be set to true for pure mode
34
34
  };
35
35
 
@@ -11,6 +11,17 @@ class S041SymbolBasedAnalyzer {
11
11
  this.ruleId = "S041";
12
12
  this.category = "security";
13
13
 
14
+ this.skipPatterns = [
15
+ /\/node_modules\//,
16
+ /\/tests?\//,
17
+ /\/dist\//,
18
+ /\/build\//,
19
+ /\.spec\.ts$/,
20
+ /\.spec\.tsx$/,
21
+ /\.test\.ts$/,
22
+ /\.test\.tsx$/,
23
+ ];
24
+
14
25
  // Session management methods that should invalidate tokens
15
26
  this.sessionMethods = [
16
27
  "logout",
@@ -106,7 +117,7 @@ class S041SymbolBasedAnalyzer {
106
117
  }
107
118
  }
108
119
 
109
- async analyze(filePath) {
120
+ async analyze(sourceFile, filePath) {
110
121
  if (this.verbose) {
111
122
  console.log(
112
123
  `🔍 [${this.ruleId}] Symbol: Starting analysis for ${filePath}`
@@ -123,7 +134,6 @@ class S041SymbolBasedAnalyzer {
123
134
  }
124
135
 
125
136
  try {
126
- const sourceFile = this.semanticEngine.getSourceFile(filePath);
127
137
  if (!sourceFile) {
128
138
  if (this.verbose) {
129
139
  console.log(
@@ -179,6 +189,11 @@ class S041SymbolBasedAnalyzer {
179
189
  console.log(`🔍 [${this.ruleId}] Symbol: Starting symbol-based analysis`);
180
190
  }
181
191
 
192
+ if (this.shouldIgnoreFile(filePath)) {
193
+ if (verbose) console.log(`[${this.ruleId}] Ignoring ${filePath}`);
194
+ return [];
195
+ }
196
+
182
197
  const callExpressions = sourceFile.getDescendantsOfKind
183
198
  ? sourceFile.getDescendantsOfKind(
184
199
  require("typescript").SyntaxKind.CallExpression
@@ -669,6 +684,10 @@ class S041SymbolBasedAnalyzer {
669
684
  return null;
670
685
  }
671
686
  }
687
+
688
+ shouldIgnoreFile(filePath) {
689
+ return this.skipPatterns.some((pattern) => pattern.test(filePath));
690
+ }
672
691
  }
673
692
 
674
693
  module.exports = S041SymbolBasedAnalyzer;
@@ -25,7 +25,9 @@ class S042SymbolBasedAnalyzer {
25
25
  /\/dist\//,
26
26
  /\/build\//,
27
27
  /\.spec\.ts$/,
28
- /\.test\.ts$/
28
+ /\.spec\.tsx$/,
29
+ /\.test\.ts$/,
30
+ /\.test\.tsx$/,
29
31
  ];
30
32
 
31
33
  // Sensitive action patterns
@@ -679,13 +681,16 @@ class S042SymbolBasedAnalyzer {
679
681
  const hasIdleTimeout = this.hasIdleTimeoutLogic(sourceFile);
680
682
 
681
683
  if (hasSessionConfig && !hasIdleTimeout) {
684
+ // Try to find the actual session configuration location
685
+ const sessionConfigLocation = this.findSessionConfigLocation(sourceFile);
686
+
682
687
  violations.push({
683
688
  ruleId: this.ruleId,
684
689
  ruleName: this.ruleName,
685
690
  severity: 'medium',
686
691
  message: `Session management detected but no idle timeout implementation found.`,
687
- line: 1,
688
- column: 1,
692
+ line: sessionConfigLocation.line,
693
+ column: sessionConfigLocation.column,
689
694
  filePath: sourceFile.getFilePath(),
690
695
  type: 'MISSING_IDLE_TIMEOUT_LOGIC',
691
696
  details: `Implement idle timeout to automatically expire sessions after ${this.formatDuration(this.MAX_IDLE_TIME)} of inactivity.`
@@ -695,6 +700,91 @@ class S042SymbolBasedAnalyzer {
695
700
  return violations;
696
701
  }
697
702
 
703
+ findSessionConfigLocation(sourceFile) {
704
+ const defaultLocation = { line: 1, column: 1 };
705
+
706
+ try {
707
+ // Look for session-related call expressions
708
+ const callExpressions = sourceFile.getDescendantsOfKind(SyntaxKind.CallExpression);
709
+
710
+ for (const callExpr of callExpressions) {
711
+ const expression = callExpr.getExpression();
712
+ const expressionText = expression.getText();
713
+
714
+ // Check for session middleware calls
715
+ if (expressionText.match(/session|express-session|cookie-session/i)) {
716
+ const args = callExpr.getArguments();
717
+
718
+ // If there's a config object argument, use its location
719
+ if (args.length > 0 && args[0].getKind() === SyntaxKind.ObjectLiteralExpression) {
720
+ const configObj = args[0];
721
+ return {
722
+ line: configObj.getStartLineNumber(),
723
+ column: configObj.getStart() - configObj.getStartLinePos() + 1
724
+ };
725
+ }
726
+
727
+ // Otherwise use the call expression location
728
+ return {
729
+ line: callExpr.getStartLineNumber(),
730
+ column: callExpr.getStart() - callExpr.getStartLinePos() + 1
731
+ };
732
+ }
733
+ }
734
+
735
+ // Look for variable declarations with session config
736
+ const variableDeclarations = sourceFile.getDescendantsOfKind(SyntaxKind.VariableDeclaration);
737
+
738
+ for (const varDecl of variableDeclarations) {
739
+ const name = varDecl.getName();
740
+
741
+ if (name.match(/sessionConfig|sessionOptions|sessionMiddleware/i)) {
742
+ const initializer = varDecl.getInitializer();
743
+
744
+ if (initializer) {
745
+ return {
746
+ line: initializer.getStartLineNumber(),
747
+ column: initializer.getStart() - initializer.getStartLinePos() + 1
748
+ };
749
+ }
750
+ }
751
+ }
752
+
753
+ // Look for object literals with session config keys
754
+ const objectLiterals = sourceFile.getDescendantsOfKind(SyntaxKind.ObjectLiteralExpression);
755
+
756
+ for (const objLiteral of objectLiterals) {
757
+ const properties = objLiteral.getProperties();
758
+ let sessionKeyCount = 0;
759
+
760
+ for (const prop of properties) {
761
+ if (prop.getKind() === SyntaxKind.PropertyAssignment) {
762
+ const name = prop.getName();
763
+ if (this.sessionConfigKeys.some(key => name === key)) {
764
+ sessionKeyCount++;
765
+ }
766
+ }
767
+ }
768
+
769
+ // If we found multiple session config keys, this is likely the config
770
+ if (sessionKeyCount >= 2) {
771
+ return {
772
+ line: objLiteral.getStartLineNumber(),
773
+ column: objLiteral.getStart() - objLiteral.getStartLinePos() + 1
774
+ };
775
+ }
776
+ }
777
+
778
+ } catch (error) {
779
+ // Fall back to default location on error
780
+ if (verbose) {
781
+ console.warn(`[${this.ruleId}] Failed to find session config location:`, error.message);
782
+ }
783
+ }
784
+
785
+ return defaultLocation;
786
+ }
787
+
698
788
  // Helper methods
699
789
 
700
790
  isJWTSignCall(expressionText) {
@@ -1046,7 +1136,10 @@ class S042SymbolBasedAnalyzer {
1046
1136
 
1047
1137
  hasSessionConfiguration(sourceFile) {
1048
1138
  const text = sourceFile.getText();
1049
- return /session\(|express-session|cookie-session|getSessionConfig|sessionConfig/i.test(text);
1139
+
1140
+ // More specific patterns for server-side session middleware
1141
+ // Avoid matching client-side hooks like useSession()
1142
+ return /express-session|cookie-session|session\.Session|getSessionConfig|sessionConfig|sessionOptions|sessionMiddleware|session\(\{|require\(['"](express-session|cookie-session)['"]\)/i.test(text);
1050
1143
  }
1051
1144
 
1052
1145
  hasIdleTimeoutLogic(sourceFile) {