@sun-asterisk/sunlint 1.3.3 → 1.3.5

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 (31) hide show
  1. package/CHANGELOG.md +69 -2
  2. package/config/presets/all.json +49 -48
  3. package/config/presets/beginner.json +7 -18
  4. package/config/presets/ci.json +63 -27
  5. package/config/presets/maintainability.json +6 -4
  6. package/config/presets/performance.json +4 -3
  7. package/config/presets/quality.json +11 -50
  8. package/config/presets/recommended.json +83 -10
  9. package/config/presets/security.json +20 -19
  10. package/config/presets/strict.json +6 -13
  11. package/config/rules/enhanced-rules-registry.json +64 -7
  12. package/core/analysis-orchestrator.js +6 -0
  13. package/core/cli-action-handler.js +15 -1
  14. package/core/config-preset-resolver.js +7 -2
  15. package/engines/engine-factory.js +7 -0
  16. package/package.json +1 -1
  17. package/rules/common/C067_no_hardcoded_config/analyzer.js +95 -0
  18. package/rules/common/C067_no_hardcoded_config/config.json +81 -0
  19. package/rules/common/C067_no_hardcoded_config/symbol-based-analyzer.js +1000 -0
  20. package/rules/security/S024_xpath_xxe_protection/analyzer.js +242 -0
  21. package/rules/security/S024_xpath_xxe_protection/config.json +152 -0
  22. package/rules/security/S024_xpath_xxe_protection/regex-based-analyzer.js +338 -0
  23. package/rules/security/S024_xpath_xxe_protection/symbol-based-analyzer.js +474 -0
  24. package/rules/security/S025_server_side_validation/README.md +179 -0
  25. package/rules/security/S025_server_side_validation/analyzer.js +242 -0
  26. package/rules/security/S025_server_side_validation/config.json +111 -0
  27. package/rules/security/S025_server_side_validation/regex-based-analyzer.js +388 -0
  28. package/rules/security/S025_server_side_validation/symbol-based-analyzer.js +523 -0
  29. package/scripts/README.md +83 -0
  30. package/scripts/analyze-core-rules.js +151 -0
  31. package/scripts/generate-presets.js +202 -0
@@ -2,7 +2,6 @@
2
2
  "name": "@sun/sunlint/security",
3
3
  "description": "Security-focused configuration with all security rules",
4
4
  "rules": {
5
- "C041": "warn",
6
5
  "S001": "error",
7
6
  "S002": "error",
8
7
  "S003": "warn",
@@ -12,51 +11,51 @@
12
11
  "S007": "warn",
13
12
  "S008": "warn",
14
13
  "S009": "warn",
15
- "S010": "error",
16
- "S011": "error",
14
+ "S010": "warn",
15
+ "S011": "warn",
17
16
  "S012": "warn",
18
- "S013": "error",
17
+ "S013": "warn",
19
18
  "S014": "warn",
20
19
  "S015": "warn",
21
- "S016": "error",
20
+ "S016": "warn",
22
21
  "S017": "error",
23
22
  "S018": "warn",
24
23
  "S019": "warn",
25
- "S020": "error",
24
+ "S020": "warn",
26
25
  "S021": "warn",
27
26
  "S022": "warn",
28
- "S023": "error",
27
+ "S023": "warn",
29
28
  "S025": "warn",
30
29
  "S026": "warn",
31
30
  "S027": "warn",
32
31
  "S028": "warn",
33
- "S029": "error",
32
+ "S029": "warn",
34
33
  "S030": "warn",
35
- "S031": "error",
34
+ "S031": "warn",
36
35
  "S032": "warn",
37
36
  "S033": "warn",
38
37
  "S034": "warn",
39
38
  "S035": "warn",
40
- "S036": "error",
39
+ "S036": "warn",
41
40
  "S037": "warn",
42
- "S039": "error",
43
- "S040": "error",
41
+ "S039": "warn",
42
+ "S040": "warn",
44
43
  "S041": "warn",
45
44
  "S042": "warn",
46
45
  "S043": "warn",
47
46
  "S044": "warn",
48
47
  "S045": "warn",
49
48
  "S046": "warn",
50
- "S047": "error",
49
+ "S047": "warn",
51
50
  "S048": "warn",
52
51
  "S049": "warn",
53
52
  "S050": "warn",
54
53
  "S051": "warn",
55
54
  "S052": "warn",
56
55
  "S053": "warn",
57
- "S054": "error",
56
+ "S054": "warn",
58
57
  "S055": "warn",
59
- "S056": "error",
58
+ "S056": "warn",
60
59
  "S057": "warn",
61
60
  "S058": "warn",
62
61
  "S059": "warn"
@@ -80,9 +79,11 @@
80
79
  "**/*.min.*"
81
80
  ],
82
81
  "metadata": {
83
- "totalRules": 58,
84
- "removedRules": 0,
85
- "lastUpdated": "2025-07-30T08:59:10.121Z",
86
- "source": "origin-rules"
82
+ "totalRules": 57,
83
+ "securityRules": 57,
84
+ "approach": "security-focused",
85
+ "source": "security-en.md",
86
+ "lastUpdated": "2025-09-08T04:33:23.247Z",
87
+ "version": "2.0.0"
87
88
  }
88
89
  }
@@ -2,9 +2,9 @@
2
2
  "name": "@sun/sunlint/strict",
3
3
  "description": "Strict configuration for production projects",
4
4
  "rules": {
5
+ "C006": "error",
5
6
  "C019": "error",
6
- "C029": "error",
7
- "C006": "warn"
7
+ "C029": "error"
8
8
  },
9
9
  "categories": {
10
10
  "quality": "error",
@@ -25,18 +25,11 @@
25
25
  "**/*.generated.*",
26
26
  "**/*.min.*"
27
27
  ],
28
- "ai": {
29
- "enabled": true,
30
- "fallbackToPattern": false
31
- },
32
- "reporting": {
33
- "exitOnError": true,
34
- "showProgress": true
35
- },
36
28
  "metadata": {
37
29
  "totalRules": 3,
38
- "removedRules": 0,
39
- "lastUpdated": "2025-07-30T08:59:10.121Z",
40
- "source": "origin-rules"
30
+ "approach": "strict",
31
+ "source": "core rules as errors",
32
+ "lastUpdated": "2025-09-08T04:34:12.590Z",
33
+ "version": "2.0.0"
41
34
  }
42
35
  }
@@ -703,17 +703,51 @@
703
703
  }
704
704
  }
705
705
  },
706
+ "S024": {
707
+ "name": "Protect against XPath Injection and XML External Entity (XXE)",
708
+ "description": "Protect against XPath Injection and XML External Entity (XXE) attacks. XPath injection occurs when user input is used to construct XPath queries without proper sanitization. XXE attacks exploit XML parsers that process external entities, potentially leading to data disclosure, server-side request forgery, or denial of service.",
709
+ "category": "security",
710
+ "severity": "error",
711
+ "languages": ["typescript", "javascript"],
712
+ "analyzer": "./rules/security/S024_xpath_xxe_protection/analyzer.js",
713
+ "config": "./rules/security/S024_xpath_xxe_protection/config.json",
714
+ "version": "1.0.0",
715
+ "status": "stable",
716
+ "tags": ["security", "xpath", "xxe", "xml", "injection", "owasp"],
717
+ "strategy": {
718
+ "preferred": "ast",
719
+ "fallbacks": ["ast", "regex"],
720
+ "accuracy": {
721
+ "ast": 95,
722
+ "regex": 85
723
+ }
724
+ },
725
+ "engineMappings": {
726
+ "heuristic": ["rules/security/S024_xpath_xxe_protection/analyzer.js"]
727
+ }
728
+ },
706
729
  "S025": {
707
- "name": "Server Side Input Validation",
708
- "description": "Require server-side input validation",
730
+ "name": "Always validate client-side data on the server",
731
+ "description": "Ensure all client-side data is validated on the server. Client-side validation is not sufficient for security as it can be bypassed by attackers. Server-side validation is mandatory for data integrity and security.",
709
732
  "category": "security",
710
733
  "severity": "error",
711
734
  "languages": ["typescript", "javascript"],
712
- "analyzer": "eslint",
713
- "eslintRule": "custom/typescript_s025",
735
+ "analyzer": "./rules/security/S025_server_side_validation/analyzer.js",
736
+ "config": "./rules/security/S025_server_side_validation/config.json",
714
737
  "version": "1.0.0",
715
738
  "status": "stable",
716
- "tags": ["security", "validation", "server-side"]
739
+ "tags": ["security", "validation", "server-side", "owasp", "input-validation"],
740
+ "strategy": {
741
+ "preferred": "ast",
742
+ "fallbacks": ["ast", "regex"],
743
+ "accuracy": {
744
+ "ast": 95,
745
+ "regex": 85
746
+ }
747
+ },
748
+ "engineMappings": {
749
+ "heuristic": ["rules/security/S025_server_side_validation/analyzer.js"]
750
+ }
717
751
  },
718
752
  "S026": {
719
753
  "name": "JSON Schema Validation",
@@ -1301,6 +1335,28 @@
1301
1335
  "eslint": ["@typescript-eslint/naming-convention", "camelcase"]
1302
1336
  }
1303
1337
  },
1338
+ "C067": {
1339
+ "name": "No Hardcoded Configuration",
1340
+ "description": "Improve configurability, reduce risk when changing environments, and make configuration management flexible and maintainable.",
1341
+ "category": "configuration",
1342
+ "severity": "warning",
1343
+ "languages": ["typescript", "javascript", "dart", "kotlin"],
1344
+ "analyzer": "./rules/common/C067_no_hardcoded_config/analyzer.js",
1345
+ "config": "./rules/common/C067_no_hardcoded_config/config.json",
1346
+ "version": "1.0.0",
1347
+ "status": "stable",
1348
+ "tags": ["configuration", "hardcode", "environment", "maintainability", "security"],
1349
+ "strategy": {
1350
+ "preferred": "ast",
1351
+ "fallbacks": ["ast"],
1352
+ "accuracy": {
1353
+ "ast": 90
1354
+ }
1355
+ },
1356
+ "engineMappings": {
1357
+ "heuristic": ["rules/common/C067_no_hardcoded_config/analyzer.js"]
1358
+ }
1359
+ },
1304
1360
  "C072": {
1305
1361
  "id": "C072",
1306
1362
  "name": "Single Test Behavior",
@@ -1771,6 +1827,7 @@
1771
1827
  "S020",
1772
1828
  "S022",
1773
1829
  "S023",
1830
+ "S024",
1774
1831
  "S025",
1775
1832
  "S026",
1776
1833
  "S027",
@@ -1889,9 +1946,9 @@
1889
1946
  "metadata": {
1890
1947
  "version": "1.1.7",
1891
1948
  "lastUpdated": "2025-08-25",
1892
- "totalRules": 97,
1949
+ "totalRules": 98,
1893
1950
  "qualityRules": 33,
1894
- "securityRules": 49,
1951
+ "securityRules": 50,
1895
1952
  "stableRules": 45,
1896
1953
  "experimentalRules": 1,
1897
1954
  "supportedLanguages": 4,
@@ -345,6 +345,12 @@ class AnalysisOrchestrator {
345
345
  getEnginePreference(rule, config) {
346
346
  // If user specified a specific engine via --engine option, use only that engine
347
347
  if (config.requestedEngine) {
348
+ // Handle "auto" engine selection
349
+ if (config.requestedEngine === 'auto') {
350
+ // Auto-select best engines: default to heuristic, add eslint for JS/TS
351
+ return ['heuristic', 'eslint'];
352
+ }
353
+
348
354
  return [config.requestedEngine];
349
355
  }
350
356
 
@@ -164,6 +164,20 @@ class CliActionHandler {
164
164
  determineEnabledEngines(config) {
165
165
  // If specific engine is requested via --engine option, use only that engine
166
166
  if (this.options.engine) {
167
+ // Handle "auto" engine selection
168
+ if (this.options.engine === 'auto') {
169
+ // Auto-select best engines: default to heuristic for compatibility
170
+ const autoEngines = ['heuristic'];
171
+
172
+ // Add ESLint for JS/TS files if available
173
+ if (this.hasJavaScriptTypeScriptFiles() || config.eslint?.enabled !== false) {
174
+ autoEngines.push('eslint');
175
+ }
176
+
177
+ return autoEngines;
178
+ }
179
+
180
+ // Return specific engine as requested
167
181
  return [this.options.engine];
168
182
  }
169
183
 
@@ -272,7 +286,7 @@ class CliActionHandler {
272
286
  validateInput(config) {
273
287
  // Validate engine option if specified (check this first, always)
274
288
  if (this.options.engine) {
275
- const validEngines = ['eslint', 'heuristic', 'openai'];
289
+ const validEngines = ['auto', 'eslint', 'heuristic', 'openai'];
276
290
  if (!validEngines.includes(this.options.engine)) {
277
291
  throw new Error(
278
292
  chalk.red(`❌ Invalid engine: ${this.options.engine}\n`) +
@@ -10,9 +10,14 @@ class ConfigPresetResolver {
10
10
  constructor() {
11
11
  this.presetMap = {
12
12
  '@sun/sunlint/recommended': 'config/presets/recommended.json',
13
- '@sun/sunlint/strict': 'config/presets/strict.json',
13
+ '@sun/sunlint/security': 'config/presets/security.json',
14
+ '@sun/sunlint/quality': 'config/presets/quality.json',
14
15
  '@sun/sunlint/beginner': 'config/presets/beginner.json',
15
- '@sun/sunlint/ci': 'config/presets/ci.json'
16
+ '@sun/sunlint/ci': 'config/presets/ci.json',
17
+ '@sun/sunlint/strict': 'config/presets/strict.json',
18
+ '@sun/sunlint/maintainability': 'config/presets/maintainability.json',
19
+ '@sun/sunlint/performance': 'config/presets/performance.json',
20
+ '@sun/sunlint/all': 'config/presets/all.json'
16
21
  };
17
22
  }
18
23
 
@@ -51,6 +51,13 @@ class EngineFactory {
51
51
  let engine = null;
52
52
 
53
53
  switch (engineType.toLowerCase()) {
54
+ case 'auto':
55
+ // Auto-detection: default to heuristic for best performance/compatibility
56
+ engine = new HeuristicEngine();
57
+ if (this.verbose) {
58
+ console.log('🤖 Auto-detected engine: heuristic (best performance/compatibility)');
59
+ }
60
+ break;
54
61
  case 'heuristic':
55
62
  engine = new HeuristicEngine();
56
63
  break;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sun-asterisk/sunlint",
3
- "version": "1.3.3",
3
+ "version": "1.3.5",
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": {
@@ -0,0 +1,95 @@
1
+ // rules/common/C067_no_hardcoded_config/analyzer.js
2
+ const C067SymbolBasedAnalyzer = require('./symbol-based-analyzer.js');
3
+
4
+ class C067Analyzer {
5
+ constructor(semanticEngine = null) {
6
+ this.ruleId = 'C067';
7
+ this.ruleName = 'No Hardcoded Configuration';
8
+ this.description = 'Improve configurability, reduce risk when changing environments, and make configuration management flexible and maintainable.';
9
+ this.semanticEngine = semanticEngine;
10
+ this.verbose = false;
11
+
12
+ // Use symbol-based only for accuracy
13
+ this.symbolAnalyzer = new C067SymbolBasedAnalyzer(semanticEngine);
14
+ }
15
+
16
+ async initialize(semanticEngine = null) {
17
+ if (semanticEngine) {
18
+ this.semanticEngine = semanticEngine;
19
+ }
20
+ this.verbose = semanticEngine?.verbose || false;
21
+ await this.symbolAnalyzer.initialize(semanticEngine);
22
+ }
23
+
24
+ // Main analyze method required by heuristic engine
25
+ async analyze(files, language, options = {}) {
26
+ const violations = [];
27
+
28
+ for (const filePath of files) {
29
+ if (options.verbose) {
30
+ console.log(`[DEBUG] 🎯 C067: Analyzing ${filePath.split('/').pop()}`);
31
+ }
32
+
33
+ try {
34
+ const fileViolations = await this.analyzeFileBasic(filePath, options);
35
+ violations.push(...fileViolations);
36
+ } catch (error) {
37
+ console.warn(`C067: Skipping ${filePath}: ${error.message}`);
38
+ }
39
+ }
40
+
41
+ return violations;
42
+ }
43
+
44
+ async analyzeFileBasic(filePath, options = {}) {
45
+ try {
46
+ // Try semantic engine first, fallback to standalone ts-morph
47
+ if (this.semanticEngine?.isSymbolEngineReady?.() && this.semanticEngine.project) {
48
+ if (this.verbose) {
49
+ console.log(`[DEBUG] 🎯 C067: Using semantic engine for ${filePath.split('/').pop()}`);
50
+ }
51
+
52
+ const violations = await this.symbolAnalyzer.analyzeFileBasic(filePath, options);
53
+
54
+ if (this.verbose) {
55
+ console.log(`[DEBUG] 🎯 C067: Symbol-based analysis found ${violations.length} violations`);
56
+ }
57
+
58
+ return violations;
59
+ } else {
60
+ // Fallback to standalone analysis
61
+ if (this.verbose) {
62
+ console.log(`[DEBUG] 🎯 C067: Using standalone analysis for ${filePath.split('/').pop()}`);
63
+ }
64
+
65
+ const violations = await this.symbolAnalyzer.analyzeFileStandalone(filePath, options);
66
+
67
+ if (this.verbose) {
68
+ console.log(`[DEBUG] 🎯 C067: Standalone analysis found ${violations.length} violations`);
69
+ }
70
+
71
+ return violations;
72
+ }
73
+ } catch (error) {
74
+ if (this.verbose) {
75
+ console.error(`[DEBUG] ❌ C067: Analysis failed: ${error.message}`);
76
+ }
77
+ throw new Error(`C067 analysis failed: ${error.message}`);
78
+ }
79
+ }
80
+
81
+ async analyzeFiles(files, options = {}) {
82
+ const allViolations = [];
83
+ for (const filePath of files) {
84
+ try {
85
+ const violations = await this.analyzeFileBasic(filePath, options);
86
+ allViolations.push(...violations);
87
+ } catch (error) {
88
+ console.warn(`C067: Skipping ${filePath}: ${error.message}`);
89
+ }
90
+ }
91
+ return allViolations;
92
+ }
93
+ }
94
+
95
+ module.exports = C067Analyzer;
@@ -0,0 +1,81 @@
1
+ {
2
+ "maxConfigValues": 50,
3
+ "excludePatterns": [
4
+ "**/*.test.js",
5
+ "**/*.spec.js",
6
+ "**/*.test.ts",
7
+ "**/*.spec.ts",
8
+ "**/config/**",
9
+ "**/constants/**",
10
+ "**/*.config.js",
11
+ "**/*.config.ts",
12
+ "**/.env*"
13
+ ],
14
+ "includePatterns": [
15
+ "**/*.js",
16
+ "**/*.ts",
17
+ "**/*.jsx",
18
+ "**/*.tsx"
19
+ ],
20
+ "allowedDomains": [
21
+ "localhost",
22
+ "127.0.0.1",
23
+ "0.0.0.0",
24
+ "example.com",
25
+ "test.com",
26
+ "dummy.com"
27
+ ],
28
+ "allowedPorts": [
29
+ 3000,
30
+ 8080,
31
+ 5000,
32
+ 4200,
33
+ 3001
34
+ ],
35
+ "commonConstants": [
36
+ 100, 200, 300, 400, 500,
37
+ 1000, 2000, 3000, 5000,
38
+ 8080, 3000, 4200
39
+ ],
40
+ "credentialKeywords": [
41
+ "password",
42
+ "secret",
43
+ "key",
44
+ "token",
45
+ "auth",
46
+ "credential",
47
+ "apikey",
48
+ "api_key"
49
+ ],
50
+ "configKeywords": [
51
+ "timeout",
52
+ "interval",
53
+ "delay",
54
+ "duration",
55
+ "limit",
56
+ "max",
57
+ "min",
58
+ "size",
59
+ "count",
60
+ "port",
61
+ "url",
62
+ "endpoint",
63
+ "host",
64
+ "retry",
65
+ "batch"
66
+ ],
67
+ "allowedUIValues": [
68
+ "password",
69
+ "text",
70
+ "email",
71
+ "username",
72
+ "input",
73
+ "field",
74
+ "ok",
75
+ "cancel",
76
+ "yes",
77
+ "no",
78
+ "true",
79
+ "false"
80
+ ]
81
+ }