@sun-asterisk/sunlint 1.3.16 → 1.3.17
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/config/rule-analysis-strategies.js +3 -3
- package/config/rules/enhanced-rules-registry.json +40 -20
- package/core/cli-action-handler.js +2 -2
- package/core/config-merger.js +28 -6
- package/core/constants/defaults.js +1 -1
- package/core/file-targeting-service.js +72 -4
- package/core/output-service.js +21 -4
- package/engines/heuristic-engine.js +5 -0
- package/package.json +1 -1
- package/rules/common/C002_no_duplicate_code/README.md +115 -0
- package/rules/common/C002_no_duplicate_code/analyzer.js +615 -219
- package/rules/common/C002_no_duplicate_code/test-cases/api-handlers.ts +64 -0
- package/rules/common/C002_no_duplicate_code/test-cases/data-processor.ts +46 -0
- package/rules/common/C002_no_duplicate_code/test-cases/good-example.tsx +40 -0
- package/rules/common/C002_no_duplicate_code/test-cases/product-service.ts +57 -0
- package/rules/common/C002_no_duplicate_code/test-cases/user-service.ts +49 -0
- package/rules/common/C008/analyzer.js +40 -0
- package/rules/common/C008/config.json +20 -0
- package/rules/common/C008/ts-morph-analyzer.js +1067 -0
- package/rules/common/C018_no_throw_generic_error/analyzer.js +1 -1
- package/rules/common/C018_no_throw_generic_error/symbol-based-analyzer.js +27 -3
- package/rules/common/C024_no_scatter_hardcoded_constants/symbol-based-analyzer.js +504 -162
- package/rules/common/C029_catch_block_logging/analyzer.js +499 -89
- package/rules/common/C033_separate_service_repository/README.md +131 -20
- package/rules/common/C033_separate_service_repository/analyzer.js +1 -1
- package/rules/common/C033_separate_service_repository/symbol-based-analyzer.js +417 -274
- package/rules/common/C041_no_sensitive_hardcode/analyzer.js +144 -254
- package/rules/common/C041_no_sensitive_hardcode/config.json +50 -0
- package/rules/common/C041_no_sensitive_hardcode/symbol-based-analyzer.js +575 -0
- package/rules/common/C067_no_hardcoded_config/analyzer.js +17 -16
- package/rules/common/C067_no_hardcoded_config/symbol-based-analyzer.js +3477 -659
- package/rules/docs/C002_no_duplicate_code.md +276 -11
- package/rules/index.js +5 -1
- package/rules/security/S006_no_plaintext_recovery_codes/analyzer.js +266 -88
- package/rules/security/S006_no_plaintext_recovery_codes/symbol-based-analyzer.js +805 -0
- package/rules/security/S010_no_insecure_encryption/README.md +78 -0
- package/rules/security/S010_no_insecure_encryption/analyzer.js +463 -398
- package/rules/security/S013_tls_enforcement/README.md +51 -0
- package/rules/security/S013_tls_enforcement/analyzer.js +99 -0
- package/rules/security/S013_tls_enforcement/config.json +41 -0
- package/rules/security/S013_tls_enforcement/symbol-based-analyzer.js +339 -0
- package/rules/security/S014_tls_version_enforcement/README.md +354 -0
- package/rules/security/S014_tls_version_enforcement/analyzer.js +118 -0
- package/rules/security/S014_tls_version_enforcement/config.json +56 -0
- package/rules/security/S014_tls_version_enforcement/symbol-based-analyzer.js +194 -0
- package/rules/security/S055_content_type_validation/analyzer.js +121 -279
- package/rules/security/S055_content_type_validation/symbol-based-analyzer.js +346 -0
- package/rules/tests/C002_no_duplicate_code.test.js +111 -22
- package/rules/common/C029_catch_block_logging/analyzer-smart-pipeline.js +0 -755
- package/rules/common/C041_no_sensitive_hardcode/ast-analyzer.js +0 -296
|
@@ -1,311 +1,153 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* S055 Main Analyzer - Content-Type Validation in REST Services
|
|
3
|
+
* Primary: Symbol-based analysis (when available)
|
|
4
|
+
* Fallback: Regex-based for all other cases
|
|
3
5
|
* Purpose: Detect REST endpoints that process request body without validating Content-Type
|
|
4
|
-
*
|
|
6
|
+
* Command: node cli.js --rule=S055 --input=examples/rule-test-fixtures/rules/S055_content_type_validation --engine=heuristic --verbose
|
|
5
7
|
*/
|
|
6
8
|
|
|
9
|
+
const S055SymbolBasedAnalyzer = require("./symbol-based-analyzer.js");
|
|
10
|
+
|
|
7
11
|
class S055Analyzer {
|
|
8
|
-
constructor() {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
// Patterns that indicate request body usage
|
|
19
|
-
this.requestBodyPatterns = [
|
|
20
|
-
// Express.js patterns
|
|
21
|
-
/req\.body/i,
|
|
22
|
-
/request\.body/i,
|
|
23
|
-
|
|
24
|
-
// NestJS patterns
|
|
25
|
-
/@Body\(\)/i,
|
|
26
|
-
/@Body\([^)]*\)/i,
|
|
27
|
-
|
|
28
|
-
// Generic body access patterns
|
|
29
|
-
/\.body\s*[;\.,\]\}]/i,
|
|
30
|
-
/body\s*[:=]/i,
|
|
31
|
-
];
|
|
32
|
-
|
|
33
|
-
// Patterns that indicate Content-Type validation
|
|
34
|
-
this.contentTypeValidationPatterns = [
|
|
35
|
-
// Express.js validation methods
|
|
36
|
-
/req\.is\s*\(\s*['"`][^'"`]*application\/[^'"`]*['"`]\s*\)/i,
|
|
37
|
-
/request\.is\s*\(\s*['"`][^'"`]*application\/[^'"`]*['"`]\s*\)/i,
|
|
38
|
-
|
|
39
|
-
// Direct header checks
|
|
40
|
-
/req\.headers\s*\[\s*['"`]content-type['"`]\s*\]/i,
|
|
41
|
-
/request\.headers\s*\[\s*['"`]content-type['"`]\s*\]/i,
|
|
42
|
-
/req\.get\s*\(\s*['"`]content-type['"`]\s*\)/i,
|
|
43
|
-
/request\.get\s*\(\s*['"`]content-type['"`]\s*\)/i,
|
|
44
|
-
|
|
45
|
-
// Content-Type comparison
|
|
46
|
-
/content-type\s*[=!]==?\s*['"`]application\//i,
|
|
47
|
-
/['"`]application\/[^'"`]*['"`]\s*[=!]==?\s*.*content-type/i,
|
|
48
|
-
|
|
49
|
-
// Middleware patterns
|
|
50
|
-
/express\.json\s*\(/i,
|
|
51
|
-
/bodyParser\.json\s*\(/i,
|
|
52
|
-
/app\.use\s*\([^)]*json[^)]*\)/i,
|
|
53
|
-
|
|
54
|
-
// NestJS decorators
|
|
55
|
-
/@Header\s*\(\s*['"`]Content-Type['"`]/i,
|
|
56
|
-
/@UseInterceptors\s*\([^)]*ContentType[^)]*\)/i,
|
|
57
|
-
|
|
58
|
-
// Custom validation functions
|
|
59
|
-
/validateContentType/i,
|
|
60
|
-
/checkContentType/i,
|
|
61
|
-
/verifyContentType/i,
|
|
62
|
-
];
|
|
63
|
-
|
|
64
|
-
// Patterns that indicate HTTP method handlers
|
|
65
|
-
this.httpHandlerPatterns = [
|
|
66
|
-
// Express.js route definitions
|
|
67
|
-
/app\.(post|put|patch|delete)\s*\(/i,
|
|
68
|
-
/router\.(post|put|patch|delete)\s*\(/i,
|
|
69
|
-
/express\(\)\.(post|put|patch|delete)\s*\(/i,
|
|
70
|
-
|
|
71
|
-
// NestJS decorators
|
|
72
|
-
/@(Post|Put|Patch|Delete)\s*\(/i,
|
|
73
|
-
|
|
74
|
-
// Generic handler patterns
|
|
75
|
-
/(post|put|patch|delete)\s*:\s*(async\s+)?function/i,
|
|
76
|
-
/(post|put|patch|delete)\s*:\s*\(/i,
|
|
77
|
-
|
|
78
|
-
// Function names indicating HTTP handlers
|
|
79
|
-
/function\s+(handle|process)?(Post|Put|Patch|Delete)/i,
|
|
80
|
-
/const\s+\w*(post|put|patch|delete)\w*\s*=/i,
|
|
81
|
-
/let\s+\w*(post|put|patch|delete)\w*\s*=/i,
|
|
82
|
-
];
|
|
83
|
-
|
|
84
|
-
// Safe patterns to exclude from violations
|
|
85
|
-
this.safePatterns = [
|
|
86
|
-
// Comments and documentation
|
|
87
|
-
/\/\/|\/\*|\*\/|@param|@return|@example/,
|
|
88
|
-
|
|
89
|
-
// Import/export statements
|
|
90
|
-
/import|export|require|module\.exports/i,
|
|
91
|
-
|
|
92
|
-
// Type definitions
|
|
93
|
-
/interface|type|enum|declare/i,
|
|
94
|
-
|
|
95
|
-
// Test files patterns
|
|
96
|
-
/describe\s*\(|it\s*\(|test\s*\(|expect\s*\(/i,
|
|
97
|
-
|
|
98
|
-
// Configuration and constants
|
|
99
|
-
/const\s+\w+\s*=\s*['"`]/i,
|
|
100
|
-
|
|
101
|
-
// Logging and debugging
|
|
102
|
-
/console\.|logger\.|log\(/i,
|
|
103
|
-
|
|
104
|
-
// Middleware already handling Content-Type
|
|
105
|
-
/express\.json|bodyParser\.json|multer\(/i,
|
|
106
|
-
];
|
|
107
|
-
|
|
108
|
-
// Patterns indicating secure implementations
|
|
109
|
-
this.secureImplementationPatterns = [
|
|
110
|
-
// Middleware usage that handles Content-Type
|
|
111
|
-
/app\.use\s*\([^)]*express\.json[^)]*\)/i,
|
|
112
|
-
/app\.use\s*\([^)]*bodyParser\.json[^)]*\)/i,
|
|
113
|
-
|
|
114
|
-
// Global Content-Type validation
|
|
115
|
-
/app\.use\s*\([^)]*validateContentType[^)]*\)/i,
|
|
116
|
-
/app\.use\s*\([^)]*checkContentType[^)]*\)/i,
|
|
117
|
-
];
|
|
118
|
-
}
|
|
12
|
+
constructor(options = {}) {
|
|
13
|
+
if (process.env.SUNLINT_DEBUG) {
|
|
14
|
+
console.log(`🔧 [S055] Constructor called with options:`, !!options);
|
|
15
|
+
console.log(
|
|
16
|
+
`🔧 [S055] Options type:`,
|
|
17
|
+
typeof options,
|
|
18
|
+
Object.keys(options || {})
|
|
19
|
+
);
|
|
20
|
+
}
|
|
119
21
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
22
|
+
this.ruleId = "S055";
|
|
23
|
+
this.ruleName = "Do not expose version information in response headers";
|
|
24
|
+
this.description =
|
|
25
|
+
"Detect REST endpoints that process request body without validating Content-Type";
|
|
26
|
+
this.semanticEngine = options.semanticEngine || null;
|
|
27
|
+
this.verbose = options.verbose || false;
|
|
28
|
+
|
|
29
|
+
this.config = {
|
|
30
|
+
useSymbolBased: true,
|
|
31
|
+
fallbackToRegex: false,
|
|
32
|
+
regexBasedOnly: false,
|
|
33
|
+
prioritizeSymbolic: true, // Prefer symbol-based when available
|
|
34
|
+
fallbackToSymbol: true, // Allow symbol analysis even without semantic engine
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
try {
|
|
38
|
+
this.symbolAnalyzer = new S055SymbolBasedAnalyzer(this.semanticEngine);
|
|
39
|
+
if (process.env.SUNLINT_DEBUG)
|
|
40
|
+
console.log(`🔧 [S055] Symbol analyzer created successfully`);
|
|
41
|
+
} catch (error) {
|
|
42
|
+
console.error(`🔧 [S055] Error creating symbol analyzer:`, error);
|
|
138
43
|
}
|
|
139
|
-
|
|
140
|
-
return violations;
|
|
141
44
|
}
|
|
142
45
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
// Config files
|
|
149
|
-
'config/', 'configs/', '.config.',
|
|
150
|
-
// Static assets
|
|
151
|
-
'public/', 'static/', 'assets/',
|
|
152
|
-
];
|
|
153
|
-
|
|
154
|
-
return skipPatterns.some(pattern => filePath.includes(pattern));
|
|
155
|
-
}
|
|
46
|
+
async initialize(semanticEngine = null) {
|
|
47
|
+
if (semanticEngine) {
|
|
48
|
+
this.semanticEngine = semanticEngine;
|
|
49
|
+
}
|
|
50
|
+
this.verbose = semanticEngine?.verbose || false;
|
|
156
51
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
const hasGlobalValidation = this.hasGlobalContentTypeValidation(content);
|
|
163
|
-
|
|
164
|
-
lines.forEach((line, index) => {
|
|
165
|
-
const lineNumber = index + 1;
|
|
166
|
-
const trimmedLine = line.trim();
|
|
167
|
-
|
|
168
|
-
// Skip comments, imports, and empty lines
|
|
169
|
-
if (this.shouldSkipLine(trimmedLine)) {
|
|
170
|
-
return;
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
// Check for potential Content-Type validation violations
|
|
174
|
-
const violation = this.checkForContentTypeViolation(
|
|
175
|
-
line,
|
|
176
|
-
lineNumber,
|
|
177
|
-
filePath,
|
|
178
|
-
content,
|
|
179
|
-
hasGlobalValidation
|
|
180
|
-
);
|
|
181
|
-
if (violation) {
|
|
182
|
-
violations.push(violation);
|
|
183
|
-
}
|
|
184
|
-
});
|
|
185
|
-
|
|
186
|
-
return violations;
|
|
187
|
-
}
|
|
52
|
+
// Initialize both analyzers
|
|
53
|
+
await this.symbolAnalyzer.initialize(semanticEngine);
|
|
54
|
+
|
|
55
|
+
// Ensure verbose flag is propagated
|
|
56
|
+
this.symbolAnalyzer.verbose = this.verbose;
|
|
188
57
|
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
this.safePatterns.some(pattern => pattern.test(line))
|
|
193
|
-
);
|
|
58
|
+
if (this.verbose) {
|
|
59
|
+
console.log(`🔧 [S055 Hybrid] Analyzer initialized - verbose: ${this.verbose}`);
|
|
60
|
+
}
|
|
194
61
|
}
|
|
195
62
|
|
|
196
|
-
|
|
197
|
-
|
|
63
|
+
analyzeSingle(filePath, options = {}) {
|
|
64
|
+
if (process.env.SUNLINT_DEBUG)
|
|
65
|
+
console.log(`🔍 [S055] analyzeSingle() called for: ${filePath}`);
|
|
66
|
+
return this.analyze([filePath], "typescript", options);
|
|
198
67
|
}
|
|
199
68
|
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
if (!hasRequestBodyUsage) {
|
|
205
|
-
return null;
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
// Check if this line is part of an HTTP handler
|
|
209
|
-
const isInHttpHandler = this.isInHttpHandlerContext(line, lineNumber, fullContent);
|
|
210
|
-
|
|
211
|
-
if (!isInHttpHandler) {
|
|
212
|
-
return null;
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
// Skip if there's global validation
|
|
216
|
-
if (hasGlobalValidation) {
|
|
217
|
-
return null;
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
// Check if there's local Content-Type validation in the same function/handler
|
|
221
|
-
const hasLocalValidation = this.hasLocalContentTypeValidation(lineNumber, fullContent);
|
|
222
|
-
|
|
223
|
-
if (hasLocalValidation) {
|
|
224
|
-
return null;
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
// Check if this is a NestJS handler with proper decorators
|
|
228
|
-
if (this.isSecureNestJSHandler(lineNumber, fullContent)) {
|
|
229
|
-
return null;
|
|
69
|
+
async analyze(files, language, options = {}) {
|
|
70
|
+
if (process.env.SUNLINT_DEBUG) {
|
|
71
|
+
console.log(`🔧 [S055] analyze() method called with ${files.length} files, language: ${language}`);
|
|
230
72
|
}
|
|
231
|
-
|
|
232
|
-
return {
|
|
233
|
-
ruleId: this.ruleId,
|
|
234
|
-
severity: 'error',
|
|
235
|
-
message: 'REST endpoint processes request body without validating Content-Type header. This can lead to security vulnerabilities.',
|
|
236
|
-
line: lineNumber,
|
|
237
|
-
column: this.findPatternColumn(line, this.requestBodyPatterns),
|
|
238
|
-
filePath: filePath,
|
|
239
|
-
type: 'missing_content_type_validation',
|
|
240
|
-
details: 'Consider adding Content-Type validation using req.is("application/json") or checking req.headers["content-type"] before processing request body.'
|
|
241
|
-
};
|
|
242
|
-
}
|
|
243
73
|
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
74
|
+
const violations = [];
|
|
75
|
+
|
|
76
|
+
for (const filePath of files) {
|
|
77
|
+
try {
|
|
78
|
+
if (process.env.SUNLINT_DEBUG) {
|
|
79
|
+
console.log(`🔧 [S055] Processing file: ${filePath}`);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const fileViolations = await this.analyzeFile(filePath, options);
|
|
83
|
+
violations.push(...fileViolations);
|
|
84
|
+
|
|
85
|
+
if (process.env.SUNLINT_DEBUG) {
|
|
86
|
+
console.log(`🔧 [S055] File ${filePath}: Found ${fileViolations.length} violations`);
|
|
87
|
+
}
|
|
88
|
+
} catch (error) {
|
|
89
|
+
console.warn(`❌ [S055] Analysis failed for ${filePath}:`, error.message);
|
|
254
90
|
}
|
|
255
91
|
}
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
return true;
|
|
92
|
+
|
|
93
|
+
if (process.env.SUNLINT_DEBUG) {
|
|
94
|
+
console.log(`🔧 [S055] Total violations found: ${violations.length}`);
|
|
260
95
|
}
|
|
261
|
-
|
|
262
|
-
return
|
|
96
|
+
|
|
97
|
+
return violations;
|
|
263
98
|
}
|
|
264
99
|
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
// Check surrounding lines for Content-Type validation
|
|
269
|
-
const startLine = Math.max(0, lineNumber - 15);
|
|
270
|
-
const endLine = Math.min(lines.length, lineNumber + 10);
|
|
271
|
-
|
|
272
|
-
for (let i = startLine; i < endLine; i++) {
|
|
273
|
-
const checkLine = lines[i];
|
|
274
|
-
if (this.contentTypeValidationPatterns.some(pattern => pattern.test(checkLine))) {
|
|
275
|
-
return true;
|
|
276
|
-
}
|
|
100
|
+
async analyzeFile(filePath, options = {}) {
|
|
101
|
+
if (process.env.SUNLINT_DEBUG) {
|
|
102
|
+
console.log(`🔧 [S055] analyzeFile() called for: ${filePath}`);
|
|
277
103
|
}
|
|
278
|
-
|
|
279
|
-
return false;
|
|
280
|
-
}
|
|
281
104
|
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
105
|
+
// 1. Try Symbol-based analysis first (primary)
|
|
106
|
+
if (this.config.useSymbolBased &&
|
|
107
|
+
this.semanticEngine?.project &&
|
|
108
|
+
this.semanticEngine?.initialized) {
|
|
109
|
+
try {
|
|
110
|
+
if (process.env.SUNLINT_DEBUG) {
|
|
111
|
+
console.log(`🔧 [S055] Trying symbol-based analysis...`);
|
|
112
|
+
}
|
|
113
|
+
const sourceFile = this.semanticEngine.project.getSourceFile(filePath);
|
|
114
|
+
if (sourceFile) {
|
|
115
|
+
if (process.env.SUNLINT_DEBUG) {
|
|
116
|
+
console.log(`🔧 [S055] Source file found, analyzing with symbol-based...`);
|
|
117
|
+
}
|
|
118
|
+
const violations = await this.symbolAnalyzer.analyzeFileWithSymbols(filePath, { ...options, verbose: options.verbose });
|
|
119
|
+
|
|
120
|
+
// Mark violations with analysis strategy
|
|
121
|
+
violations.forEach(v => v.analysisStrategy = 'symbol-based');
|
|
122
|
+
|
|
123
|
+
if (process.env.SUNLINT_DEBUG) {
|
|
124
|
+
console.log(`✅ [S055] Symbol-based analysis: ${violations.length} violations`);
|
|
125
|
+
}
|
|
126
|
+
return violations; // Return even if 0 violations - symbol analysis completed successfully
|
|
127
|
+
} else {
|
|
128
|
+
if (process.env.SUNLINT_DEBUG) {
|
|
129
|
+
console.log(`⚠️ [S055] Source file not found in project`);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
} catch (error) {
|
|
133
|
+
console.warn(`⚠️ [S055] Symbol analysis failed: ${error.message}`);
|
|
134
|
+
// Continue to fallback
|
|
292
135
|
}
|
|
293
|
-
|
|
294
|
-
|
|
136
|
+
} else {
|
|
137
|
+
if (process.env.SUNLINT_DEBUG) {
|
|
138
|
+
console.log(`🔄 [S055] Symbol analysis conditions check:`);
|
|
139
|
+
console.log(` - useSymbolBased: ${this.config.useSymbolBased}`);
|
|
140
|
+
console.log(` - semanticEngine: ${!!this.semanticEngine}`);
|
|
141
|
+
console.log(` - semanticEngine.project: ${!!this.semanticEngine?.project}`);
|
|
142
|
+
console.log(` - semanticEngine.initialized: ${this.semanticEngine?.initialized}`);
|
|
143
|
+
console.log(`🔄 [S055] Symbol analysis unavailable, using regex fallback`);
|
|
295
144
|
}
|
|
296
145
|
}
|
|
297
|
-
|
|
298
|
-
return false;
|
|
299
|
-
}
|
|
300
146
|
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
const match = pattern.exec(line);
|
|
304
|
-
if (match) {
|
|
305
|
-
return match.index + 1;
|
|
306
|
-
}
|
|
147
|
+
if (options?.verbose) {
|
|
148
|
+
console.log(`🔧 [S055] No analysis methods succeeded, returning empty`);
|
|
307
149
|
}
|
|
308
|
-
return
|
|
150
|
+
return [];
|
|
309
151
|
}
|
|
310
152
|
}
|
|
311
153
|
|