@sun-asterisk/sunlint 1.3.4 → 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.
- package/CHANGELOG.md +32 -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/rules/enhanced-rules-registry.json +64 -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 +1000 -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
|
@@ -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,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":
|
|
1949
|
+
"totalRules": 98,
|
|
1893
1950
|
"qualityRules": 33,
|
|
1894
|
-
"securityRules":
|
|
1951
|
+
"securityRules": 50,
|
|
1895
1952
|
"stableRules": 45,
|
|
1896
1953
|
"experimentalRules": 1,
|
|
1897
1954
|
"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
|
+
}
|