@neurcode-ai/governance-runtime 0.1.1 → 0.1.2
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/dist/constraints.d.ts +7 -0
- package/dist/constraints.d.ts.map +1 -1
- package/dist/constraints.js +220 -3
- package/dist/constraints.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +63 -3
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/constraints.ts +252 -3
- package/src/index.test.ts +178 -0
- package/src/index.ts +85 -3
package/dist/constraints.d.ts
CHANGED
|
@@ -6,6 +6,13 @@ export interface DeterministicConstraintRule {
|
|
|
6
6
|
displayName: string;
|
|
7
7
|
pattern: RegExp;
|
|
8
8
|
matchToken: string;
|
|
9
|
+
pathIncludePatterns?: string[];
|
|
10
|
+
pathExcludePatterns?: string[];
|
|
11
|
+
pathIncludes?: RegExp[];
|
|
12
|
+
pathExcludes?: RegExp[];
|
|
13
|
+
minMatchesPerFile?: number;
|
|
14
|
+
maxMatchesPerFile?: number;
|
|
15
|
+
evaluationMode?: 'added_lines' | 'full_file';
|
|
9
16
|
}
|
|
10
17
|
export interface DeterministicConstraintCompilation {
|
|
11
18
|
rules: DeterministicConstraintRule[];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"constraints.d.ts","sourceRoot":"","sources":["../src/constraints.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,6BAA6B,GAAG,QAAQ,GAAG,QAAQ,CAAC;AAEhE,MAAM,WAAW,2BAA2B;IAC1C,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,6BAA6B,CAAC;IACtC,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"constraints.d.ts","sourceRoot":"","sources":["../src/constraints.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,6BAA6B,GAAG,QAAQ,GAAG,QAAQ,CAAC;AAEhE,MAAM,WAAW,2BAA2B;IAC1C,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,6BAA6B,CAAC;IACtC,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,mBAAmB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC/B,mBAAmB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC/B,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,cAAc,CAAC,EAAE,aAAa,GAAG,WAAW,CAAC;CAC9C;AAED,MAAM,WAAW,kCAAkC;IACjD,KAAK,EAAE,2BAA2B,EAAE,CAAC;IACrC,mBAAmB,EAAE,MAAM,EAAE,CAAC;CAC/B;AAED,MAAM,WAAW,uCAAuC;IACtD,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;CACxB;AAsYD,wBAAgB,+BAA+B,CAC7C,KAAK,EAAE,uCAAuC,GAC7C,kCAAkC,CAapC"}
|
package/dist/constraints.js
CHANGED
|
@@ -52,6 +52,27 @@ const CONSTRAINT_TEMPLATES = [
|
|
|
52
52
|
pattern: /\b(TODO|FIXME)\b/i,
|
|
53
53
|
matchToken: 'todo/fixme',
|
|
54
54
|
},
|
|
55
|
+
{
|
|
56
|
+
id: 'no_network_calls',
|
|
57
|
+
displayName: 'No network calls',
|
|
58
|
+
triggerTokens: ['network call', 'network calls', 'http call', 'api call', 'external call'],
|
|
59
|
+
pattern: /\b(fetch\s*\(|axios\.[a-z]+\s*\(|axios\s*\(|XMLHttpRequest\b|http\.request\s*\(|https\.request\s*\(|got\s*\(|superagent\s*\()/i,
|
|
60
|
+
matchToken: 'network-call',
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
id: 'no_child_process',
|
|
64
|
+
displayName: 'No child_process execution',
|
|
65
|
+
triggerTokens: ['child_process', 'shell command', 'exec(', 'spawn('],
|
|
66
|
+
pattern: /\b(child_process|exec\s*\(|execFile\s*\(|spawn\s*\(|fork\s*\()/i,
|
|
67
|
+
matchToken: 'child_process',
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
id: 'no_innerhtml',
|
|
71
|
+
displayName: 'No innerHTML / dangerouslySetInnerHTML',
|
|
72
|
+
triggerTokens: ['innerhtml', 'dangerouslysetinnerhtml', 'dom injection'],
|
|
73
|
+
pattern: /\b(innerHTML|dangerouslySetInnerHTML)\b/i,
|
|
74
|
+
matchToken: 'innerHTML',
|
|
75
|
+
},
|
|
55
76
|
];
|
|
56
77
|
function splitStatements(raw) {
|
|
57
78
|
return raw
|
|
@@ -65,10 +86,180 @@ function normalizeStatement(statement) {
|
|
|
65
86
|
.trim()
|
|
66
87
|
.toLowerCase();
|
|
67
88
|
}
|
|
89
|
+
function normalizePathScopeToken(rawValue) {
|
|
90
|
+
return rawValue
|
|
91
|
+
.trim()
|
|
92
|
+
.replace(/^[`'"]+|[`'"]+$/g, '')
|
|
93
|
+
.replace(/\\/g, '/')
|
|
94
|
+
.replace(/^\.\//, '')
|
|
95
|
+
.replace(/\/{2,}/g, '/');
|
|
96
|
+
}
|
|
97
|
+
function looksLikePathScope(token) {
|
|
98
|
+
if (!token)
|
|
99
|
+
return false;
|
|
100
|
+
if (/\s/.test(token))
|
|
101
|
+
return false;
|
|
102
|
+
if (token.includes('/'))
|
|
103
|
+
return true;
|
|
104
|
+
if (token.includes('*'))
|
|
105
|
+
return true;
|
|
106
|
+
return /\.[a-z0-9]{1,8}$/i.test(token);
|
|
107
|
+
}
|
|
108
|
+
function globLikeToRegex(pattern) {
|
|
109
|
+
const escaped = pattern
|
|
110
|
+
.split('*')
|
|
111
|
+
.map((segment) => segment.replace(/[.+^${}()|[\]\\]/g, '\\$&'))
|
|
112
|
+
.join('.*');
|
|
113
|
+
return new RegExp(`^${escaped}$`, 'i');
|
|
114
|
+
}
|
|
115
|
+
function compilePathPatterns(patterns) {
|
|
116
|
+
return patterns
|
|
117
|
+
.map((pattern) => {
|
|
118
|
+
try {
|
|
119
|
+
return globLikeToRegex(pattern);
|
|
120
|
+
}
|
|
121
|
+
catch {
|
|
122
|
+
return null;
|
|
123
|
+
}
|
|
124
|
+
})
|
|
125
|
+
.filter((item) => item instanceof RegExp);
|
|
126
|
+
}
|
|
127
|
+
function parsePathScopes(statement) {
|
|
128
|
+
const includePatterns = new Set();
|
|
129
|
+
const excludePatterns = new Set();
|
|
130
|
+
const includeRegex = /\b(?:in|within|under|inside)\s+[`'"]?([A-Za-z0-9_./*?-]+)[`'"]?/gi;
|
|
131
|
+
const excludeRegex = /\b(?:except|excluding|but\s+not\s+in|not\s+in)\s+[`'"]?([A-Za-z0-9_./*?-]+)[`'"]?/gi;
|
|
132
|
+
for (const match of statement.matchAll(includeRegex)) {
|
|
133
|
+
const candidate = normalizePathScopeToken(match[1] || '');
|
|
134
|
+
if (looksLikePathScope(candidate)) {
|
|
135
|
+
includePatterns.add(candidate);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
for (const match of statement.matchAll(excludeRegex)) {
|
|
139
|
+
const candidate = normalizePathScopeToken(match[1] || '');
|
|
140
|
+
if (looksLikePathScope(candidate)) {
|
|
141
|
+
excludePatterns.add(candidate);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
return {
|
|
145
|
+
includePatterns: [...includePatterns],
|
|
146
|
+
excludePatterns: [...excludePatterns],
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
function escapeRegex(value) {
|
|
150
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
151
|
+
}
|
|
152
|
+
function parseInvocationLimitRule(statement, source, pathScopes) {
|
|
153
|
+
const normalized = normalizeStatement(statement);
|
|
154
|
+
const fnPatterns = [
|
|
155
|
+
{
|
|
156
|
+
regex: /\b([a-z_$][a-z0-9_$]*)\s+(?:function\s+)?(?:should\s+be\s+)?(?:invoked|called)\s+(?:only\s+)?(\d+)\s+times?\b/i,
|
|
157
|
+
fnIndex: 1,
|
|
158
|
+
countIndex: 2,
|
|
159
|
+
comparator: 'max',
|
|
160
|
+
},
|
|
161
|
+
{
|
|
162
|
+
regex: /\b([a-z_$][a-z0-9_$]*)\s+(?:function\s+)?(?:should\s+be\s+)?(?:invoked|called)\s+(?:at\s+most|no\s+more\s+than|maximum)\s+(\d+)\s+times?\b/i,
|
|
163
|
+
fnIndex: 1,
|
|
164
|
+
countIndex: 2,
|
|
165
|
+
comparator: 'max',
|
|
166
|
+
},
|
|
167
|
+
{
|
|
168
|
+
regex: /\b([a-z_$][a-z0-9_$]*)\s+(?:function\s+)?(?:should\s+be\s+)?(?:invoked|called)\s+(?:at\s+least|no\s+less\s+than|minimum)\s+(\d+)\s+times?\b/i,
|
|
169
|
+
fnIndex: 1,
|
|
170
|
+
countIndex: 2,
|
|
171
|
+
comparator: 'min',
|
|
172
|
+
},
|
|
173
|
+
{
|
|
174
|
+
regex: /\b([a-z_$][a-z0-9_$]*)\s+(?:function\s+)?(?:should\s+be\s+)?(?:invoked|called)\s+(?:exactly)\s+(\d+)\s+times?\b/i,
|
|
175
|
+
fnIndex: 1,
|
|
176
|
+
countIndex: 2,
|
|
177
|
+
comparator: 'exact',
|
|
178
|
+
},
|
|
179
|
+
{
|
|
180
|
+
regex: /\bonly\s+(\d+)\s+calls?\s+to\s+([a-z_$][a-z0-9_$]*)\b/i,
|
|
181
|
+
fnIndex: 2,
|
|
182
|
+
countIndex: 1,
|
|
183
|
+
comparator: 'max',
|
|
184
|
+
},
|
|
185
|
+
{
|
|
186
|
+
regex: /\bat\s+most\s+(\d+)\s+calls?\s+to\s+([a-z_$][a-z0-9_$]*)\b/i,
|
|
187
|
+
fnIndex: 2,
|
|
188
|
+
countIndex: 1,
|
|
189
|
+
comparator: 'max',
|
|
190
|
+
},
|
|
191
|
+
{
|
|
192
|
+
regex: /\bat\s+least\s+(\d+)\s+calls?\s+to\s+([a-z_$][a-z0-9_$]*)\b/i,
|
|
193
|
+
fnIndex: 2,
|
|
194
|
+
countIndex: 1,
|
|
195
|
+
comparator: 'min',
|
|
196
|
+
},
|
|
197
|
+
{
|
|
198
|
+
regex: /\bexactly\s+(\d+)\s+calls?\s+to\s+([a-z_$][a-z0-9_$]*)\b/i,
|
|
199
|
+
fnIndex: 2,
|
|
200
|
+
countIndex: 1,
|
|
201
|
+
comparator: 'exact',
|
|
202
|
+
},
|
|
203
|
+
];
|
|
204
|
+
let fnName = null;
|
|
205
|
+
let rawLimit = null;
|
|
206
|
+
let comparator = 'max';
|
|
207
|
+
for (const pattern of fnPatterns) {
|
|
208
|
+
const match = normalized.match(pattern.regex);
|
|
209
|
+
if (!match) {
|
|
210
|
+
continue;
|
|
211
|
+
}
|
|
212
|
+
fnName = match[pattern.fnIndex] || null;
|
|
213
|
+
rawLimit = match[pattern.countIndex] || null;
|
|
214
|
+
comparator = pattern.comparator;
|
|
215
|
+
break;
|
|
216
|
+
}
|
|
217
|
+
if (!fnName || !rawLimit) {
|
|
218
|
+
return null;
|
|
219
|
+
}
|
|
220
|
+
const limit = Number(rawLimit);
|
|
221
|
+
if (!Number.isFinite(limit) || limit < 0) {
|
|
222
|
+
return null;
|
|
223
|
+
}
|
|
224
|
+
const includePatterns = [...pathScopes.includePatterns];
|
|
225
|
+
const excludePatterns = [...pathScopes.excludePatterns];
|
|
226
|
+
const displaySuffix = comparator === 'exact'
|
|
227
|
+
? `exactly ${limit}`
|
|
228
|
+
: comparator === 'min'
|
|
229
|
+
? `at least ${limit}`
|
|
230
|
+
: `at most ${limit}`;
|
|
231
|
+
const minMatches = comparator === 'min' || comparator === 'exact' ? limit : undefined;
|
|
232
|
+
const maxMatches = comparator === 'max' || comparator === 'exact' ? limit : undefined;
|
|
233
|
+
return {
|
|
234
|
+
id: `${source}:${comparator}_invocations_${fnName.toLowerCase()}`,
|
|
235
|
+
source,
|
|
236
|
+
statement,
|
|
237
|
+
displayName: `${fnName}() invocation limit (${displaySuffix})`,
|
|
238
|
+
pattern: new RegExp(`(?<!function\\s)\\b${escapeRegex(fnName)}\\s*\\(`, 'i'),
|
|
239
|
+
matchToken: `${fnName}(`,
|
|
240
|
+
...(typeof minMatches === 'number' ? { minMatchesPerFile: minMatches } : {}),
|
|
241
|
+
...(typeof maxMatches === 'number' ? { maxMatchesPerFile: maxMatches } : {}),
|
|
242
|
+
evaluationMode: 'full_file',
|
|
243
|
+
...(includePatterns.length > 0
|
|
244
|
+
? {
|
|
245
|
+
pathIncludePatterns: includePatterns,
|
|
246
|
+
pathIncludes: compilePathPatterns(includePatterns),
|
|
247
|
+
}
|
|
248
|
+
: {}),
|
|
249
|
+
...(excludePatterns.length > 0
|
|
250
|
+
? {
|
|
251
|
+
pathExcludePatterns: excludePatterns,
|
|
252
|
+
pathExcludes: compilePathPatterns(excludePatterns),
|
|
253
|
+
}
|
|
254
|
+
: {}),
|
|
255
|
+
};
|
|
256
|
+
}
|
|
68
257
|
function statementMatchesTemplate(normalizedStatement, template) {
|
|
69
258
|
return template.triggerTokens.some((token) => normalizedStatement.includes(token));
|
|
70
259
|
}
|
|
71
|
-
function createRule(template, source, statement) {
|
|
260
|
+
function createRule(template, source, statement, pathScopes) {
|
|
261
|
+
const includePatterns = [...pathScopes.includePatterns];
|
|
262
|
+
const excludePatterns = [...pathScopes.excludePatterns];
|
|
72
263
|
return {
|
|
73
264
|
id: `${source}:${template.id}`,
|
|
74
265
|
source,
|
|
@@ -76,6 +267,18 @@ function createRule(template, source, statement) {
|
|
|
76
267
|
displayName: template.displayName,
|
|
77
268
|
pattern: template.pattern,
|
|
78
269
|
matchToken: template.matchToken,
|
|
270
|
+
...(includePatterns.length > 0
|
|
271
|
+
? {
|
|
272
|
+
pathIncludePatterns: includePatterns,
|
|
273
|
+
pathIncludes: compilePathPatterns(includePatterns),
|
|
274
|
+
}
|
|
275
|
+
: {}),
|
|
276
|
+
...(excludePatterns.length > 0
|
|
277
|
+
? {
|
|
278
|
+
pathExcludePatterns: excludePatterns,
|
|
279
|
+
pathExcludes: compilePathPatterns(excludePatterns),
|
|
280
|
+
}
|
|
281
|
+
: {}),
|
|
79
282
|
};
|
|
80
283
|
}
|
|
81
284
|
function compileStatements(statements, source) {
|
|
@@ -86,6 +289,12 @@ function compileStatements(statements, source) {
|
|
|
86
289
|
if (!normalized) {
|
|
87
290
|
continue;
|
|
88
291
|
}
|
|
292
|
+
const pathScopes = parsePathScopes(rawStatement);
|
|
293
|
+
const invocationLimitRule = parseInvocationLimitRule(rawStatement, source, pathScopes);
|
|
294
|
+
if (invocationLimitRule) {
|
|
295
|
+
rules.push(invocationLimitRule);
|
|
296
|
+
continue;
|
|
297
|
+
}
|
|
89
298
|
const requiresProhibitiveLanguage = source === 'intent';
|
|
90
299
|
if (requiresProhibitiveLanguage && !PROHIBITIVE_PATTERN.test(normalized)) {
|
|
91
300
|
continue;
|
|
@@ -96,7 +305,7 @@ function compileStatements(statements, source) {
|
|
|
96
305
|
continue;
|
|
97
306
|
}
|
|
98
307
|
for (const match of matches) {
|
|
99
|
-
rules.push(createRule(match, source, rawStatement));
|
|
308
|
+
rules.push(createRule(match, source, rawStatement, pathScopes));
|
|
100
309
|
}
|
|
101
310
|
}
|
|
102
311
|
return {
|
|
@@ -108,7 +317,15 @@ function dedupeRules(rules) {
|
|
|
108
317
|
const seen = new Set();
|
|
109
318
|
const deduped = [];
|
|
110
319
|
for (const rule of rules) {
|
|
111
|
-
const key =
|
|
320
|
+
const key = [
|
|
321
|
+
rule.id,
|
|
322
|
+
rule.statement.toLowerCase(),
|
|
323
|
+
rule.pathIncludePatterns?.join('|') || '',
|
|
324
|
+
rule.pathExcludePatterns?.join('|') || '',
|
|
325
|
+
typeof rule.minMatchesPerFile === 'number' ? String(rule.minMatchesPerFile) : '',
|
|
326
|
+
typeof rule.maxMatchesPerFile === 'number' ? String(rule.maxMatchesPerFile) : '',
|
|
327
|
+
rule.evaluationMode || '',
|
|
328
|
+
].join('::');
|
|
112
329
|
if (seen.has(key)) {
|
|
113
330
|
continue;
|
|
114
331
|
}
|
package/dist/constraints.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"constraints.js","sourceRoot":"","sources":["../src/constraints.ts"],"names":[],"mappings":";;
|
|
1
|
+
{"version":3,"file":"constraints.js","sourceRoot":"","sources":["../src/constraints.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC;AACnD,OAAO,EACL,+BAA+B,EAC/B,KAAK,kCAAkC,EACvC,KAAK,uCAAuC,EAC5C,KAAK,2BAA2B,EAChC,KAAK,6BAA6B,GACnC,MAAM,eAAe,CAAC;AACvB,OAAO,EAAmC,KAAK,2BAA2B,EAAE,MAAM,eAAe,CAAC;AAElG,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,SAAS,GAAG,OAAO,GAAG,SAAS,CAAC;IACtC,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,YAAY;IAC3B,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,YAAY,EAAE,CAAC;CACvB;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,KAAK,GAAG,QAAQ,GAAG,QAAQ,GAAG,QAAQ,CAAC;IACnD,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,YAAY,EAAE,CAAC;CACxB;AAED,MAAM,WAAW,aAAa;IAC5B,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,qBAAqB;IACpC,SAAS,EAAE,iBAAiB,EAAE,CAAC;IAC/B,YAAY,EAAE,YAAY,EAAE,CAAC;IAC7B,SAAS,CAAC,EAAE,aAAa,CAAC;IAC1B,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB,oBAAoB,CAAC,EAAE,2BAA2B,EAAE,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -24,7 +24,7 @@ function extractPlannedFilePaths(planFiles) {
|
|
|
24
24
|
}
|
|
25
25
|
return Array.from(plannedFilePaths);
|
|
26
26
|
}
|
|
27
|
-
function detectConstraintViolations(intentConstraints, policyRules, extraConstraintRules, changedFiles) {
|
|
27
|
+
function detectConstraintViolations(intentConstraints, policyRules, extraConstraintRules, changedFiles, fileContents) {
|
|
28
28
|
const compiled = (0, constraints_2.compileDeterministicConstraints)({
|
|
29
29
|
intentConstraints,
|
|
30
30
|
policyRules,
|
|
@@ -38,17 +38,77 @@ function detectConstraintViolations(intentConstraints, policyRules, extraConstra
|
|
|
38
38
|
}
|
|
39
39
|
const violations = [];
|
|
40
40
|
const seenViolations = new Set();
|
|
41
|
+
const pathMatchesRule = (rule, filePath) => {
|
|
42
|
+
const include = rule.pathIncludes || [];
|
|
43
|
+
const exclude = rule.pathExcludes || [];
|
|
44
|
+
if (include.length > 0 && !include.some((pattern) => pattern.test(filePath))) {
|
|
45
|
+
return false;
|
|
46
|
+
}
|
|
47
|
+
if (exclude.length > 0 && exclude.some((pattern) => pattern.test(filePath))) {
|
|
48
|
+
return false;
|
|
49
|
+
}
|
|
50
|
+
return true;
|
|
51
|
+
};
|
|
52
|
+
const countMatches = (pattern, input) => {
|
|
53
|
+
if (!input)
|
|
54
|
+
return 0;
|
|
55
|
+
const flags = pattern.flags.includes('g') ? pattern.flags : `${pattern.flags}g`;
|
|
56
|
+
const re = new RegExp(pattern.source, flags);
|
|
57
|
+
let total = 0;
|
|
58
|
+
for (const _match of input.matchAll(re)) {
|
|
59
|
+
total += 1;
|
|
60
|
+
}
|
|
61
|
+
return total;
|
|
62
|
+
};
|
|
41
63
|
for (const rule of rules) {
|
|
42
64
|
for (const file of changedFiles) {
|
|
65
|
+
if (!pathMatchesRule(rule, file.path)) {
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
43
68
|
if (!file.hunks || file.hunks.length === 0) {
|
|
44
69
|
continue;
|
|
45
70
|
}
|
|
71
|
+
const addedLines = file.hunks.flatMap((hunk) => hunk.lines
|
|
72
|
+
.filter((line) => line.type === 'added')
|
|
73
|
+
.map((line) => line.content));
|
|
74
|
+
if ((typeof rule.maxMatchesPerFile === 'number'
|
|
75
|
+
&& Number.isFinite(rule.maxMatchesPerFile)) || (typeof rule.minMatchesPerFile === 'number'
|
|
76
|
+
&& Number.isFinite(rule.minMatchesPerFile))) {
|
|
77
|
+
const fallbackText = addedLines.join('\n');
|
|
78
|
+
const fullContent = fileContents?.[file.path];
|
|
79
|
+
const haystack = rule.evaluationMode === 'full_file' && typeof fullContent === 'string'
|
|
80
|
+
? fullContent
|
|
81
|
+
: fallbackText;
|
|
82
|
+
const matchCount = countMatches(rule.pattern, haystack);
|
|
83
|
+
if (typeof rule.maxMatchesPerFile === 'number'
|
|
84
|
+
&& Number.isFinite(rule.maxMatchesPerFile)
|
|
85
|
+
&& matchCount > rule.maxMatchesPerFile) {
|
|
86
|
+
const violation = `${rule.matchToken} matched ${matchCount} times in ${file.path} ` +
|
|
87
|
+
`(limit ${rule.maxMatchesPerFile}, violates constraint: "${rule.displayName}")`;
|
|
88
|
+
if (!seenViolations.has(violation)) {
|
|
89
|
+
seenViolations.add(violation);
|
|
90
|
+
violations.push(violation);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
if (typeof rule.minMatchesPerFile === 'number'
|
|
94
|
+
&& Number.isFinite(rule.minMatchesPerFile)
|
|
95
|
+
&& matchCount < rule.minMatchesPerFile) {
|
|
96
|
+
const violation = `${rule.matchToken} matched ${matchCount} times in ${file.path} ` +
|
|
97
|
+
`(minimum ${rule.minMatchesPerFile}, violates constraint: "${rule.displayName}")`;
|
|
98
|
+
if (!seenViolations.has(violation)) {
|
|
99
|
+
seenViolations.add(violation);
|
|
100
|
+
violations.push(violation);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
continue;
|
|
104
|
+
}
|
|
46
105
|
for (const hunk of file.hunks) {
|
|
47
106
|
for (const line of hunk.lines) {
|
|
48
107
|
if (line.type !== 'added') {
|
|
49
108
|
continue;
|
|
50
109
|
}
|
|
51
|
-
|
|
110
|
+
const pattern = new RegExp(rule.pattern.source, rule.pattern.flags);
|
|
111
|
+
if (!pattern.test(line.content)) {
|
|
52
112
|
continue;
|
|
53
113
|
}
|
|
54
114
|
const violation = `${rule.matchToken} found in ${file.path} (violates constraint: \"${rule.displayName}\")`;
|
|
@@ -114,7 +174,7 @@ function evaluatePlanVerification(input) {
|
|
|
114
174
|
const adherenceScore = totalPlannedFiles > 0
|
|
115
175
|
? Math.round((plannedFilesModified / totalPlannedFiles) * 100)
|
|
116
176
|
: 0;
|
|
117
|
-
const constraintViolations = detectConstraintViolations(input.intentConstraints, input.policyRules, input.extraConstraintRules, normalizedChangedFiles);
|
|
177
|
+
const constraintViolations = detectConstraintViolations(input.intentConstraints, input.policyRules, input.extraConstraintRules, normalizedChangedFiles, input.fileContents);
|
|
118
178
|
const verdict = resolvePlanVerdict({
|
|
119
179
|
bloatCount,
|
|
120
180
|
adherenceScore,
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
package/src/constraints.ts
CHANGED
|
@@ -7,6 +7,13 @@ export interface DeterministicConstraintRule {
|
|
|
7
7
|
displayName: string;
|
|
8
8
|
pattern: RegExp;
|
|
9
9
|
matchToken: string;
|
|
10
|
+
pathIncludePatterns?: string[];
|
|
11
|
+
pathExcludePatterns?: string[];
|
|
12
|
+
pathIncludes?: RegExp[];
|
|
13
|
+
pathExcludes?: RegExp[];
|
|
14
|
+
minMatchesPerFile?: number;
|
|
15
|
+
maxMatchesPerFile?: number;
|
|
16
|
+
evaluationMode?: 'added_lines' | 'full_file';
|
|
10
17
|
}
|
|
11
18
|
|
|
12
19
|
export interface DeterministicConstraintCompilation {
|
|
@@ -79,6 +86,27 @@ const CONSTRAINT_TEMPLATES: ConstraintTemplate[] = [
|
|
|
79
86
|
pattern: /\b(TODO|FIXME)\b/i,
|
|
80
87
|
matchToken: 'todo/fixme',
|
|
81
88
|
},
|
|
89
|
+
{
|
|
90
|
+
id: 'no_network_calls',
|
|
91
|
+
displayName: 'No network calls',
|
|
92
|
+
triggerTokens: ['network call', 'network calls', 'http call', 'api call', 'external call'],
|
|
93
|
+
pattern: /\b(fetch\s*\(|axios\.[a-z]+\s*\(|axios\s*\(|XMLHttpRequest\b|http\.request\s*\(|https\.request\s*\(|got\s*\(|superagent\s*\()/i,
|
|
94
|
+
matchToken: 'network-call',
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
id: 'no_child_process',
|
|
98
|
+
displayName: 'No child_process execution',
|
|
99
|
+
triggerTokens: ['child_process', 'shell command', 'exec(', 'spawn('],
|
|
100
|
+
pattern: /\b(child_process|exec\s*\(|execFile\s*\(|spawn\s*\(|fork\s*\()/i,
|
|
101
|
+
matchToken: 'child_process',
|
|
102
|
+
},
|
|
103
|
+
{
|
|
104
|
+
id: 'no_innerhtml',
|
|
105
|
+
displayName: 'No innerHTML / dangerouslySetInnerHTML',
|
|
106
|
+
triggerTokens: ['innerhtml', 'dangerouslysetinnerhtml', 'dom injection'],
|
|
107
|
+
pattern: /\b(innerHTML|dangerouslySetInnerHTML)\b/i,
|
|
108
|
+
matchToken: 'innerHTML',
|
|
109
|
+
},
|
|
82
110
|
];
|
|
83
111
|
|
|
84
112
|
function splitStatements(raw: string): string[] {
|
|
@@ -95,6 +123,198 @@ function normalizeStatement(statement: string): string {
|
|
|
95
123
|
.toLowerCase();
|
|
96
124
|
}
|
|
97
125
|
|
|
126
|
+
function normalizePathScopeToken(rawValue: string): string {
|
|
127
|
+
return rawValue
|
|
128
|
+
.trim()
|
|
129
|
+
.replace(/^[`'"]+|[`'"]+$/g, '')
|
|
130
|
+
.replace(/\\/g, '/')
|
|
131
|
+
.replace(/^\.\//, '')
|
|
132
|
+
.replace(/\/{2,}/g, '/');
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function looksLikePathScope(token: string): boolean {
|
|
136
|
+
if (!token) return false;
|
|
137
|
+
if (/\s/.test(token)) return false;
|
|
138
|
+
if (token.includes('/')) return true;
|
|
139
|
+
if (token.includes('*')) return true;
|
|
140
|
+
return /\.[a-z0-9]{1,8}$/i.test(token);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function globLikeToRegex(pattern: string): RegExp {
|
|
144
|
+
const escaped = pattern
|
|
145
|
+
.split('*')
|
|
146
|
+
.map((segment) => segment.replace(/[.+^${}()|[\]\\]/g, '\\$&'))
|
|
147
|
+
.join('.*');
|
|
148
|
+
return new RegExp(`^${escaped}$`, 'i');
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
function compilePathPatterns(patterns: string[]): RegExp[] {
|
|
152
|
+
return patterns
|
|
153
|
+
.map((pattern) => {
|
|
154
|
+
try {
|
|
155
|
+
return globLikeToRegex(pattern);
|
|
156
|
+
} catch {
|
|
157
|
+
return null;
|
|
158
|
+
}
|
|
159
|
+
})
|
|
160
|
+
.filter((item): item is RegExp => item instanceof RegExp);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
function parsePathScopes(statement: string): {
|
|
164
|
+
includePatterns: string[];
|
|
165
|
+
excludePatterns: string[];
|
|
166
|
+
} {
|
|
167
|
+
const includePatterns = new Set<string>();
|
|
168
|
+
const excludePatterns = new Set<string>();
|
|
169
|
+
|
|
170
|
+
const includeRegex = /\b(?:in|within|under|inside)\s+[`'"]?([A-Za-z0-9_./*?-]+)[`'"]?/gi;
|
|
171
|
+
const excludeRegex = /\b(?:except|excluding|but\s+not\s+in|not\s+in)\s+[`'"]?([A-Za-z0-9_./*?-]+)[`'"]?/gi;
|
|
172
|
+
|
|
173
|
+
for (const match of statement.matchAll(includeRegex)) {
|
|
174
|
+
const candidate = normalizePathScopeToken(match[1] || '');
|
|
175
|
+
if (looksLikePathScope(candidate)) {
|
|
176
|
+
includePatterns.add(candidate);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
for (const match of statement.matchAll(excludeRegex)) {
|
|
180
|
+
const candidate = normalizePathScopeToken(match[1] || '');
|
|
181
|
+
if (looksLikePathScope(candidate)) {
|
|
182
|
+
excludePatterns.add(candidate);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
return {
|
|
187
|
+
includePatterns: [...includePatterns],
|
|
188
|
+
excludePatterns: [...excludePatterns],
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
function escapeRegex(value: string): string {
|
|
193
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
function parseInvocationLimitRule(
|
|
197
|
+
statement: string,
|
|
198
|
+
source: DeterministicConstraintSource,
|
|
199
|
+
pathScopes: { includePatterns: string[]; excludePatterns: string[] }
|
|
200
|
+
): DeterministicConstraintRule | null {
|
|
201
|
+
const normalized = normalizeStatement(statement);
|
|
202
|
+
|
|
203
|
+
const fnPatterns: Array<{
|
|
204
|
+
regex: RegExp;
|
|
205
|
+
fnIndex: number;
|
|
206
|
+
countIndex: number;
|
|
207
|
+
comparator: 'max' | 'min' | 'exact';
|
|
208
|
+
}> = [
|
|
209
|
+
{
|
|
210
|
+
regex: /\b([a-z_$][a-z0-9_$]*)\s+(?:function\s+)?(?:should\s+be\s+)?(?:invoked|called)\s+(?:only\s+)?(\d+)\s+times?\b/i,
|
|
211
|
+
fnIndex: 1,
|
|
212
|
+
countIndex: 2,
|
|
213
|
+
comparator: 'max',
|
|
214
|
+
},
|
|
215
|
+
{
|
|
216
|
+
regex: /\b([a-z_$][a-z0-9_$]*)\s+(?:function\s+)?(?:should\s+be\s+)?(?:invoked|called)\s+(?:at\s+most|no\s+more\s+than|maximum)\s+(\d+)\s+times?\b/i,
|
|
217
|
+
fnIndex: 1,
|
|
218
|
+
countIndex: 2,
|
|
219
|
+
comparator: 'max',
|
|
220
|
+
},
|
|
221
|
+
{
|
|
222
|
+
regex: /\b([a-z_$][a-z0-9_$]*)\s+(?:function\s+)?(?:should\s+be\s+)?(?:invoked|called)\s+(?:at\s+least|no\s+less\s+than|minimum)\s+(\d+)\s+times?\b/i,
|
|
223
|
+
fnIndex: 1,
|
|
224
|
+
countIndex: 2,
|
|
225
|
+
comparator: 'min',
|
|
226
|
+
},
|
|
227
|
+
{
|
|
228
|
+
regex: /\b([a-z_$][a-z0-9_$]*)\s+(?:function\s+)?(?:should\s+be\s+)?(?:invoked|called)\s+(?:exactly)\s+(\d+)\s+times?\b/i,
|
|
229
|
+
fnIndex: 1,
|
|
230
|
+
countIndex: 2,
|
|
231
|
+
comparator: 'exact',
|
|
232
|
+
},
|
|
233
|
+
{
|
|
234
|
+
regex: /\bonly\s+(\d+)\s+calls?\s+to\s+([a-z_$][a-z0-9_$]*)\b/i,
|
|
235
|
+
fnIndex: 2,
|
|
236
|
+
countIndex: 1,
|
|
237
|
+
comparator: 'max',
|
|
238
|
+
},
|
|
239
|
+
{
|
|
240
|
+
regex: /\bat\s+most\s+(\d+)\s+calls?\s+to\s+([a-z_$][a-z0-9_$]*)\b/i,
|
|
241
|
+
fnIndex: 2,
|
|
242
|
+
countIndex: 1,
|
|
243
|
+
comparator: 'max',
|
|
244
|
+
},
|
|
245
|
+
{
|
|
246
|
+
regex: /\bat\s+least\s+(\d+)\s+calls?\s+to\s+([a-z_$][a-z0-9_$]*)\b/i,
|
|
247
|
+
fnIndex: 2,
|
|
248
|
+
countIndex: 1,
|
|
249
|
+
comparator: 'min',
|
|
250
|
+
},
|
|
251
|
+
{
|
|
252
|
+
regex: /\bexactly\s+(\d+)\s+calls?\s+to\s+([a-z_$][a-z0-9_$]*)\b/i,
|
|
253
|
+
fnIndex: 2,
|
|
254
|
+
countIndex: 1,
|
|
255
|
+
comparator: 'exact',
|
|
256
|
+
},
|
|
257
|
+
];
|
|
258
|
+
|
|
259
|
+
let fnName: string | null = null;
|
|
260
|
+
let rawLimit: string | null = null;
|
|
261
|
+
let comparator: 'max' | 'min' | 'exact' = 'max';
|
|
262
|
+
|
|
263
|
+
for (const pattern of fnPatterns) {
|
|
264
|
+
const match = normalized.match(pattern.regex);
|
|
265
|
+
if (!match) {
|
|
266
|
+
continue;
|
|
267
|
+
}
|
|
268
|
+
fnName = match[pattern.fnIndex] || null;
|
|
269
|
+
rawLimit = match[pattern.countIndex] || null;
|
|
270
|
+
comparator = pattern.comparator;
|
|
271
|
+
break;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
if (!fnName || !rawLimit) {
|
|
275
|
+
return null;
|
|
276
|
+
}
|
|
277
|
+
const limit = Number(rawLimit);
|
|
278
|
+
if (!Number.isFinite(limit) || limit < 0) {
|
|
279
|
+
return null;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
const includePatterns = [...pathScopes.includePatterns];
|
|
283
|
+
const excludePatterns = [...pathScopes.excludePatterns];
|
|
284
|
+
const displaySuffix =
|
|
285
|
+
comparator === 'exact'
|
|
286
|
+
? `exactly ${limit}`
|
|
287
|
+
: comparator === 'min'
|
|
288
|
+
? `at least ${limit}`
|
|
289
|
+
: `at most ${limit}`;
|
|
290
|
+
const minMatches = comparator === 'min' || comparator === 'exact' ? limit : undefined;
|
|
291
|
+
const maxMatches = comparator === 'max' || comparator === 'exact' ? limit : undefined;
|
|
292
|
+
|
|
293
|
+
return {
|
|
294
|
+
id: `${source}:${comparator}_invocations_${fnName.toLowerCase()}`,
|
|
295
|
+
source,
|
|
296
|
+
statement,
|
|
297
|
+
displayName: `${fnName}() invocation limit (${displaySuffix})`,
|
|
298
|
+
pattern: new RegExp(`(?<!function\\s)\\b${escapeRegex(fnName)}\\s*\\(`, 'i'),
|
|
299
|
+
matchToken: `${fnName}(`,
|
|
300
|
+
...(typeof minMatches === 'number' ? { minMatchesPerFile: minMatches } : {}),
|
|
301
|
+
...(typeof maxMatches === 'number' ? { maxMatchesPerFile: maxMatches } : {}),
|
|
302
|
+
evaluationMode: 'full_file',
|
|
303
|
+
...(includePatterns.length > 0
|
|
304
|
+
? {
|
|
305
|
+
pathIncludePatterns: includePatterns,
|
|
306
|
+
pathIncludes: compilePathPatterns(includePatterns),
|
|
307
|
+
}
|
|
308
|
+
: {}),
|
|
309
|
+
...(excludePatterns.length > 0
|
|
310
|
+
? {
|
|
311
|
+
pathExcludePatterns: excludePatterns,
|
|
312
|
+
pathExcludes: compilePathPatterns(excludePatterns),
|
|
313
|
+
}
|
|
314
|
+
: {}),
|
|
315
|
+
};
|
|
316
|
+
}
|
|
317
|
+
|
|
98
318
|
function statementMatchesTemplate(normalizedStatement: string, template: ConstraintTemplate): boolean {
|
|
99
319
|
return template.triggerTokens.some((token) => normalizedStatement.includes(token));
|
|
100
320
|
}
|
|
@@ -102,8 +322,11 @@ function statementMatchesTemplate(normalizedStatement: string, template: Constra
|
|
|
102
322
|
function createRule(
|
|
103
323
|
template: ConstraintTemplate,
|
|
104
324
|
source: DeterministicConstraintSource,
|
|
105
|
-
statement: string
|
|
325
|
+
statement: string,
|
|
326
|
+
pathScopes: { includePatterns: string[]; excludePatterns: string[] }
|
|
106
327
|
): DeterministicConstraintRule {
|
|
328
|
+
const includePatterns = [...pathScopes.includePatterns];
|
|
329
|
+
const excludePatterns = [...pathScopes.excludePatterns];
|
|
107
330
|
return {
|
|
108
331
|
id: `${source}:${template.id}`,
|
|
109
332
|
source,
|
|
@@ -111,6 +334,18 @@ function createRule(
|
|
|
111
334
|
displayName: template.displayName,
|
|
112
335
|
pattern: template.pattern,
|
|
113
336
|
matchToken: template.matchToken,
|
|
337
|
+
...(includePatterns.length > 0
|
|
338
|
+
? {
|
|
339
|
+
pathIncludePatterns: includePatterns,
|
|
340
|
+
pathIncludes: compilePathPatterns(includePatterns),
|
|
341
|
+
}
|
|
342
|
+
: {}),
|
|
343
|
+
...(excludePatterns.length > 0
|
|
344
|
+
? {
|
|
345
|
+
pathExcludePatterns: excludePatterns,
|
|
346
|
+
pathExcludes: compilePathPatterns(excludePatterns),
|
|
347
|
+
}
|
|
348
|
+
: {}),
|
|
114
349
|
};
|
|
115
350
|
}
|
|
116
351
|
|
|
@@ -126,6 +361,12 @@ function compileStatements(
|
|
|
126
361
|
if (!normalized) {
|
|
127
362
|
continue;
|
|
128
363
|
}
|
|
364
|
+
const pathScopes = parsePathScopes(rawStatement);
|
|
365
|
+
const invocationLimitRule = parseInvocationLimitRule(rawStatement, source, pathScopes);
|
|
366
|
+
if (invocationLimitRule) {
|
|
367
|
+
rules.push(invocationLimitRule);
|
|
368
|
+
continue;
|
|
369
|
+
}
|
|
129
370
|
|
|
130
371
|
const requiresProhibitiveLanguage = source === 'intent';
|
|
131
372
|
if (requiresProhibitiveLanguage && !PROHIBITIVE_PATTERN.test(normalized)) {
|
|
@@ -139,7 +380,7 @@ function compileStatements(
|
|
|
139
380
|
}
|
|
140
381
|
|
|
141
382
|
for (const match of matches) {
|
|
142
|
-
rules.push(createRule(match, source, rawStatement));
|
|
383
|
+
rules.push(createRule(match, source, rawStatement, pathScopes));
|
|
143
384
|
}
|
|
144
385
|
}
|
|
145
386
|
|
|
@@ -154,7 +395,15 @@ function dedupeRules(rules: DeterministicConstraintRule[]): DeterministicConstra
|
|
|
154
395
|
const deduped: DeterministicConstraintRule[] = [];
|
|
155
396
|
|
|
156
397
|
for (const rule of rules) {
|
|
157
|
-
const key =
|
|
398
|
+
const key = [
|
|
399
|
+
rule.id,
|
|
400
|
+
rule.statement.toLowerCase(),
|
|
401
|
+
rule.pathIncludePatterns?.join('|') || '',
|
|
402
|
+
rule.pathExcludePatterns?.join('|') || '',
|
|
403
|
+
typeof rule.minMatchesPerFile === 'number' ? String(rule.minMatchesPerFile) : '',
|
|
404
|
+
typeof rule.maxMatchesPerFile === 'number' ? String(rule.maxMatchesPerFile) : '',
|
|
405
|
+
rule.evaluationMode || '',
|
|
406
|
+
].join('::');
|
|
158
407
|
if (seen.has(key)) {
|
|
159
408
|
continue;
|
|
160
409
|
}
|
package/src/index.test.ts
CHANGED
|
@@ -157,6 +157,16 @@ test('compileDeterministicConstraints builds rules from intent and policy text',
|
|
|
157
157
|
assert.ok(ruleIds.includes('policy:no_process_env'));
|
|
158
158
|
});
|
|
159
159
|
|
|
160
|
+
test('compileDeterministicConstraints supports path-scoped rules', () => {
|
|
161
|
+
const compiled = compileDeterministicConstraints({
|
|
162
|
+
policyRules: ['No console.log in src/server/** except src/server/tests/**'],
|
|
163
|
+
});
|
|
164
|
+
const scopedRule = compiled.rules.find((rule) => rule.id === 'policy:no_console_log');
|
|
165
|
+
assert.ok(scopedRule, 'expected path-scoped console.log rule');
|
|
166
|
+
assert.deepEqual(scopedRule?.pathIncludePatterns, ['src/server/**']);
|
|
167
|
+
assert.deepEqual(scopedRule?.pathExcludePatterns, ['src/server/tests/**']);
|
|
168
|
+
});
|
|
169
|
+
|
|
160
170
|
test('evaluatePlanVerification enforces compiled policy rules deterministically', () => {
|
|
161
171
|
const result = evaluatePlanVerification({
|
|
162
172
|
planFiles: SAMPLE_PLAN,
|
|
@@ -190,6 +200,174 @@ test('evaluatePlanVerification enforces compiled policy rules deterministically'
|
|
|
190
200
|
assert.match(result.constraintViolations[0], /console\.log/i);
|
|
191
201
|
});
|
|
192
202
|
|
|
203
|
+
test('evaluatePlanVerification enforces path-scoped exclusions to reduce false positives', () => {
|
|
204
|
+
const result = evaluatePlanVerification({
|
|
205
|
+
planFiles: SAMPLE_PLAN,
|
|
206
|
+
changedFiles: [
|
|
207
|
+
{
|
|
208
|
+
path: 'src/server/app.ts',
|
|
209
|
+
changeType: 'modify',
|
|
210
|
+
added: 1,
|
|
211
|
+
removed: 0,
|
|
212
|
+
hunks: [
|
|
213
|
+
{
|
|
214
|
+
oldStart: 1,
|
|
215
|
+
oldLines: 0,
|
|
216
|
+
newStart: 1,
|
|
217
|
+
newLines: 1,
|
|
218
|
+
lines: [
|
|
219
|
+
{
|
|
220
|
+
type: 'added',
|
|
221
|
+
content: 'console.log("server");',
|
|
222
|
+
},
|
|
223
|
+
],
|
|
224
|
+
},
|
|
225
|
+
],
|
|
226
|
+
},
|
|
227
|
+
{
|
|
228
|
+
path: 'src/server/tests/app.test.ts',
|
|
229
|
+
changeType: 'modify',
|
|
230
|
+
added: 1,
|
|
231
|
+
removed: 0,
|
|
232
|
+
hunks: [
|
|
233
|
+
{
|
|
234
|
+
oldStart: 1,
|
|
235
|
+
oldLines: 0,
|
|
236
|
+
newStart: 1,
|
|
237
|
+
newLines: 1,
|
|
238
|
+
lines: [
|
|
239
|
+
{
|
|
240
|
+
type: 'added',
|
|
241
|
+
content: 'console.log("test");',
|
|
242
|
+
},
|
|
243
|
+
],
|
|
244
|
+
},
|
|
245
|
+
],
|
|
246
|
+
},
|
|
247
|
+
],
|
|
248
|
+
policyRules: ['No console.log in src/server/** except src/server/tests/**'],
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
assert.equal(result.constraintViolations.length, 1);
|
|
252
|
+
assert.match(result.constraintViolations[0], /src\/server\/app\.ts/i);
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
test('evaluatePlanVerification supports deterministic invocation limits with full-file context', () => {
|
|
256
|
+
const result = evaluatePlanVerification({
|
|
257
|
+
planFiles: SAMPLE_PLAN,
|
|
258
|
+
changedFiles: [
|
|
259
|
+
{
|
|
260
|
+
path: 'src/a.ts',
|
|
261
|
+
changeType: 'modify',
|
|
262
|
+
added: 1,
|
|
263
|
+
removed: 0,
|
|
264
|
+
hunks: [
|
|
265
|
+
{
|
|
266
|
+
oldStart: 1,
|
|
267
|
+
oldLines: 0,
|
|
268
|
+
newStart: 1,
|
|
269
|
+
newLines: 1,
|
|
270
|
+
lines: [
|
|
271
|
+
{
|
|
272
|
+
type: 'added',
|
|
273
|
+
content: 'const marker = true;',
|
|
274
|
+
},
|
|
275
|
+
],
|
|
276
|
+
},
|
|
277
|
+
],
|
|
278
|
+
},
|
|
279
|
+
],
|
|
280
|
+
intentConstraints: 'foo invoked only 1 times in src/a.ts',
|
|
281
|
+
fileContents: {
|
|
282
|
+
'src/a.ts': 'foo();\nfoo();\n',
|
|
283
|
+
},
|
|
284
|
+
});
|
|
285
|
+
|
|
286
|
+
assert.equal(result.verdict, 'FAIL');
|
|
287
|
+
assert.equal(result.constraintViolations.length, 1);
|
|
288
|
+
assert.match(result.constraintViolations[0], /limit 1/i);
|
|
289
|
+
});
|
|
290
|
+
|
|
291
|
+
test('evaluatePlanVerification supports at-least invocation constraints', () => {
|
|
292
|
+
const result = evaluatePlanVerification({
|
|
293
|
+
planFiles: SAMPLE_PLAN,
|
|
294
|
+
changedFiles: [
|
|
295
|
+
{
|
|
296
|
+
path: 'src/a.ts',
|
|
297
|
+
changeType: 'modify',
|
|
298
|
+
added: 1,
|
|
299
|
+
removed: 0,
|
|
300
|
+
hunks: [
|
|
301
|
+
{
|
|
302
|
+
oldStart: 1,
|
|
303
|
+
oldLines: 0,
|
|
304
|
+
newStart: 1,
|
|
305
|
+
newLines: 1,
|
|
306
|
+
lines: [
|
|
307
|
+
{
|
|
308
|
+
type: 'added',
|
|
309
|
+
content: 'const marker = true;',
|
|
310
|
+
},
|
|
311
|
+
],
|
|
312
|
+
},
|
|
313
|
+
],
|
|
314
|
+
},
|
|
315
|
+
],
|
|
316
|
+
intentConstraints: 'foo should be called at least 2 times in src/a.ts',
|
|
317
|
+
fileContents: {
|
|
318
|
+
'src/a.ts': 'foo();\n',
|
|
319
|
+
},
|
|
320
|
+
});
|
|
321
|
+
|
|
322
|
+
assert.equal(result.verdict, 'FAIL');
|
|
323
|
+
assert.equal(result.constraintViolations.length, 1);
|
|
324
|
+
assert.match(result.constraintViolations[0], /minimum 2/i);
|
|
325
|
+
});
|
|
326
|
+
|
|
327
|
+
test('compileDeterministicConstraints supports exact invocation constraints', () => {
|
|
328
|
+
const compiled = compileDeterministicConstraints({
|
|
329
|
+
intentConstraints: 'bar should be called exactly 3 times in src/a.ts',
|
|
330
|
+
});
|
|
331
|
+
|
|
332
|
+
const rule = compiled.rules.find((item) => item.id.includes('exact_invocations_bar'));
|
|
333
|
+
assert.ok(rule, 'expected exact invocation rule');
|
|
334
|
+
assert.equal(rule?.minMatchesPerFile, 3);
|
|
335
|
+
assert.equal(rule?.maxMatchesPerFile, 3);
|
|
336
|
+
});
|
|
337
|
+
|
|
338
|
+
test('evaluatePlanVerification detects network-call invariants deterministically', () => {
|
|
339
|
+
const result = evaluatePlanVerification({
|
|
340
|
+
planFiles: SAMPLE_PLAN,
|
|
341
|
+
changedFiles: [
|
|
342
|
+
{
|
|
343
|
+
path: 'src/a.ts',
|
|
344
|
+
changeType: 'modify',
|
|
345
|
+
added: 1,
|
|
346
|
+
removed: 0,
|
|
347
|
+
hunks: [
|
|
348
|
+
{
|
|
349
|
+
oldStart: 1,
|
|
350
|
+
oldLines: 0,
|
|
351
|
+
newStart: 1,
|
|
352
|
+
newLines: 1,
|
|
353
|
+
lines: [
|
|
354
|
+
{
|
|
355
|
+
type: 'added',
|
|
356
|
+
content: 'await fetch("https://api.example.com");',
|
|
357
|
+
},
|
|
358
|
+
],
|
|
359
|
+
},
|
|
360
|
+
],
|
|
361
|
+
},
|
|
362
|
+
],
|
|
363
|
+
policyRules: ['No network calls in committed code'],
|
|
364
|
+
});
|
|
365
|
+
|
|
366
|
+
assert.equal(result.verdict, 'FAIL');
|
|
367
|
+
assert.equal(result.constraintViolations.length, 1);
|
|
368
|
+
assert.match(result.constraintViolations[0], /network-call/i);
|
|
369
|
+
});
|
|
370
|
+
|
|
193
371
|
test('buildPlanVerificationMessage handles incomplete 0/0 plans', () => {
|
|
194
372
|
const message = buildPlanVerificationMessage({
|
|
195
373
|
constraintViolations: [],
|
package/src/index.ts
CHANGED
|
@@ -49,6 +49,7 @@ export interface PlanVerificationInput {
|
|
|
49
49
|
intentConstraints?: string;
|
|
50
50
|
policyRules?: string[];
|
|
51
51
|
extraConstraintRules?: DeterministicConstraintRule[];
|
|
52
|
+
fileContents?: Record<string, string>;
|
|
52
53
|
}
|
|
53
54
|
|
|
54
55
|
export interface PlanDiffSummary {
|
|
@@ -105,7 +106,8 @@ function detectConstraintViolations(
|
|
|
105
106
|
intentConstraints: string | undefined,
|
|
106
107
|
policyRules: string[] | undefined,
|
|
107
108
|
extraConstraintRules: DeterministicConstraintRule[] | undefined,
|
|
108
|
-
changedFiles: PlanDiffFile[]
|
|
109
|
+
changedFiles: PlanDiffFile[],
|
|
110
|
+
fileContents?: Record<string, string>
|
|
109
111
|
): string[] {
|
|
110
112
|
const compiled = compileDeterministicConstraints({
|
|
111
113
|
intentConstraints,
|
|
@@ -124,17 +126,96 @@ function detectConstraintViolations(
|
|
|
124
126
|
const violations: string[] = [];
|
|
125
127
|
const seenViolations = new Set<string>();
|
|
126
128
|
|
|
129
|
+
const pathMatchesRule = (rule: DeterministicConstraintRule, filePath: string): boolean => {
|
|
130
|
+
const include = rule.pathIncludes || [];
|
|
131
|
+
const exclude = rule.pathExcludes || [];
|
|
132
|
+
if (include.length > 0 && !include.some((pattern) => pattern.test(filePath))) {
|
|
133
|
+
return false;
|
|
134
|
+
}
|
|
135
|
+
if (exclude.length > 0 && exclude.some((pattern) => pattern.test(filePath))) {
|
|
136
|
+
return false;
|
|
137
|
+
}
|
|
138
|
+
return true;
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
const countMatches = (pattern: RegExp, input: string): number => {
|
|
142
|
+
if (!input) return 0;
|
|
143
|
+
const flags = pattern.flags.includes('g') ? pattern.flags : `${pattern.flags}g`;
|
|
144
|
+
const re = new RegExp(pattern.source, flags);
|
|
145
|
+
let total = 0;
|
|
146
|
+
for (const _match of input.matchAll(re)) {
|
|
147
|
+
total += 1;
|
|
148
|
+
}
|
|
149
|
+
return total;
|
|
150
|
+
};
|
|
151
|
+
|
|
127
152
|
for (const rule of rules) {
|
|
128
153
|
for (const file of changedFiles) {
|
|
154
|
+
if (!pathMatchesRule(rule, file.path)) {
|
|
155
|
+
continue;
|
|
156
|
+
}
|
|
129
157
|
if (!file.hunks || file.hunks.length === 0) {
|
|
130
158
|
continue;
|
|
131
159
|
}
|
|
160
|
+
|
|
161
|
+
const addedLines = file.hunks.flatMap((hunk) =>
|
|
162
|
+
hunk.lines
|
|
163
|
+
.filter((line) => line.type === 'added')
|
|
164
|
+
.map((line) => line.content)
|
|
165
|
+
);
|
|
166
|
+
|
|
167
|
+
if (
|
|
168
|
+
(
|
|
169
|
+
typeof rule.maxMatchesPerFile === 'number'
|
|
170
|
+
&& Number.isFinite(rule.maxMatchesPerFile)
|
|
171
|
+
) || (
|
|
172
|
+
typeof rule.minMatchesPerFile === 'number'
|
|
173
|
+
&& Number.isFinite(rule.minMatchesPerFile)
|
|
174
|
+
)
|
|
175
|
+
) {
|
|
176
|
+
const fallbackText = addedLines.join('\n');
|
|
177
|
+
const fullContent = fileContents?.[file.path];
|
|
178
|
+
const haystack =
|
|
179
|
+
rule.evaluationMode === 'full_file' && typeof fullContent === 'string'
|
|
180
|
+
? fullContent
|
|
181
|
+
: fallbackText;
|
|
182
|
+
const matchCount = countMatches(rule.pattern, haystack);
|
|
183
|
+
if (
|
|
184
|
+
typeof rule.maxMatchesPerFile === 'number'
|
|
185
|
+
&& Number.isFinite(rule.maxMatchesPerFile)
|
|
186
|
+
&& matchCount > rule.maxMatchesPerFile
|
|
187
|
+
) {
|
|
188
|
+
const violation =
|
|
189
|
+
`${rule.matchToken} matched ${matchCount} times in ${file.path} ` +
|
|
190
|
+
`(limit ${rule.maxMatchesPerFile}, violates constraint: "${rule.displayName}")`;
|
|
191
|
+
if (!seenViolations.has(violation)) {
|
|
192
|
+
seenViolations.add(violation);
|
|
193
|
+
violations.push(violation);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
if (
|
|
197
|
+
typeof rule.minMatchesPerFile === 'number'
|
|
198
|
+
&& Number.isFinite(rule.minMatchesPerFile)
|
|
199
|
+
&& matchCount < rule.minMatchesPerFile
|
|
200
|
+
) {
|
|
201
|
+
const violation =
|
|
202
|
+
`${rule.matchToken} matched ${matchCount} times in ${file.path} ` +
|
|
203
|
+
`(minimum ${rule.minMatchesPerFile}, violates constraint: "${rule.displayName}")`;
|
|
204
|
+
if (!seenViolations.has(violation)) {
|
|
205
|
+
seenViolations.add(violation);
|
|
206
|
+
violations.push(violation);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
continue;
|
|
210
|
+
}
|
|
211
|
+
|
|
132
212
|
for (const hunk of file.hunks) {
|
|
133
213
|
for (const line of hunk.lines) {
|
|
134
214
|
if (line.type !== 'added') {
|
|
135
215
|
continue;
|
|
136
216
|
}
|
|
137
|
-
|
|
217
|
+
const pattern = new RegExp(rule.pattern.source, rule.pattern.flags);
|
|
218
|
+
if (!pattern.test(line.content)) {
|
|
138
219
|
continue;
|
|
139
220
|
}
|
|
140
221
|
const violation = `${rule.matchToken} found in ${file.path} (violates constraint: \"${rule.displayName}\")`;
|
|
@@ -234,7 +315,8 @@ export function evaluatePlanVerification(input: PlanVerificationInput): PlanVeri
|
|
|
234
315
|
input.intentConstraints,
|
|
235
316
|
input.policyRules,
|
|
236
317
|
input.extraConstraintRules,
|
|
237
|
-
normalizedChangedFiles
|
|
318
|
+
normalizedChangedFiles,
|
|
319
|
+
input.fileContents
|
|
238
320
|
);
|
|
239
321
|
const verdict = resolvePlanVerdict({
|
|
240
322
|
bloatCount,
|