code-sentinel-mcp 0.2.4 → 0.2.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/README.md +161 -19
- package/build/analyzers/core.d.ts +7 -0
- package/build/analyzers/core.js +69 -0
- package/build/analyzers/deceptive.js +5 -198
- package/build/analyzers/errors.js +5 -212
- package/build/analyzers/placeholders.js +5 -214
- package/build/analyzers/security.js +5 -236
- package/build/index.js +661 -1
- package/build/patterns/builders.d.ts +20 -0
- package/build/patterns/builders.js +335 -0
- package/build/patterns/compiler.d.ts +16 -0
- package/build/patterns/compiler.js +108 -0
- package/build/patterns/definitions/deceptive.d.ts +2 -0
- package/build/patterns/definitions/deceptive.js +226 -0
- package/build/patterns/definitions/errors.d.ts +2 -0
- package/build/patterns/definitions/errors.js +206 -0
- package/build/patterns/definitions/index.d.ts +4 -0
- package/build/patterns/definitions/index.js +5 -0
- package/build/patterns/definitions/placeholders.d.ts +2 -0
- package/build/patterns/definitions/placeholders.js +260 -0
- package/build/patterns/definitions/security.d.ts +2 -0
- package/build/patterns/definitions/security.js +268 -0
- package/build/patterns/index.d.ts +4 -0
- package/build/patterns/index.js +7 -0
- package/build/patterns/types.d.ts +117 -0
- package/build/patterns/types.js +2 -0
- package/package.json +1 -1
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { MatchConfig, EmptyBlockConfig, FunctionCallConfig, ReturnsOnlyConfig, ContainsTextConfig, FallbackValueConfig, AssignmentPatternConfig, ChainedAccessConfig, CatchHandlerConfig, PromiseCatchConfig, CommentMarkerConfig, StringLiteralConfig, SecretPatternConfig, UrlPatternConfig, SuppressionCommentConfig, TypeCastConfig, ComparisonConfig, LoopPatternConfig, RawRegexConfig } from './types.js';
|
|
2
|
+
export declare function buildEmptyBlock(config: EmptyBlockConfig): RegExp[];
|
|
3
|
+
export declare function buildFunctionCall(config: FunctionCallConfig): RegExp[];
|
|
4
|
+
export declare function buildReturnsOnly(config: ReturnsOnlyConfig): RegExp[];
|
|
5
|
+
export declare function buildContainsText(config: ContainsTextConfig): RegExp[];
|
|
6
|
+
export declare function buildFallbackValue(config: FallbackValueConfig): RegExp[];
|
|
7
|
+
export declare function buildAssignmentPattern(config: AssignmentPatternConfig): RegExp[];
|
|
8
|
+
export declare function buildChainedAccess(config: ChainedAccessConfig): RegExp[];
|
|
9
|
+
export declare function buildCatchHandler(config: CatchHandlerConfig): RegExp[];
|
|
10
|
+
export declare function buildPromiseCatch(config: PromiseCatchConfig): RegExp[];
|
|
11
|
+
export declare function buildCommentMarker(config: CommentMarkerConfig): RegExp[];
|
|
12
|
+
export declare function buildStringLiteral(config: StringLiteralConfig): RegExp[];
|
|
13
|
+
export declare function buildSecretPattern(config: SecretPatternConfig): RegExp[];
|
|
14
|
+
export declare function buildUrlPattern(config: UrlPatternConfig): RegExp[];
|
|
15
|
+
export declare function buildSuppressionComment(config: SuppressionCommentConfig): RegExp[];
|
|
16
|
+
export declare function buildTypeCast(config: TypeCastConfig): RegExp[];
|
|
17
|
+
export declare function buildComparison(config: ComparisonConfig): RegExp[];
|
|
18
|
+
export declare function buildLoopPattern(config: LoopPatternConfig): RegExp[];
|
|
19
|
+
export declare function buildRawRegex(config: RawRegexConfig): RegExp[];
|
|
20
|
+
export declare function buildPattern(config: MatchConfig): RegExp[];
|
|
@@ -0,0 +1,335 @@
|
|
|
1
|
+
// Pattern builders - transform declarative configs into regex
|
|
2
|
+
// Utility: escape special regex characters
|
|
3
|
+
function escapeRegex(str) {
|
|
4
|
+
return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
5
|
+
}
|
|
6
|
+
// Utility: convert value shorthand to regex pattern
|
|
7
|
+
function valueToPattern(value) {
|
|
8
|
+
const valueMap = {
|
|
9
|
+
'[]': '\\[\\s*\\]',
|
|
10
|
+
'{}': '\\{\\s*\\}',
|
|
11
|
+
'null': 'null',
|
|
12
|
+
'undefined': 'undefined',
|
|
13
|
+
'true': 'true',
|
|
14
|
+
'false': 'false',
|
|
15
|
+
'""': '["\']\\s*["\']',
|
|
16
|
+
"''": '["\']\\s*["\']',
|
|
17
|
+
'0': '0',
|
|
18
|
+
'-1': '-1',
|
|
19
|
+
};
|
|
20
|
+
return valueMap[value] || escapeRegex(value);
|
|
21
|
+
}
|
|
22
|
+
// Builder: Empty blocks (catch, finally, .catch, .then)
|
|
23
|
+
export function buildEmptyBlock(config) {
|
|
24
|
+
const results = [];
|
|
25
|
+
for (const construct of config.constructs) {
|
|
26
|
+
if (construct === 'catch') {
|
|
27
|
+
// try-catch style: catch(e) { }
|
|
28
|
+
results.push(/catch\s*\([^)]*\)\s*\{\s*\}/g);
|
|
29
|
+
if (config.allowComments) {
|
|
30
|
+
results.push(/catch\s*\([^)]*\)\s*\{\s*\/\/[^\n]*\s*\}/g);
|
|
31
|
+
results.push(/catch\s*\([^)]*\)\s*\{\s*\/\*[\s\S]*?\*\/\s*\}/g);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
if (construct === 'finally') {
|
|
35
|
+
results.push(/finally\s*\{\s*\}/g);
|
|
36
|
+
}
|
|
37
|
+
if (construct === '.catch') {
|
|
38
|
+
// Promise style: .catch(() => {})
|
|
39
|
+
results.push(/\.catch\s*\(\s*\(\s*\)\s*=>\s*\{\s*\}\s*\)/g);
|
|
40
|
+
results.push(/\.catch\s*\(\s*\w+\s*=>\s*\{\s*\}\s*\)/g);
|
|
41
|
+
results.push(/\.catch\s*\(\s*function\s*\([^)]*\)\s*\{\s*\}\s*\)/g);
|
|
42
|
+
}
|
|
43
|
+
if (construct === '.then') {
|
|
44
|
+
results.push(/\.then\s*\(\s*\(\s*\)\s*=>\s*\{\s*\}\s*\)/g);
|
|
45
|
+
results.push(/\.then\s*\(\s*\w+\s*=>\s*\{\s*\}\s*\)/g);
|
|
46
|
+
}
|
|
47
|
+
if (construct === '.finally') {
|
|
48
|
+
results.push(/\.finally\s*\(\s*\(\s*\)\s*=>\s*\{\s*\}\s*\)/g);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return results;
|
|
52
|
+
}
|
|
53
|
+
// Builder: Function calls
|
|
54
|
+
export function buildFunctionCall(config) {
|
|
55
|
+
const results = [];
|
|
56
|
+
const escaped = config.names.map(n => escapeRegex(n));
|
|
57
|
+
const namesPattern = escaped.join('|');
|
|
58
|
+
// Direct call: eval(), Function()
|
|
59
|
+
results.push(new RegExp(`\\b(${namesPattern})\\s*\\(`, 'gi'));
|
|
60
|
+
// Method call: obj.eval() if enabled
|
|
61
|
+
if (config.methods) {
|
|
62
|
+
results.push(new RegExp(`\\.(${namesPattern})\\s*\\(`, 'gi'));
|
|
63
|
+
}
|
|
64
|
+
// Constructor: new Function() if enabled
|
|
65
|
+
if (config.constructors) {
|
|
66
|
+
results.push(new RegExp(`new\\s+(${namesPattern})\\s*\\(`, 'gi'));
|
|
67
|
+
}
|
|
68
|
+
return results;
|
|
69
|
+
}
|
|
70
|
+
// Builder: Return statements with specific values
|
|
71
|
+
export function buildReturnsOnly(config) {
|
|
72
|
+
const results = [];
|
|
73
|
+
const valuePatterns = config.values.map(valueToPattern);
|
|
74
|
+
const valuesPattern = valuePatterns.join('|');
|
|
75
|
+
if (config.withComment && config.withComment.length > 0) {
|
|
76
|
+
// return value; // comment with specific words
|
|
77
|
+
const commentWords = config.withComment.join('|');
|
|
78
|
+
results.push(new RegExp(`return\\s+(${valuesPattern})\\s*;?\\s*\\/\\/\\s*(?:.*?)(${commentWords})`, 'gi'));
|
|
79
|
+
}
|
|
80
|
+
else {
|
|
81
|
+
// Just return value;
|
|
82
|
+
results.push(new RegExp(`return\\s+(${valuesPattern})\\s*;?`, 'gi'));
|
|
83
|
+
}
|
|
84
|
+
return results;
|
|
85
|
+
}
|
|
86
|
+
// Builder: Text search in context
|
|
87
|
+
export function buildContainsText(config) {
|
|
88
|
+
const results = [];
|
|
89
|
+
const flags = config.caseInsensitive !== false ? 'gi' : 'g';
|
|
90
|
+
const termsPattern = config.terms.map(escapeRegex).join('|');
|
|
91
|
+
const context = config.context || 'any';
|
|
92
|
+
if (context === 'single_line_comment' || context === 'comment' || context === 'any') {
|
|
93
|
+
results.push(new RegExp(`\\/\\/.*?(${termsPattern})`, flags));
|
|
94
|
+
}
|
|
95
|
+
if (context === 'block_comment' || context === 'comment' || context === 'any') {
|
|
96
|
+
results.push(new RegExp(`\\/\\*[\\s\\S]*?(${termsPattern})[\\s\\S]*?\\*\\/`, flags));
|
|
97
|
+
}
|
|
98
|
+
if (context === 'string' || context === 'any') {
|
|
99
|
+
results.push(new RegExp(`(['"\`])[^'"\`]*(${termsPattern})[^'"\`]*\\1`, flags));
|
|
100
|
+
}
|
|
101
|
+
return results;
|
|
102
|
+
}
|
|
103
|
+
// Builder: Fallback values
|
|
104
|
+
export function buildFallbackValue(config) {
|
|
105
|
+
const results = [];
|
|
106
|
+
const valuePatterns = config.values.map(valueToPattern);
|
|
107
|
+
const valuesPattern = valuePatterns.join('|');
|
|
108
|
+
for (const op of config.operators) {
|
|
109
|
+
const escapedOp = escapeRegex(op);
|
|
110
|
+
results.push(new RegExp(`${escapedOp}\\s*(${valuesPattern})`, 'g'));
|
|
111
|
+
}
|
|
112
|
+
return results;
|
|
113
|
+
}
|
|
114
|
+
// Builder: Assignment patterns
|
|
115
|
+
export function buildAssignmentPattern(config) {
|
|
116
|
+
const results = [];
|
|
117
|
+
const keysPattern = config.keys.map(escapeRegex).join('|');
|
|
118
|
+
const operators = config.operators || ['=', ':'];
|
|
119
|
+
for (const op of operators) {
|
|
120
|
+
if (config.values && config.values.length > 0) {
|
|
121
|
+
const valuesPattern = config.values.map(escapeRegex).join('|');
|
|
122
|
+
results.push(new RegExp(`(?:${keysPattern})\\s*${escapeRegex(op)}\\s*['"\`]([^'"\`]*(?:${valuesPattern})[^'"\`]*)['"\`]`, 'gi'));
|
|
123
|
+
}
|
|
124
|
+
else {
|
|
125
|
+
results.push(new RegExp(`(?:${keysPattern})\\s*${escapeRegex(op)}\\s*['"\`][^'"\`]{8,}['"\`]`, 'gi'));
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
return results;
|
|
129
|
+
}
|
|
130
|
+
// Builder: Chained access
|
|
131
|
+
export function buildChainedAccess(config) {
|
|
132
|
+
const op = config.operator === '?.' ? '\\?\\.' : '\\.';
|
|
133
|
+
const pattern = `(?:${op}\\w+){${config.minDepth},}`;
|
|
134
|
+
return [new RegExp(pattern, 'g')];
|
|
135
|
+
}
|
|
136
|
+
// Builder: Catch handlers
|
|
137
|
+
export function buildCatchHandler(config) {
|
|
138
|
+
const results = [];
|
|
139
|
+
switch (config.behavior) {
|
|
140
|
+
case 'empty':
|
|
141
|
+
results.push(/catch\s*\([^)]*\)\s*\{\s*\}/g);
|
|
142
|
+
break;
|
|
143
|
+
case 'comment_only':
|
|
144
|
+
results.push(/catch\s*\([^)]*\)\s*\{\s*\/\/.*?\s*\}/gs);
|
|
145
|
+
results.push(/catch\s*\([^)]*\)\s*\{\s*\/\*[\s\S]*?\*\/\s*\}/g);
|
|
146
|
+
break;
|
|
147
|
+
case 'log_only':
|
|
148
|
+
results.push(/catch\s*\([^)]*\)\s*\{\s*(?:console\.log|print)\s*\([^)]*\)\s*;?\s*\}/g);
|
|
149
|
+
break;
|
|
150
|
+
case 'returns_value':
|
|
151
|
+
results.push(/catch\s*\([^)]*\)\s*\{[^}]*return\s+(?:null|undefined|false|true|''|""|\[\s*\]|\{\s*\})\s*;?\s*\}/g);
|
|
152
|
+
break;
|
|
153
|
+
case 'ignores_error':
|
|
154
|
+
results.push(/catch\s*\(\s*_\s*\)/g);
|
|
155
|
+
break;
|
|
156
|
+
}
|
|
157
|
+
return results;
|
|
158
|
+
}
|
|
159
|
+
// Builder: Promise catch
|
|
160
|
+
export function buildPromiseCatch(config) {
|
|
161
|
+
const results = [];
|
|
162
|
+
switch (config.behavior) {
|
|
163
|
+
case 'empty':
|
|
164
|
+
results.push(/\.catch\s*\(\s*\(\s*\)\s*=>\s*\{\s*\}\s*\)/g);
|
|
165
|
+
results.push(/\.catch\s*\(\s*\w+\s*=>\s*\{\s*\}\s*\)/g);
|
|
166
|
+
break;
|
|
167
|
+
case 'returns_silent':
|
|
168
|
+
const values = config.silentValues || ['null', 'undefined', 'false', 'true', "''", '""'];
|
|
169
|
+
const valuesPattern = values.map(valueToPattern).join('|');
|
|
170
|
+
results.push(new RegExp(`\\.catch\\s*\\(\\s*\\(\\s*\\)\\s*=>\\s*(?:${valuesPattern})\\s*\\)`, 'g'));
|
|
171
|
+
break;
|
|
172
|
+
case 'ignores_param':
|
|
173
|
+
results.push(/\.catch\s*\(\s*_\s*=>\s*\{?\s*\}?\s*\)/g);
|
|
174
|
+
break;
|
|
175
|
+
}
|
|
176
|
+
return results;
|
|
177
|
+
}
|
|
178
|
+
// Builder: Comment markers
|
|
179
|
+
export function buildCommentMarker(config) {
|
|
180
|
+
const results = [];
|
|
181
|
+
const markersPattern = config.markers.map(escapeRegex).join('|');
|
|
182
|
+
const style = config.style || 'any';
|
|
183
|
+
if (style === 'single' || style === 'any') {
|
|
184
|
+
results.push(new RegExp(`\\/\\/\\s*(${markersPattern})(?::|\\.|\\s).*$`, 'gim'));
|
|
185
|
+
}
|
|
186
|
+
if (style === 'block' || style === 'any') {
|
|
187
|
+
results.push(new RegExp(`\\/\\*[\\s\\S]*?(${markersPattern})[\\s\\S]*?\\*\\/`, 'gi'));
|
|
188
|
+
}
|
|
189
|
+
return results;
|
|
190
|
+
}
|
|
191
|
+
// Builder: String literals
|
|
192
|
+
export function buildStringLiteral(config) {
|
|
193
|
+
const flags = config.caseInsensitive ? 'gi' : 'g';
|
|
194
|
+
const patternsJoined = config.patterns.map(escapeRegex).join('|');
|
|
195
|
+
return [new RegExp(`['"\`](?:[^'"\`]*)?(${patternsJoined})(?:[^'"\`]*)?['"\`]`, flags)];
|
|
196
|
+
}
|
|
197
|
+
// Builder: Secret patterns
|
|
198
|
+
export function buildSecretPattern(config) {
|
|
199
|
+
const results = [];
|
|
200
|
+
switch (config.kind) {
|
|
201
|
+
case 'generic':
|
|
202
|
+
results.push(/(?:api[_-]?key|apikey|secret|password|passwd|pwd|token|auth[_-]?token|access[_-]?token|private[_-]?key)\s*[:=]\s*['"`][^'"`]{8,}['"`]/gi);
|
|
203
|
+
break;
|
|
204
|
+
case 'github':
|
|
205
|
+
results.push(/(?:ghp|gho|ghu|ghs|ghr)_[A-Za-z0-9_]{36,}/g);
|
|
206
|
+
break;
|
|
207
|
+
case 'openai':
|
|
208
|
+
results.push(/sk-[A-Za-z0-9]{48,}/g);
|
|
209
|
+
break;
|
|
210
|
+
case 'aws':
|
|
211
|
+
results.push(/AKIA[0-9A-Z]{16}/g);
|
|
212
|
+
break;
|
|
213
|
+
case 'stripe':
|
|
214
|
+
results.push(/sk_(?:live|test)_[A-Za-z0-9]{24,}/g);
|
|
215
|
+
results.push(/pk_(?:live|test)_[A-Za-z0-9]{24,}/g);
|
|
216
|
+
break;
|
|
217
|
+
case 'custom':
|
|
218
|
+
if (config.customPattern) {
|
|
219
|
+
results.push(new RegExp(config.customPattern, 'g'));
|
|
220
|
+
}
|
|
221
|
+
break;
|
|
222
|
+
}
|
|
223
|
+
return results;
|
|
224
|
+
}
|
|
225
|
+
// Builder: URL patterns
|
|
226
|
+
export function buildUrlPattern(config) {
|
|
227
|
+
const results = [];
|
|
228
|
+
const protocol = config.protocol || 'any';
|
|
229
|
+
if (protocol === 'http' || protocol === 'any') {
|
|
230
|
+
if (config.excludeLocalhost) {
|
|
231
|
+
results.push(/http:\/\/(?!localhost|127\.0\.0\.1)[^\s'"`)]+/g);
|
|
232
|
+
}
|
|
233
|
+
else {
|
|
234
|
+
results.push(/http:\/\/[^\s'"`)]+/g);
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
if (protocol === 'https' || protocol === 'any') {
|
|
238
|
+
results.push(/https:\/\/[^\s'"`)]+/g);
|
|
239
|
+
}
|
|
240
|
+
return results;
|
|
241
|
+
}
|
|
242
|
+
// Builder: Suppression comments
|
|
243
|
+
export function buildSuppressionComment(config) {
|
|
244
|
+
const toolsPattern = config.tools.map(escapeRegex).join('|');
|
|
245
|
+
return [new RegExp(`\\/\\/\\s*(?:@)?(${toolsPattern})`, 'g')];
|
|
246
|
+
}
|
|
247
|
+
// Builder: Type casts
|
|
248
|
+
export function buildTypeCast(config) {
|
|
249
|
+
const targetsPattern = config.targets.map(escapeRegex).join('|');
|
|
250
|
+
return [new RegExp(`as\\s+(${targetsPattern})(?!\\w)`, 'g')];
|
|
251
|
+
}
|
|
252
|
+
// Builder: Comparison
|
|
253
|
+
export function buildComparison(config) {
|
|
254
|
+
const results = [];
|
|
255
|
+
for (const op of config.operators) {
|
|
256
|
+
if (op === '==') {
|
|
257
|
+
results.push(/[^!=]==[^=]/g);
|
|
258
|
+
}
|
|
259
|
+
else if (op === '!=') {
|
|
260
|
+
results.push(/!=[^=]/g);
|
|
261
|
+
}
|
|
262
|
+
else if (op === '===') {
|
|
263
|
+
results.push(/===/g);
|
|
264
|
+
}
|
|
265
|
+
else if (op === '!==') {
|
|
266
|
+
results.push(/!==/g);
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
return results;
|
|
270
|
+
}
|
|
271
|
+
// Builder: Loop patterns
|
|
272
|
+
export function buildLoopPattern(config) {
|
|
273
|
+
const results = [];
|
|
274
|
+
switch (config.kind) {
|
|
275
|
+
case 'for_in':
|
|
276
|
+
results.push(/for\s*\(\s*(?:var|let|const)\s+\w+\s+in\s+/g);
|
|
277
|
+
break;
|
|
278
|
+
case 'while_true':
|
|
279
|
+
results.push(/while\s*\(\s*true\s*\)/g);
|
|
280
|
+
break;
|
|
281
|
+
case 'infinite':
|
|
282
|
+
results.push(/while\s*\(\s*true\s*\)/g);
|
|
283
|
+
results.push(/for\s*\(\s*;\s*;\s*\)/g);
|
|
284
|
+
break;
|
|
285
|
+
}
|
|
286
|
+
return results;
|
|
287
|
+
}
|
|
288
|
+
// Builder: Raw regex (escape hatch)
|
|
289
|
+
export function buildRawRegex(config) {
|
|
290
|
+
return [new RegExp(config.pattern, config.flags || 'g')];
|
|
291
|
+
}
|
|
292
|
+
// Main dispatcher: convert any MatchConfig to RegExp[]
|
|
293
|
+
export function buildPattern(config) {
|
|
294
|
+
switch (config.type) {
|
|
295
|
+
case 'empty_block':
|
|
296
|
+
return buildEmptyBlock(config);
|
|
297
|
+
case 'function_call':
|
|
298
|
+
return buildFunctionCall(config);
|
|
299
|
+
case 'returns_only':
|
|
300
|
+
return buildReturnsOnly(config);
|
|
301
|
+
case 'contains_text':
|
|
302
|
+
return buildContainsText(config);
|
|
303
|
+
case 'fallback_value':
|
|
304
|
+
return buildFallbackValue(config);
|
|
305
|
+
case 'assignment_pattern':
|
|
306
|
+
return buildAssignmentPattern(config);
|
|
307
|
+
case 'chained_access':
|
|
308
|
+
return buildChainedAccess(config);
|
|
309
|
+
case 'catch_handler':
|
|
310
|
+
return buildCatchHandler(config);
|
|
311
|
+
case 'promise_catch':
|
|
312
|
+
return buildPromiseCatch(config);
|
|
313
|
+
case 'comment_marker':
|
|
314
|
+
return buildCommentMarker(config);
|
|
315
|
+
case 'string_literal':
|
|
316
|
+
return buildStringLiteral(config);
|
|
317
|
+
case 'secret_pattern':
|
|
318
|
+
return buildSecretPattern(config);
|
|
319
|
+
case 'url_pattern':
|
|
320
|
+
return buildUrlPattern(config);
|
|
321
|
+
case 'suppression_comment':
|
|
322
|
+
return buildSuppressionComment(config);
|
|
323
|
+
case 'type_cast':
|
|
324
|
+
return buildTypeCast(config);
|
|
325
|
+
case 'comparison':
|
|
326
|
+
return buildComparison(config);
|
|
327
|
+
case 'loop_pattern':
|
|
328
|
+
return buildLoopPattern(config);
|
|
329
|
+
case 'raw_regex':
|
|
330
|
+
return buildRawRegex(config);
|
|
331
|
+
default:
|
|
332
|
+
console.warn(`Unknown match type: ${config.type}`);
|
|
333
|
+
return [];
|
|
334
|
+
}
|
|
335
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { PatternDefinition, CompiledPattern } from './types.js';
|
|
2
|
+
import { Category } from '../types.js';
|
|
3
|
+
export declare function compileDefinition(definition: PatternDefinition): CompiledPattern;
|
|
4
|
+
export declare function compileCategory(definitions: PatternDefinition[]): CompiledPattern[];
|
|
5
|
+
export declare function getCompiledPatterns(category?: Category): CompiledPattern[];
|
|
6
|
+
export declare function clearPatternCache(): void;
|
|
7
|
+
export declare function getDefinitions(category?: Category): PatternDefinition[];
|
|
8
|
+
export declare function validateDefinitions(): {
|
|
9
|
+
valid: boolean;
|
|
10
|
+
errors: string[];
|
|
11
|
+
};
|
|
12
|
+
export declare function getPatternStats(): {
|
|
13
|
+
total: number;
|
|
14
|
+
byCategory: Record<string, number>;
|
|
15
|
+
bySeverity: Record<string, number>;
|
|
16
|
+
};
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
// Pattern compiler - transforms definitions into executable patterns
|
|
2
|
+
import { buildPattern } from './builders.js';
|
|
3
|
+
import { securityDefinitions, deceptiveDefinitions, placeholderDefinitions, errorDefinitions, } from './definitions/index.js';
|
|
4
|
+
// Cache for compiled patterns
|
|
5
|
+
let compiledCache = null;
|
|
6
|
+
// Compile a single pattern definition
|
|
7
|
+
export function compileDefinition(definition) {
|
|
8
|
+
const patterns = buildPattern(definition.match);
|
|
9
|
+
return {
|
|
10
|
+
id: definition.id,
|
|
11
|
+
title: definition.title,
|
|
12
|
+
description: definition.description,
|
|
13
|
+
severity: definition.severity,
|
|
14
|
+
category: definition.category,
|
|
15
|
+
suggestion: definition.suggestion,
|
|
16
|
+
patterns,
|
|
17
|
+
verification: definition.verification,
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
// Compile all definitions of a specific category
|
|
21
|
+
export function compileCategory(definitions) {
|
|
22
|
+
return definitions.map(compileDefinition);
|
|
23
|
+
}
|
|
24
|
+
// Get all compiled patterns, optionally filtered by category
|
|
25
|
+
export function getCompiledPatterns(category) {
|
|
26
|
+
// Build cache if needed
|
|
27
|
+
if (!compiledCache) {
|
|
28
|
+
compiledCache = new Map();
|
|
29
|
+
compiledCache.set('security', compileCategory(securityDefinitions));
|
|
30
|
+
compiledCache.set('deceptive', compileCategory(deceptiveDefinitions));
|
|
31
|
+
compiledCache.set('placeholder', compileCategory(placeholderDefinitions));
|
|
32
|
+
compiledCache.set('error', compileCategory(errorDefinitions));
|
|
33
|
+
}
|
|
34
|
+
if (category) {
|
|
35
|
+
return compiledCache.get(category) || [];
|
|
36
|
+
}
|
|
37
|
+
// Return all patterns
|
|
38
|
+
const all = [];
|
|
39
|
+
for (const patterns of compiledCache.values()) {
|
|
40
|
+
all.push(...patterns);
|
|
41
|
+
}
|
|
42
|
+
return all;
|
|
43
|
+
}
|
|
44
|
+
// Clear the cache (useful for testing or hot reload)
|
|
45
|
+
export function clearPatternCache() {
|
|
46
|
+
compiledCache = null;
|
|
47
|
+
}
|
|
48
|
+
// Get raw definitions (for inspection/debugging)
|
|
49
|
+
export function getDefinitions(category) {
|
|
50
|
+
const allDefs = {
|
|
51
|
+
security: securityDefinitions,
|
|
52
|
+
deceptive: deceptiveDefinitions,
|
|
53
|
+
placeholder: placeholderDefinitions,
|
|
54
|
+
error: errorDefinitions,
|
|
55
|
+
strength: [], // Strengths use a different system
|
|
56
|
+
};
|
|
57
|
+
if (category) {
|
|
58
|
+
return allDefs[category] || [];
|
|
59
|
+
}
|
|
60
|
+
return [
|
|
61
|
+
...securityDefinitions,
|
|
62
|
+
...deceptiveDefinitions,
|
|
63
|
+
...placeholderDefinitions,
|
|
64
|
+
...errorDefinitions,
|
|
65
|
+
];
|
|
66
|
+
}
|
|
67
|
+
// Validate all definitions (for testing)
|
|
68
|
+
export function validateDefinitions() {
|
|
69
|
+
const errors = [];
|
|
70
|
+
const allDefs = getDefinitions();
|
|
71
|
+
const ids = new Set();
|
|
72
|
+
for (const def of allDefs) {
|
|
73
|
+
// Check for duplicate IDs
|
|
74
|
+
if (ids.has(def.id)) {
|
|
75
|
+
errors.push(`Duplicate pattern ID: ${def.id}`);
|
|
76
|
+
}
|
|
77
|
+
ids.add(def.id);
|
|
78
|
+
// Try to compile and check for errors
|
|
79
|
+
try {
|
|
80
|
+
const compiled = compileDefinition(def);
|
|
81
|
+
if (compiled.patterns.length === 0) {
|
|
82
|
+
errors.push(`Pattern ${def.id} compiled to zero regex patterns`);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
catch (e) {
|
|
86
|
+
errors.push(`Pattern ${def.id} failed to compile: ${e instanceof Error ? e.message : String(e)}`);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
return {
|
|
90
|
+
valid: errors.length === 0,
|
|
91
|
+
errors,
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
// Stats about patterns
|
|
95
|
+
export function getPatternStats() {
|
|
96
|
+
const allDefs = getDefinitions();
|
|
97
|
+
const byCategory = {};
|
|
98
|
+
const bySeverity = {};
|
|
99
|
+
for (const def of allDefs) {
|
|
100
|
+
byCategory[def.category] = (byCategory[def.category] || 0) + 1;
|
|
101
|
+
bySeverity[def.severity] = (bySeverity[def.severity] || 0) + 1;
|
|
102
|
+
}
|
|
103
|
+
return {
|
|
104
|
+
total: allDefs.length,
|
|
105
|
+
byCategory,
|
|
106
|
+
bySeverity,
|
|
107
|
+
};
|
|
108
|
+
}
|