@sun-asterisk/sunlint 1.3.4 → 1.3.6
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.
- package/CHANGELOG.md +62 -0
- package/config/presets/all.json +49 -48
- package/config/presets/beginner.json +7 -18
- package/config/presets/ci.json +63 -27
- package/config/presets/maintainability.json +6 -4
- package/config/presets/performance.json +4 -3
- package/config/presets/quality.json +11 -50
- package/config/presets/recommended.json +83 -10
- package/config/presets/security.json +20 -19
- package/config/presets/strict.json +6 -13
- package/config/rule-analysis-strategies.js +5 -0
- package/config/rules/enhanced-rules-registry.json +87 -7
- package/core/config-preset-resolver.js +7 -2
- package/package.json +1 -1
- package/rules/common/C067_no_hardcoded_config/analyzer.js +95 -0
- package/rules/common/C067_no_hardcoded_config/config.json +81 -0
- package/rules/common/C067_no_hardcoded_config/symbol-based-analyzer.js +1034 -0
- package/rules/common/C070_no_real_time_tests/analyzer.js +320 -0
- package/rules/common/C070_no_real_time_tests/config.json +78 -0
- package/rules/common/C070_no_real_time_tests/regex-analyzer.js +424 -0
- package/rules/security/S024_xpath_xxe_protection/analyzer.js +242 -0
- package/rules/security/S024_xpath_xxe_protection/config.json +152 -0
- package/rules/security/S024_xpath_xxe_protection/regex-based-analyzer.js +338 -0
- package/rules/security/S024_xpath_xxe_protection/symbol-based-analyzer.js +474 -0
- package/rules/security/S025_server_side_validation/README.md +179 -0
- package/rules/security/S025_server_side_validation/analyzer.js +242 -0
- package/rules/security/S025_server_side_validation/config.json +111 -0
- package/rules/security/S025_server_side_validation/regex-based-analyzer.js +388 -0
- package/rules/security/S025_server_side_validation/symbol-based-analyzer.js +523 -0
- package/scripts/README.md +83 -0
- package/scripts/analyze-core-rules.js +151 -0
- package/scripts/generate-presets.js +202 -0
|
@@ -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
|
-
"
|
|
39
|
-
"
|
|
40
|
-
"
|
|
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
|
}
|
|
@@ -61,6 +61,11 @@ module.exports = {
|
|
|
61
61
|
methods: ['regex'],
|
|
62
62
|
accuracy: { regex: 90 }
|
|
63
63
|
},
|
|
64
|
+
'C070': {
|
|
65
|
+
reason: 'Real-time dependencies detection via timer/sleep patterns',
|
|
66
|
+
methods: ['regex'],
|
|
67
|
+
accuracy: { regex: 95 }
|
|
68
|
+
},
|
|
64
69
|
'S001': {
|
|
65
70
|
reason: 'Security patterns are often string-based',
|
|
66
71
|
methods: ['regex', 'ast'],
|
|
@@ -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": "
|
|
708
|
-
"description": "
|
|
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": "
|
|
713
|
-
"
|
|
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,51 @@
|
|
|
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
|
+
},
|
|
1360
|
+
"C070": {
|
|
1361
|
+
"name": "No Real Time Tests",
|
|
1362
|
+
"description": "Tests should not depend on real time delays or sleeps. Use fake timers, clock injection, or condition-based waits to improve test reliability and speed.",
|
|
1363
|
+
"category": "testing",
|
|
1364
|
+
"severity": "error",
|
|
1365
|
+
"languages": ["typescript", "javascript"],
|
|
1366
|
+
"analyzer": "../rules/common/C070_no_real_time_tests/regex-analyzer.js",
|
|
1367
|
+
"config": "../rules/common/C070_no_real_time_tests/config.json",
|
|
1368
|
+
"version": "1.0.0",
|
|
1369
|
+
"status": "stable",
|
|
1370
|
+
"tags": ["testing", "flaky-tests", "timing", "fake-timers", "reliability"],
|
|
1371
|
+
"strategy": {
|
|
1372
|
+
"preferred": "ast",
|
|
1373
|
+
"fallbacks": ["regex"],
|
|
1374
|
+
"accuracy": {
|
|
1375
|
+
"ast": 95,
|
|
1376
|
+
"regex": 88
|
|
1377
|
+
}
|
|
1378
|
+
},
|
|
1379
|
+
"engineMappings": {
|
|
1380
|
+
"heuristic": ["../rules/common/C070_no_real_time_tests/regex-analyzer.js"]
|
|
1381
|
+
}
|
|
1382
|
+
},
|
|
1304
1383
|
"C072": {
|
|
1305
1384
|
"id": "C072",
|
|
1306
1385
|
"name": "Single Test Behavior",
|
|
@@ -1771,6 +1850,7 @@
|
|
|
1771
1850
|
"S020",
|
|
1772
1851
|
"S022",
|
|
1773
1852
|
"S023",
|
|
1853
|
+
"S024",
|
|
1774
1854
|
"S025",
|
|
1775
1855
|
"S026",
|
|
1776
1856
|
"S027",
|
|
@@ -1889,9 +1969,9 @@
|
|
|
1889
1969
|
"metadata": {
|
|
1890
1970
|
"version": "1.1.7",
|
|
1891
1971
|
"lastUpdated": "2025-08-25",
|
|
1892
|
-
"totalRules":
|
|
1972
|
+
"totalRules": 98,
|
|
1893
1973
|
"qualityRules": 33,
|
|
1894
|
-
"securityRules":
|
|
1974
|
+
"securityRules": 50,
|
|
1895
1975
|
"stableRules": 45,
|
|
1896
1976
|
"experimentalRules": 1,
|
|
1897
1977
|
"supportedLanguages": 4,
|
|
@@ -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/
|
|
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
|
|
package/package.json
CHANGED
|
@@ -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
|
+
}
|