@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.
Files changed (32) hide show
  1. package/CHANGELOG.md +62 -0
  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/rule-analysis-strategies.js +5 -0
  12. package/config/rules/enhanced-rules-registry.json +87 -7
  13. package/core/config-preset-resolver.js +7 -2
  14. package/package.json +1 -1
  15. package/rules/common/C067_no_hardcoded_config/analyzer.js +95 -0
  16. package/rules/common/C067_no_hardcoded_config/config.json +81 -0
  17. package/rules/common/C067_no_hardcoded_config/symbol-based-analyzer.js +1034 -0
  18. package/rules/common/C070_no_real_time_tests/analyzer.js +320 -0
  19. package/rules/common/C070_no_real_time_tests/config.json +78 -0
  20. package/rules/common/C070_no_real_time_tests/regex-analyzer.js +424 -0
  21. package/rules/security/S024_xpath_xxe_protection/analyzer.js +242 -0
  22. package/rules/security/S024_xpath_xxe_protection/config.json +152 -0
  23. package/rules/security/S024_xpath_xxe_protection/regex-based-analyzer.js +338 -0
  24. package/rules/security/S024_xpath_xxe_protection/symbol-based-analyzer.js +474 -0
  25. package/rules/security/S025_server_side_validation/README.md +179 -0
  26. package/rules/security/S025_server_side_validation/analyzer.js +242 -0
  27. package/rules/security/S025_server_side_validation/config.json +111 -0
  28. package/rules/security/S025_server_side_validation/regex-based-analyzer.js +388 -0
  29. package/rules/security/S025_server_side_validation/symbol-based-analyzer.js +523 -0
  30. package/scripts/README.md +83 -0
  31. package/scripts/analyze-core-rules.js +151 -0
  32. package/scripts/generate-presets.js +202 -0
@@ -0,0 +1,152 @@
1
+ {
2
+ "rule": {
3
+ "id": "S024",
4
+ "name": "Protect against XPath Injection and XML External Entity (XXE)",
5
+ "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.",
6
+ "category": "security",
7
+ "severity": "error",
8
+ "languages": ["typescript", "javascript"],
9
+ "frameworks": ["express", "nestjs", "node"],
10
+ "version": "1.0.0",
11
+ "status": "stable",
12
+ "tags": ["security", "xpath", "xxe", "xml", "injection", "owasp"],
13
+ "references": [
14
+ "https://owasp.org/www-community/attacks/XPATH_Injection",
15
+ "https://owasp.org/www-community/vulnerabilities/XML_External_Entity_(XXE)_Processing",
16
+ "https://cheatsheetseries.owasp.org/cheatsheets/XML_External_Entity_Prevention_Cheat_Sheet.html",
17
+ "https://portswigger.net/web-security/xpath-injection",
18
+ "https://portswigger.net/web-security/xxe"
19
+ ]
20
+ },
21
+ "configuration": {
22
+ "enableXPathInjectionDetection": true,
23
+ "enableXXEDetection": true,
24
+ "checkUserInputSources": [
25
+ "req",
26
+ "request",
27
+ "params",
28
+ "query",
29
+ "body",
30
+ "headers",
31
+ "cookies"
32
+ ],
33
+ "vulnerableXPathMethods": [
34
+ "evaluate",
35
+ "select",
36
+ "selectText",
37
+ "selectValue",
38
+ "selectNodes",
39
+ "selectSingleNode"
40
+ ],
41
+ "vulnerableXMLMethods": [
42
+ "parseString",
43
+ "parseXml",
44
+ "parseFromString",
45
+ "parse",
46
+ "parseXmlString",
47
+ "transform"
48
+ ],
49
+ "vulnerableXMLConstructors": [
50
+ "DOMParser",
51
+ "XSLTProcessor",
52
+ "SAXParser"
53
+ ],
54
+ "vulnerableXMLLibraries": [
55
+ "xml2js",
56
+ "libxmljs",
57
+ "xmldom",
58
+ "fast-xml-parser",
59
+ "node-xml2js",
60
+ "xml-parser",
61
+ "xmldoc",
62
+ "xpath"
63
+ ],
64
+ "xxeProtectionPatterns": [
65
+ "resolveExternalEntities\\s*:\\s*false",
66
+ "setFeature.*disallow-doctype-decl.*true",
67
+ "setFeature.*external-general-entities.*false",
68
+ "setFeature.*external-parameter-entities.*false",
69
+ "explicitChildren\\s*:\\s*false",
70
+ "ignoreAttrs\\s*:\\s*true",
71
+ "parseDoctype\\s*:\\s*false"
72
+ ],
73
+ "secureXPathPatterns": [
74
+ "parameterized",
75
+ "escaped",
76
+ "sanitized",
77
+ "validate"
78
+ ]
79
+ },
80
+ "examples": {
81
+ "violations": [
82
+ {
83
+ "description": "XPath injection via direct user input",
84
+ "code": "const result = xpath.evaluate(req.query.expression, doc);"
85
+ },
86
+ {
87
+ "description": "XPath injection via string concatenation",
88
+ "code": "const query = \"//user[@name='\" + req.body.username + \"']\";\nconst result = xpath.select(query, doc);"
89
+ },
90
+ {
91
+ "description": "XXE vulnerability in XML parsing",
92
+ "code": "const parser = new DOMParser();\nconst doc = parser.parseFromString(req.body.xml, 'text/xml');"
93
+ },
94
+ {
95
+ "description": "XXE vulnerability with xml2js",
96
+ "code": "xml2js.parseString(req.body.xml, (err, result) => { /* ... */ });"
97
+ }
98
+ ],
99
+ "fixes": [
100
+ {
101
+ "description": "Use parameterized XPath with validation",
102
+ "code": "const sanitizedInput = sanitize(req.query.expression);\nconst result = xpath.evaluate(sanitizedInput, doc);"
103
+ },
104
+ {
105
+ "description": "Disable external entities in XML parsing",
106
+ "code": "const parser = new DOMParser();\nparser.setFeature('http://apache.org/xml/features/disallow-doctype-decl', true);\nparser.setFeature('http://xml.org/sax/features/external-general-entities', false);\nparser.setFeature('http://xml.org/sax/features/external-parameter-entities', false);"
107
+ },
108
+ {
109
+ "description": "Secure xml2js configuration",
110
+ "code": "xml2js.parseString(req.body.xml, {\n explicitChildren: false,\n ignoreAttrs: true\n}, (err, result) => { /* ... */ });"
111
+ }
112
+ ]
113
+ },
114
+ "testing": {
115
+ "testCases": [
116
+ {
117
+ "name": "xpath_injection_direct_input",
118
+ "type": "violation",
119
+ "description": "Direct user input in XPath query"
120
+ },
121
+ {
122
+ "name": "xpath_injection_concatenation",
123
+ "type": "violation",
124
+ "description": "String concatenation with user input"
125
+ },
126
+ {
127
+ "name": "xxe_domparser",
128
+ "type": "violation",
129
+ "description": "DOMParser without XXE protection"
130
+ },
131
+ {
132
+ "name": "xxe_xml2js",
133
+ "type": "violation",
134
+ "description": "xml2js without XXE protection"
135
+ },
136
+ {
137
+ "name": "secure_xpath_parameterized",
138
+ "type": "clean",
139
+ "description": "Parameterized XPath query"
140
+ },
141
+ {
142
+ "name": "secure_xml_xxe_protected",
143
+ "type": "clean",
144
+ "description": "XML parsing with XXE protection"
145
+ }
146
+ ]
147
+ },
148
+ "performance": {
149
+ "complexity": "O(n)",
150
+ "description": "Linear complexity based on number of function calls and expressions in the source code"
151
+ }
152
+ }
@@ -0,0 +1,338 @@
1
+ /**
2
+ * S024 Regex-Based Analyzer - Protect against XPath Injection and XML External Entity (XXE)
3
+ * Fallback analysis using regex patterns for Express.js, NestJS, and TypeScript
4
+ */
5
+
6
+ const fs = require("fs");
7
+
8
+ class S024RegexBasedAnalyzer {
9
+ constructor(semanticEngine = null) {
10
+ this.semanticEngine = semanticEngine;
11
+ this.ruleId = "S024";
12
+ this.category = "security";
13
+
14
+ // XPath injection patterns - user input in XPath queries
15
+ this.xpathInjectionPatterns = [
16
+ // XPath evaluate methods with user input
17
+ /\.evaluate\s*\(\s*[^,]*(?:req\.|request\.|params\.|query\.|body\.)[^,]*\s*,/gi,
18
+ /xpath\s*\.\s*(?:evaluate|select|selectText|selectValue)\s*\(\s*[^,]*(?:req\.|request\.|params\.|query\.|body\.)[^,]*\s*[,)]/gi,
19
+
20
+ // XPath string concatenation with user input
21
+ /['"`][^'"`]*\+\s*(?:req\.|request\.|params\.|query\.|body\.)[^'"`]*['"`]/gi,
22
+ /(?:req\.|request\.|params\.|query\.|body\.)[^+]*\+\s*['"`][^'"`]*['"`]/gi,
23
+
24
+ // XPath libraries with direct user input
25
+ /xpath\s*\(\s*[^,]*(?:req\.|request\.|params\.|query\.|body\.)[^)]*\)/gi,
26
+ /xmldom\s*\.\s*XPath\s*\(\s*[^,]*(?:req\.|request\.|params\.|query\.|body\.)[^)]*\)/gi,
27
+
28
+ // Template literals with user input in XPath
29
+ /`[^`]*\$\{[^}]*(?:req\.|request\.|params\.|query\.|body\.)[^}]*\}[^`]*`/gi
30
+ ];
31
+
32
+ // XXE vulnerability patterns - insecure XML parsing
33
+ this.xxeVulnerabilityPatterns = [
34
+ // XML parsers without XXE protection
35
+ /new\s+DOMParser\s*\(\s*\)/gi,
36
+ /libxmljs\s*\.\s*parseXml\s*\(/gi,
37
+ /xml2js\s*\.\s*parseString\s*\(/gi,
38
+ /xmldom\s*\.\s*DOMParser\s*\(/gi,
39
+ /fast-xml-parser/gi,
40
+
41
+ // XML parsing without entity resolution disabled
42
+ /\.parseFromString\s*\(/gi,
43
+ /XMLHttpRequest\s*\(\s*\)/gi,
44
+
45
+ // XSLT processors without XXE protection
46
+ /new\s+XSLTProcessor\s*\(\s*\)/gi,
47
+ /xslt\s*\.\s*transform\s*\(/gi,
48
+
49
+ // SAX parsers without XXE protection
50
+ /sax\s*\.\s*(?:parser|createStream)\s*\(/gi,
51
+ /new\s+sax\s*\.\s*SAXParser\s*\(/gi
52
+ ];
53
+
54
+ // Secure patterns that should NOT be flagged
55
+ this.securePatterns = [
56
+ // XPath with proper parameterization/escaping
57
+ /xpath\s*\.\s*(?:evaluate|select)\s*\(\s*['"`][^'"`${}]*['"`]\s*,/gi,
58
+ /\.evaluate\s*\(\s*['"`][^'"`${}]*['"`]\s*,/gi,
59
+
60
+ // XML parsers with XXE protection explicitly disabled
61
+ /resolveExternalEntities\s*:\s*false/gi,
62
+ /\.setFeature\s*\(\s*['"`]http:\/\/apache\.org\/xml\/features\/disallow-doctype-decl['"`]\s*,\s*true\s*\)/gi,
63
+ /\.setFeature\s*\(\s*['"`]http:\/\/xml\.org\/sax\/features\/external-general-entities['"`]\s*,\s*false\s*\)/gi,
64
+ /\.setFeature\s*\(\s*['"`]http:\/\/xml\.org\/sax\/features\/external-parameter-entities['"`]\s*,\s*false\s*\)/gi
65
+ ];
66
+
67
+ // XPath functions that are commonly misused
68
+ this.vulnerableXPathFunctions = [
69
+ "evaluate",
70
+ "select",
71
+ "selectText",
72
+ "selectValue",
73
+ "selectNodes",
74
+ "selectSingleNode"
75
+ ];
76
+
77
+ // XML parsing libraries that need XXE protection
78
+ this.vulnerableXMLLibraries = [
79
+ "xml2js",
80
+ "libxmljs",
81
+ "xmldom",
82
+ "fast-xml-parser",
83
+ "node-xml2js",
84
+ "xml-parser",
85
+ "xmldoc",
86
+ "xpath"
87
+ ];
88
+ }
89
+
90
+ async analyze(filePath) {
91
+ if (this.verbose) {
92
+ console.log(`🔍 [${this.ruleId}] Regex-based analysis for: ${filePath}`);
93
+ }
94
+
95
+ try {
96
+ const content = fs.readFileSync(filePath, "utf8");
97
+ return this.analyzeContent(content, filePath);
98
+ } catch (error) {
99
+ if (this.verbose) {
100
+ console.log(
101
+ `🔍 [${this.ruleId}] Regex: Error reading file:`,
102
+ error.message
103
+ );
104
+ }
105
+ return [];
106
+ }
107
+ }
108
+
109
+ analyzeContent(content, filePath) {
110
+ const violations = [];
111
+ const lines = content.split("\n");
112
+
113
+ // Remove comments to avoid false positives
114
+ const cleanContent = this.removeComments(content);
115
+
116
+ try {
117
+ // Pattern 1: XPath Injection vulnerabilities
118
+ violations.push(
119
+ ...this.analyzeXPathInjection(cleanContent, lines, filePath)
120
+ );
121
+
122
+ // Pattern 2: XXE vulnerabilities in XML parsing
123
+ violations.push(
124
+ ...this.analyzeXXEVulnerabilities(cleanContent, lines, filePath)
125
+ );
126
+
127
+ // Pattern 3: Insecure XPath query construction
128
+ violations.push(
129
+ ...this.analyzeInsecureXPathConstruction(cleanContent, lines, filePath)
130
+ );
131
+
132
+ // Pattern 4: XML libraries without XXE protection
133
+ violations.push(
134
+ ...this.analyzeXMLLibraryUsage(cleanContent, lines, filePath)
135
+ );
136
+
137
+ } catch (error) {
138
+ if (this.verbose) {
139
+ console.log(
140
+ `🔍 [${this.ruleId}] Regex: Error in analysis:`,
141
+ error.message
142
+ );
143
+ }
144
+ }
145
+
146
+ return violations;
147
+ }
148
+
149
+ analyzeXPathInjection(content, lines, filePath) {
150
+ const violations = [];
151
+
152
+ // Check for XPath injection patterns
153
+ for (const pattern of this.xpathInjectionPatterns) {
154
+ let match;
155
+ pattern.lastIndex = 0; // Reset regex state
156
+
157
+ while ((match = pattern.exec(content)) !== null) {
158
+ const lineNumber = this.getLineNumber(content, match.index);
159
+ const lineContent = lines[lineNumber - 1];
160
+
161
+ if (this.verbose) {
162
+ console.log(
163
+ `🔍 [${this.ruleId}] Regex: XPath injection pattern found at line ${lineNumber}: ${match[0]}`
164
+ );
165
+ }
166
+
167
+ // Skip if this is a secure pattern
168
+ if (this.isSecureXPathPattern(lineContent)) {
169
+ continue;
170
+ }
171
+
172
+ violations.push({
173
+ rule: this.ruleId,
174
+ source: filePath,
175
+ category: this.category,
176
+ line: lineNumber,
177
+ column: 1,
178
+ message: `XPath Injection vulnerability: User input used directly in XPath query without proper sanitization`,
179
+ severity: "error",
180
+ });
181
+ }
182
+ }
183
+
184
+ return violations;
185
+ }
186
+
187
+ analyzeXXEVulnerabilities(content, lines, filePath) {
188
+ const violations = [];
189
+
190
+ // Check for XXE vulnerability patterns
191
+ for (const pattern of this.xxeVulnerabilityPatterns) {
192
+ let match;
193
+ pattern.lastIndex = 0; // Reset regex state
194
+
195
+ while ((match = pattern.exec(content)) !== null) {
196
+ const lineNumber = this.getLineNumber(content, match.index);
197
+ const lineContent = lines[lineNumber - 1];
198
+ const contextLines = this.getContextLines(lines, lineNumber - 1, 5);
199
+
200
+ if (this.verbose) {
201
+ console.log(
202
+ `🔍 [${this.ruleId}] Regex: XXE vulnerability pattern found at line ${lineNumber}: ${match[0]}`
203
+ );
204
+ }
205
+
206
+ // Skip if XXE protection is already implemented in context
207
+ if (this.hasXXEProtection(contextLines.join('\n'))) {
208
+ continue;
209
+ }
210
+
211
+ violations.push({
212
+ rule: this.ruleId,
213
+ source: filePath,
214
+ category: this.category,
215
+ line: lineNumber,
216
+ column: 1,
217
+ message: `XXE vulnerability: XML parser used without disabling external entity processing`,
218
+ severity: "error",
219
+ });
220
+ }
221
+ }
222
+
223
+ return violations;
224
+ }
225
+
226
+ analyzeInsecureXPathConstruction(content, lines, filePath) {
227
+ const violations = [];
228
+
229
+ // Look for string concatenation or template literals with user input in XPath context
230
+ const xpathContextPattern = /(?:xpath|XPath|XPATH).*(?:\+|`.*\$\{).*(?:req\.|request\.|params\.|query\.|body\.)/gi;
231
+ let match;
232
+
233
+ while ((match = xpathContextPattern.exec(content)) !== null) {
234
+ const lineNumber = this.getLineNumber(content, match.index);
235
+
236
+ if (this.verbose) {
237
+ console.log(
238
+ `🔍 [${this.ruleId}] Regex: Insecure XPath construction at line ${lineNumber}: ${match[0]}`
239
+ );
240
+ }
241
+
242
+ violations.push({
243
+ rule: this.ruleId,
244
+ source: filePath,
245
+ category: this.category,
246
+ line: lineNumber,
247
+ column: 1,
248
+ message: `XPath Injection vulnerability: XPath query constructed using string concatenation with user input`,
249
+ severity: "error",
250
+ });
251
+ }
252
+
253
+ return violations;
254
+ }
255
+
256
+ analyzeXMLLibraryUsage(content, lines, filePath) {
257
+ const violations = [];
258
+
259
+ // Check imports of vulnerable XML libraries
260
+ const importPattern = /(?:import|require)\s*.*(?:xml2js|libxmljs|xmldom|fast-xml-parser|xpath)/gi;
261
+ let match;
262
+
263
+ while ((match = importPattern.exec(content)) !== null) {
264
+ const lineNumber = this.getLineNumber(content, match.index);
265
+ const contextLines = this.getContextLines(lines, lineNumber - 1, 10);
266
+ const contextContent = contextLines.join('\n');
267
+
268
+ if (this.verbose) {
269
+ console.log(
270
+ `🔍 [${this.ruleId}] Regex: XML library import found at line ${lineNumber}: ${match[0]}`
271
+ );
272
+ }
273
+
274
+ // Check if XXE protection is implemented in the context
275
+ if (!this.hasXXEProtection(contextContent)) {
276
+ violations.push({
277
+ rule: this.ruleId,
278
+ source: filePath,
279
+ category: this.category,
280
+ line: lineNumber,
281
+ column: 1,
282
+ message: `XXE vulnerability: XML parsing library imported without implementing XXE protection measures`,
283
+ severity: "warning",
284
+ });
285
+ }
286
+ }
287
+
288
+ return violations;
289
+ }
290
+
291
+ isSecureXPathPattern(lineContent) {
292
+ // Check if the line contains secure XPath patterns
293
+ return this.securePatterns.some(pattern => {
294
+ pattern.lastIndex = 0;
295
+ return pattern.test(lineContent);
296
+ });
297
+ }
298
+
299
+ hasXXEProtection(content) {
300
+ // Check if content contains XXE protection mechanisms
301
+ const protectionPatterns = [
302
+ /resolveExternalEntities\s*:\s*false/i,
303
+ /setFeature.*disallow-doctype-decl.*true/i,
304
+ /setFeature.*external-general-entities.*false/i,
305
+ /setFeature.*external-parameter-entities.*false/i,
306
+ /explicitChildren\s*:\s*false/i,
307
+ /ignoreAttrs\s*:\s*true/i,
308
+ /parseDoctype\s*:\s*false/i,
309
+ /replaceEntities\s*:\s*false/i,
310
+ /recover\s*:\s*false/i
311
+ ];
312
+
313
+ return protectionPatterns.some(pattern => {
314
+ pattern.lastIndex = 0;
315
+ return pattern.test(content);
316
+ });
317
+ }
318
+
319
+ getContextLines(lines, centerIndex, contextSize) {
320
+ const start = Math.max(0, centerIndex - contextSize);
321
+ const end = Math.min(lines.length, centerIndex + contextSize + 1);
322
+ return lines.slice(start, end);
323
+ }
324
+
325
+ removeComments(content) {
326
+ // Remove single-line comments
327
+ content = content.replace(/\/\/.*$/gm, "");
328
+ // Remove multi-line comments
329
+ content = content.replace(/\/\*[\s\S]*?\*\//g, "");
330
+ return content;
331
+ }
332
+
333
+ getLineNumber(content, index) {
334
+ return content.substring(0, index).split("\n").length;
335
+ }
336
+ }
337
+
338
+ module.exports = S024RegexBasedAnalyzer;