@sun-asterisk/sunlint 1.3.8 → 1.3.9
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 +25 -0
- package/config/rules/enhanced-rules-registry.json +61 -22
- package/core/file-targeting-service.js +15 -0
- package/package.json +1 -1
- package/rules/security/S020_no_eval_dynamic_code/README.md +136 -0
- package/rules/security/S020_no_eval_dynamic_code/analyzer.js +263 -0
- package/rules/security/S020_no_eval_dynamic_code/config.json +54 -0
- package/rules/security/S020_no_eval_dynamic_code/regex-based-analyzer.js +307 -0
- package/rules/security/S020_no_eval_dynamic_code/symbol-based-analyzer.js +280 -0
- package/rules/security/S024_xpath_xxe_protection/symbol-based-analyzer.js +3 -3
- package/rules/security/S025_server_side_validation/symbol-based-analyzer.js +3 -4
- package/rules/security/S030_directory_browsing_protection/README.md +128 -0
- package/rules/security/S030_directory_browsing_protection/analyzer.js +264 -0
- package/rules/security/S030_directory_browsing_protection/config.json +63 -0
- package/rules/security/S030_directory_browsing_protection/regex-based-analyzer.js +483 -0
- package/rules/security/S030_directory_browsing_protection/symbol-based-analyzer.js +539 -0
- package/rules/security/S033_samesite_session_cookies/symbol-based-analyzer.js +8 -9
- package/rules/security/S039_no_session_tokens_in_url/symbol-based-analyzer.js +33 -26
- package/rules/security/S056_log_injection_protection/analyzer.js +2 -2
- package/rules/security/S056_log_injection_protection/symbol-based-analyzer.js +77 -118
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
# S030 - Disable Directory Browsing and Protect Sensitive Metadata Files
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
This rule detects and prevents directory browsing vulnerabilities and exposure of sensitive metadata files that could lead to information disclosure.
|
|
6
|
+
|
|
7
|
+
## Security Concerns
|
|
8
|
+
|
|
9
|
+
### Directory Browsing
|
|
10
|
+
|
|
11
|
+
- **Information Disclosure**: Directory listing can reveal application structure, file names, and potentially sensitive information
|
|
12
|
+
- **Attack Surface**: Exposed directories provide attackers with reconnaissance information
|
|
13
|
+
- **Compliance**: Many security standards require disabling directory browsing
|
|
14
|
+
|
|
15
|
+
### Sensitive Metadata Files
|
|
16
|
+
|
|
17
|
+
- **Version Control**: `.git/`, `.svn/`, `.hg/` directories contain source code history
|
|
18
|
+
- **Configuration Files**: `.env`, `config.json`, `settings.yml` may contain secrets
|
|
19
|
+
- **Backup Files**: Database dumps, configuration backups can expose sensitive data
|
|
20
|
+
- **Development Files**: IDE settings, temporary files may contain sensitive information
|
|
21
|
+
|
|
22
|
+
## Detection Patterns
|
|
23
|
+
|
|
24
|
+
### ❌ Violations
|
|
25
|
+
|
|
26
|
+
```javascript
|
|
27
|
+
// Express.js - Directory browsing enabled
|
|
28
|
+
app.use(express.static("public", { index: false, dotfiles: "allow" }));
|
|
29
|
+
app.use("/files", serveIndex("uploads")); // Directory listing middleware
|
|
30
|
+
|
|
31
|
+
// Serving sensitive directories
|
|
32
|
+
app.use("/backup", express.static("./backups/"));
|
|
33
|
+
app.use("/.git", express.static("./.git/"));
|
|
34
|
+
|
|
35
|
+
// Missing protection for sensitive files
|
|
36
|
+
app.get("/.env", (req, res) => {
|
|
37
|
+
res.sendFile(".env");
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
// Koa.js - Unsafe static serving
|
|
41
|
+
app.use(koaStatic("./public", { hidden: true }));
|
|
42
|
+
|
|
43
|
+
// Fastify - Directory browsing
|
|
44
|
+
fastify.register(require("@fastify/static"), {
|
|
45
|
+
root: path.join(__dirname, "public"),
|
|
46
|
+
list: true, // Enables directory listing
|
|
47
|
+
});
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### ✅ Safe Alternatives
|
|
51
|
+
|
|
52
|
+
```javascript
|
|
53
|
+
// Express.js - Secure static file serving
|
|
54
|
+
app.use(
|
|
55
|
+
express.static("public", {
|
|
56
|
+
index: ["index.html", "index.htm"],
|
|
57
|
+
dotfiles: "deny", // Block access to dotfiles
|
|
58
|
+
redirect: false,
|
|
59
|
+
})
|
|
60
|
+
);
|
|
61
|
+
|
|
62
|
+
// Explicit file serving with validation
|
|
63
|
+
app.get("/files/:filename", (req, res) => {
|
|
64
|
+
const filename = path.basename(req.params.filename);
|
|
65
|
+
const safePath = path.join(SAFE_DIRECTORY, filename);
|
|
66
|
+
|
|
67
|
+
// Validate file exists and is safe
|
|
68
|
+
if (fs.existsSync(safePath) && isAllowedFile(filename)) {
|
|
69
|
+
res.sendFile(safePath);
|
|
70
|
+
} else {
|
|
71
|
+
res.status(404).send("File not found");
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
// Koa.js - Secure configuration
|
|
76
|
+
app.use(
|
|
77
|
+
koaStatic("./public", {
|
|
78
|
+
hidden: false, // Don't serve hidden files
|
|
79
|
+
index: "index.html",
|
|
80
|
+
gzip: true,
|
|
81
|
+
})
|
|
82
|
+
);
|
|
83
|
+
|
|
84
|
+
// Fastify - Disable directory listing
|
|
85
|
+
fastify.register(require("@fastify/static"), {
|
|
86
|
+
root: path.join(__dirname, "public"),
|
|
87
|
+
list: false, // Disable directory listing
|
|
88
|
+
wildcard: false,
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
// Nginx-style protection in configuration
|
|
92
|
+
const protectedPaths = [".env", ".git", "config/", "backup/"];
|
|
93
|
+
app.use((req, res, next) => {
|
|
94
|
+
const isProtected = protectedPaths.some((path) => req.url.includes(path));
|
|
95
|
+
|
|
96
|
+
if (isProtected) {
|
|
97
|
+
return res.status(403).send("Access denied");
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
next();
|
|
101
|
+
});
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
## Configuration Options
|
|
105
|
+
|
|
106
|
+
- **Severity**: Error (high security impact)
|
|
107
|
+
- **Categories**: Security, Information Disclosure
|
|
108
|
+
- **Frameworks**: Express.js, Koa.js, Fastify, Hapi.js
|
|
109
|
+
- **File Types**: JavaScript, TypeScript, Configuration files
|
|
110
|
+
|
|
111
|
+
## Implementation Notes
|
|
112
|
+
|
|
113
|
+
1. **Symbol-based Analysis**: Detects static file serving configurations and middleware usage
|
|
114
|
+
2. **Regex-based Fallback**: Identifies patterns in configuration files and string literals
|
|
115
|
+
3. **Framework Detection**: Supports multiple Node.js web frameworks
|
|
116
|
+
4. **Path Validation**: Checks for exposure of sensitive directories and files
|
|
117
|
+
|
|
118
|
+
## Related Security Rules
|
|
119
|
+
|
|
120
|
+
- S027: No hardcoded secrets
|
|
121
|
+
- S038: No version headers
|
|
122
|
+
- S055: Content type validation
|
|
123
|
+
|
|
124
|
+
## References
|
|
125
|
+
|
|
126
|
+
- [OWASP: Information Exposure](https://owasp.org/www-community/vulnerabilities/Information_exposure)
|
|
127
|
+
- [CWE-548: Exposure of Information Through Directory Listing](https://cwe.mitre.org/data/definitions/548.html)
|
|
128
|
+
- [Express.js Security Best Practices](https://expressjs.com/en/advanced/best-practice-security.html)
|
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* S030 Main Analyzer - Disable directory browsing and protect sensitive metadata files
|
|
3
|
+
* Primary: Symbol-based analysis (when available)
|
|
4
|
+
* Fallback: Regex-based for all other cases
|
|
5
|
+
* Command: node cli.js --rule=S030 --input=examples/rule-test-fixtures/rules/S030_directory_browsing_protection --engine=heuristic
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const S030SymbolBasedAnalyzer = require("./symbol-based-analyzer.js");
|
|
9
|
+
const S030RegexBasedAnalyzer = require("./regex-based-analyzer.js");
|
|
10
|
+
|
|
11
|
+
class S030Analyzer {
|
|
12
|
+
constructor(options = {}) {
|
|
13
|
+
if (process.env.SUNLINT_DEBUG) {
|
|
14
|
+
console.log(`🔧 [S030] Constructor called with options:`, !!options);
|
|
15
|
+
console.log(
|
|
16
|
+
`🔧 [S030] Options type:`,
|
|
17
|
+
typeof options,
|
|
18
|
+
Object.keys(options || {})
|
|
19
|
+
);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
this.ruleId = "S030";
|
|
23
|
+
this.ruleName =
|
|
24
|
+
"Disable directory browsing and protect sensitive metadata files";
|
|
25
|
+
this.description =
|
|
26
|
+
"Disable directory browsing and protect sensitive metadata files (.git/, .env, config files, etc.) to prevent information disclosure and potential security vulnerabilities.";
|
|
27
|
+
this.semanticEngine = options.semanticEngine || null;
|
|
28
|
+
this.verbose = options.verbose || false;
|
|
29
|
+
|
|
30
|
+
this.config = {
|
|
31
|
+
useSymbolBased: true,
|
|
32
|
+
fallbackToRegex: true,
|
|
33
|
+
regexBasedOnly: false,
|
|
34
|
+
prioritizeSymbolic: true, // Prefer symbol-based when available
|
|
35
|
+
fallbackToSymbol: true, // Allow symbol analysis even without semantic engine
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
try {
|
|
39
|
+
this.symbolAnalyzer = new S030SymbolBasedAnalyzer(this.semanticEngine);
|
|
40
|
+
if (process.env.SUNLINT_DEBUG)
|
|
41
|
+
console.log(`🔧 [S030] Symbol analyzer created successfully`);
|
|
42
|
+
} catch (error) {
|
|
43
|
+
console.error(`🔧 [S030] Error creating symbol analyzer:`, error);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
try {
|
|
47
|
+
this.regexAnalyzer = new S030RegexBasedAnalyzer(this.semanticEngine);
|
|
48
|
+
if (process.env.SUNLINT_DEBUG)
|
|
49
|
+
console.log(`🔧 [S030] Regex analyzer created successfully`);
|
|
50
|
+
} catch (error) {
|
|
51
|
+
console.error(`🔧 [S030] Error creating regex analyzer:`, error);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
async initialize(semanticEngine) {
|
|
56
|
+
this.semanticEngine = semanticEngine;
|
|
57
|
+
if (process.env.SUNLINT_DEBUG)
|
|
58
|
+
console.log(`🔧 [S030] Main analyzer initializing...`);
|
|
59
|
+
|
|
60
|
+
if (this.symbolAnalyzer)
|
|
61
|
+
await this.symbolAnalyzer.initialize?.(semanticEngine);
|
|
62
|
+
if (this.regexAnalyzer)
|
|
63
|
+
await this.regexAnalyzer.initialize?.(semanticEngine);
|
|
64
|
+
if (this.regexAnalyzer) this.regexAnalyzer.cleanup?.();
|
|
65
|
+
|
|
66
|
+
if (process.env.SUNLINT_DEBUG)
|
|
67
|
+
console.log(`🔧 [S030] Main analyzer initialized successfully`);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
analyzeSingle(filePath, options = {}) {
|
|
71
|
+
if (process.env.SUNLINT_DEBUG)
|
|
72
|
+
console.log(`🔍 [S030] analyzeSingle() called for: ${filePath}`);
|
|
73
|
+
return this.analyze([filePath], "typescript", options);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
async analyze(files, language, options = {}) {
|
|
77
|
+
if (process.env.SUNLINT_DEBUG) {
|
|
78
|
+
console.log(
|
|
79
|
+
`🔧 [S030] analyze() method called with ${files.length} files, language: ${language}`
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const violations = [];
|
|
84
|
+
for (const filePath of files) {
|
|
85
|
+
try {
|
|
86
|
+
if (process.env.SUNLINT_DEBUG)
|
|
87
|
+
console.log(`🔧 [S030] Processing file: ${filePath}`);
|
|
88
|
+
const fileViolations = await this.analyzeFile(filePath, options);
|
|
89
|
+
violations.push(...fileViolations);
|
|
90
|
+
if (process.env.SUNLINT_DEBUG)
|
|
91
|
+
console.log(
|
|
92
|
+
`🔧 [S030] File ${filePath}: Found ${fileViolations.length} violations`
|
|
93
|
+
);
|
|
94
|
+
} catch (error) {
|
|
95
|
+
console.warn(
|
|
96
|
+
`⚠ [S030] Analysis failed for ${filePath}:`,
|
|
97
|
+
error.message
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
if (process.env.SUNLINT_DEBUG)
|
|
103
|
+
console.log(`🔧 [S030] Total violations found: ${violations.length}`);
|
|
104
|
+
return violations;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
async analyzeFile(filePath, options = {}) {
|
|
108
|
+
if (process.env.SUNLINT_DEBUG)
|
|
109
|
+
console.log(`🔍 [S030] analyzeFile() called for: ${filePath}`);
|
|
110
|
+
const violationMap = new Map();
|
|
111
|
+
|
|
112
|
+
// Try symbol-based analysis first when semantic engine is available OR when explicitly enabled
|
|
113
|
+
if (process.env.SUNLINT_DEBUG) {
|
|
114
|
+
console.log(
|
|
115
|
+
`🔧 [S030] Symbol check: useSymbolBased=${
|
|
116
|
+
this.config.useSymbolBased
|
|
117
|
+
}, semanticEngine=${!!this.semanticEngine}, project=${!!this
|
|
118
|
+
.semanticEngine?.project}, initialized=${!!this.semanticEngine
|
|
119
|
+
?.initialized}`
|
|
120
|
+
);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const canUseSymbol =
|
|
124
|
+
this.config.useSymbolBased &&
|
|
125
|
+
((this.semanticEngine?.project && this.semanticEngine?.initialized) ||
|
|
126
|
+
(!this.semanticEngine && this.config.fallbackToSymbol !== false));
|
|
127
|
+
|
|
128
|
+
if (canUseSymbol) {
|
|
129
|
+
try {
|
|
130
|
+
if (process.env.SUNLINT_DEBUG)
|
|
131
|
+
console.log(`🔧 [S030] Trying symbol-based analysis...`);
|
|
132
|
+
|
|
133
|
+
let sourceFile = null;
|
|
134
|
+
if (this.semanticEngine?.project) {
|
|
135
|
+
sourceFile = this.semanticEngine.project.getSourceFile(filePath);
|
|
136
|
+
if (process.env.SUNLINT_DEBUG) {
|
|
137
|
+
console.log(
|
|
138
|
+
`🔧 [S030] Checked existing semantic engine project: sourceFile=${!!sourceFile}`
|
|
139
|
+
);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
if (!sourceFile) {
|
|
144
|
+
// Create a minimal ts-morph project for this analysis
|
|
145
|
+
if (process.env.SUNLINT_DEBUG)
|
|
146
|
+
console.log(
|
|
147
|
+
`🔧 [S030] Creating temporary ts-morph project for: ${filePath}`
|
|
148
|
+
);
|
|
149
|
+
try {
|
|
150
|
+
const fs = require("fs");
|
|
151
|
+
const path = require("path");
|
|
152
|
+
const { Project } = require("ts-morph");
|
|
153
|
+
|
|
154
|
+
// Check if file exists and read content
|
|
155
|
+
if (!fs.existsSync(filePath)) {
|
|
156
|
+
throw new Error(`File not found: ${filePath}`);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
const fileContent = fs.readFileSync(filePath, "utf8");
|
|
160
|
+
const fileName = path.basename(filePath);
|
|
161
|
+
|
|
162
|
+
const tempProject = new Project({
|
|
163
|
+
useInMemoryFileSystem: true,
|
|
164
|
+
compilerOptions: {
|
|
165
|
+
allowJs: true,
|
|
166
|
+
allowSyntheticDefaultImports: true,
|
|
167
|
+
},
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
// Add file content to in-memory project
|
|
171
|
+
sourceFile = tempProject.createSourceFile(fileName, fileContent);
|
|
172
|
+
if (process.env.SUNLINT_DEBUG)
|
|
173
|
+
console.log(
|
|
174
|
+
`🔧 [S030] Temporary project created successfully with file: ${fileName}`
|
|
175
|
+
);
|
|
176
|
+
} catch (projectError) {
|
|
177
|
+
if (process.env.SUNLINT_DEBUG)
|
|
178
|
+
console.log(
|
|
179
|
+
`🔧 [S030] Failed to create temporary project:`,
|
|
180
|
+
projectError.message
|
|
181
|
+
);
|
|
182
|
+
throw projectError;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
if (sourceFile) {
|
|
187
|
+
const symbolViolations = await this.symbolAnalyzer.analyze(
|
|
188
|
+
sourceFile,
|
|
189
|
+
filePath
|
|
190
|
+
);
|
|
191
|
+
symbolViolations.forEach((v) => {
|
|
192
|
+
const key = `${v.line}:${v.column}:${v.message}`;
|
|
193
|
+
if (!violationMap.has(key)) violationMap.set(key, v);
|
|
194
|
+
});
|
|
195
|
+
if (process.env.SUNLINT_DEBUG)
|
|
196
|
+
console.log(
|
|
197
|
+
`🔧 [S030] Symbol analysis completed: ${symbolViolations.length} violations`
|
|
198
|
+
);
|
|
199
|
+
|
|
200
|
+
// If symbol-based found violations AND prioritizeSymbolic is true, skip regex
|
|
201
|
+
// But still run regex if symbol-based didn't find any violations
|
|
202
|
+
if (this.config.prioritizeSymbolic && symbolViolations.length > 0) {
|
|
203
|
+
const finalViolations = Array.from(violationMap.values()).map(
|
|
204
|
+
(v) => ({
|
|
205
|
+
...v,
|
|
206
|
+
filePath,
|
|
207
|
+
file: filePath,
|
|
208
|
+
})
|
|
209
|
+
);
|
|
210
|
+
if (process.env.SUNLINT_DEBUG)
|
|
211
|
+
console.log(
|
|
212
|
+
`🔧 [S030] Symbol-based analysis prioritized: ${finalViolations.length} violations`
|
|
213
|
+
);
|
|
214
|
+
return finalViolations;
|
|
215
|
+
}
|
|
216
|
+
} else {
|
|
217
|
+
if (process.env.SUNLINT_DEBUG)
|
|
218
|
+
console.log(
|
|
219
|
+
`🔧 [S030] No source file found, skipping symbol analysis`
|
|
220
|
+
);
|
|
221
|
+
}
|
|
222
|
+
} catch (error) {
|
|
223
|
+
console.warn(`⚠ [S030] Symbol analysis failed:`, error.message);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
// Fallback to regex-based analysis
|
|
228
|
+
if (this.config.fallbackToRegex || this.config.regexBasedOnly) {
|
|
229
|
+
try {
|
|
230
|
+
if (process.env.SUNLINT_DEBUG)
|
|
231
|
+
console.log(`🔧 [S030] Trying regex-based analysis...`);
|
|
232
|
+
const regexViolations = await this.regexAnalyzer.analyze(filePath);
|
|
233
|
+
regexViolations.forEach((v) => {
|
|
234
|
+
const key = `${v.line}:${v.column}:${v.message}`;
|
|
235
|
+
if (!violationMap.has(key)) violationMap.set(key, v);
|
|
236
|
+
});
|
|
237
|
+
if (process.env.SUNLINT_DEBUG)
|
|
238
|
+
console.log(
|
|
239
|
+
`🔧 [S030] Regex analysis completed: ${regexViolations.length} violations`
|
|
240
|
+
);
|
|
241
|
+
} catch (error) {
|
|
242
|
+
console.warn(`⚠ [S030] Regex analysis failed:`, error.message);
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
const finalViolations = Array.from(violationMap.values()).map((v) => ({
|
|
247
|
+
...v,
|
|
248
|
+
filePath,
|
|
249
|
+
file: filePath,
|
|
250
|
+
}));
|
|
251
|
+
if (process.env.SUNLINT_DEBUG)
|
|
252
|
+
console.log(
|
|
253
|
+
`🔧 [S030] File analysis completed: ${finalViolations.length} unique violations`
|
|
254
|
+
);
|
|
255
|
+
return finalViolations;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
cleanup() {
|
|
259
|
+
if (this.symbolAnalyzer?.cleanup) this.symbolAnalyzer.cleanup();
|
|
260
|
+
if (this.regexAnalyzer?.cleanup) this.regexAnalyzer.cleanup();
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
module.exports = S030Analyzer;
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "S030",
|
|
3
|
+
"name": "Disable directory browsing and protect sensitive metadata files",
|
|
4
|
+
"category": "security",
|
|
5
|
+
"description": "S030 - Disable directory browsing and protect sensitive metadata files (.git/, .env, config files, etc.) to prevent information disclosure and potential security vulnerabilities.",
|
|
6
|
+
"severity": "error",
|
|
7
|
+
"enabled": true,
|
|
8
|
+
"semantic": {
|
|
9
|
+
"enabled": true,
|
|
10
|
+
"priority": "high",
|
|
11
|
+
"fallback": "heuristic"
|
|
12
|
+
},
|
|
13
|
+
"patterns": {
|
|
14
|
+
"include": [
|
|
15
|
+
"**/*.js",
|
|
16
|
+
"**/*.ts",
|
|
17
|
+
"**/*.jsx",
|
|
18
|
+
"**/*.tsx",
|
|
19
|
+
"**/*.json",
|
|
20
|
+
"**/*.yaml",
|
|
21
|
+
"**/*.yml",
|
|
22
|
+
"**/.*"
|
|
23
|
+
],
|
|
24
|
+
"exclude": [
|
|
25
|
+
"**/*.test.js",
|
|
26
|
+
"**/*.test.ts",
|
|
27
|
+
"**/*.spec.js",
|
|
28
|
+
"**/*.spec.ts",
|
|
29
|
+
"**/node_modules/**",
|
|
30
|
+
"**/dist/**",
|
|
31
|
+
"**/build/**"
|
|
32
|
+
]
|
|
33
|
+
},
|
|
34
|
+
"analysis": {
|
|
35
|
+
"approach": "symbol-based-primary",
|
|
36
|
+
"fallback": "regex-based",
|
|
37
|
+
"depth": 2,
|
|
38
|
+
"timeout": 5000
|
|
39
|
+
},
|
|
40
|
+
"validation": {
|
|
41
|
+
"serverConfigs": ["express", "koa", "fastify", "hapi"],
|
|
42
|
+
"sensitiveFiles": [
|
|
43
|
+
".env",
|
|
44
|
+
".git",
|
|
45
|
+
".svn",
|
|
46
|
+
".hg",
|
|
47
|
+
"config",
|
|
48
|
+
"settings",
|
|
49
|
+
"secrets",
|
|
50
|
+
"keys",
|
|
51
|
+
"backup",
|
|
52
|
+
"database"
|
|
53
|
+
],
|
|
54
|
+
"directoryListingIndicators": [
|
|
55
|
+
"autoIndex",
|
|
56
|
+
"directory",
|
|
57
|
+
"listing",
|
|
58
|
+
"browse",
|
|
59
|
+
"index"
|
|
60
|
+
],
|
|
61
|
+
"protectionMethods": ["serveStatic", "static", "staticFiles", "public"]
|
|
62
|
+
}
|
|
63
|
+
}
|