nexus-agents 2.91.0 → 2.92.0
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/{chunk-PLFKZUVT.js → chunk-HPV2D4HN.js} +7 -5
- package/dist/{chunk-PLFKZUVT.js.map → chunk-HPV2D4HN.js.map} +1 -1
- package/dist/{chunk-6IV43POQ.js → chunk-L6JZCAFH.js} +96 -624
- package/dist/chunk-L6JZCAFH.js.map +1 -0
- package/dist/{chunk-DH4V6S5K.js → chunk-LOBVTRQQ.js} +3 -3
- package/dist/chunk-NR4NSTJH.js +547 -0
- package/dist/chunk-NR4NSTJH.js.map +1 -0
- package/dist/{chunk-3RZWLQSC.js → chunk-RS6RGSYG.js} +31 -2
- package/dist/chunk-RS6RGSYG.js.map +1 -0
- package/dist/{chunk-X2M7OF27.js → chunk-S4UF577T.js} +3 -1
- package/dist/chunk-S4UF577T.js.map +1 -0
- package/dist/{chunk-W7YQOXCJ.js → chunk-VW2LBC7B.js} +2 -2
- package/dist/cli.js +58 -18
- package/dist/cli.js.map +1 -1
- package/dist/index.js +19 -17
- package/dist/index.js.map +1 -1
- package/dist/{issue-triage-CDSHKFPP.js → issue-triage-FVZOVIRX.js} +3 -2
- package/dist/{pr-reviewer-helpers-WYPUYQ2U.js → pr-reviewer-helpers-7D2W5HTO.js} +10 -3
- package/dist/{setup-command-B5QPHCKY.js → setup-command-UN3XDJ2H.js} +3 -3
- package/package.json +1 -1
- package/dist/chunk-3RZWLQSC.js.map +0 -1
- package/dist/chunk-6IV43POQ.js.map +0 -1
- package/dist/chunk-X2M7OF27.js.map +0 -1
- /package/dist/{chunk-DH4V6S5K.js.map → chunk-LOBVTRQQ.js.map} +0 -0
- /package/dist/{chunk-W7YQOXCJ.js.map → chunk-VW2LBC7B.js.map} +0 -0
- /package/dist/{issue-triage-CDSHKFPP.js.map → issue-triage-FVZOVIRX.js.map} +0 -0
- /package/dist/{pr-reviewer-helpers-WYPUYQ2U.js.map → pr-reviewer-helpers-7D2W5HTO.js.map} +0 -0
- /package/dist/{setup-command-B5QPHCKY.js.map → setup-command-UN3XDJ2H.js.map} +0 -0
|
@@ -3,332 +3,22 @@ import {
|
|
|
3
3
|
ScmError
|
|
4
4
|
} from "./chunk-EFHZHCF2.js";
|
|
5
5
|
import {
|
|
6
|
-
|
|
6
|
+
ROLE_DEFAULT_TRUST,
|
|
7
|
+
ReputationCache,
|
|
8
|
+
TRUST_TIER_NUMERIC,
|
|
9
|
+
TrustTierSchema,
|
|
10
|
+
assessReputation,
|
|
11
|
+
gateWithReputation,
|
|
12
|
+
resolveReputationGatingMode,
|
|
13
|
+
sanitizeInput
|
|
14
|
+
} from "./chunk-NR4NSTJH.js";
|
|
15
|
+
import {
|
|
7
16
|
createLogger,
|
|
8
17
|
err,
|
|
9
18
|
getTimeProvider,
|
|
10
19
|
ok
|
|
11
20
|
} from "./chunk-W5I6L4UT.js";
|
|
12
21
|
|
|
13
|
-
// src/security/trust-types.ts
|
|
14
|
-
import { z } from "zod";
|
|
15
|
-
var TrustTierSchema = z.enum(["1", "2", "3", "4"]);
|
|
16
|
-
var TRUST_TIER_NUMERIC = {
|
|
17
|
-
"1": 1,
|
|
18
|
-
"2": 2,
|
|
19
|
-
"3": 3,
|
|
20
|
-
"4": 4
|
|
21
|
-
};
|
|
22
|
-
var GitHubUserRoleSchema = z.enum([
|
|
23
|
-
"owner",
|
|
24
|
-
"maintainer",
|
|
25
|
-
"collaborator",
|
|
26
|
-
"contributor",
|
|
27
|
-
"member",
|
|
28
|
-
"unknown"
|
|
29
|
-
]);
|
|
30
|
-
var ROLE_DEFAULT_TRUST = {
|
|
31
|
-
owner: "1",
|
|
32
|
-
maintainer: "1",
|
|
33
|
-
collaborator: "2",
|
|
34
|
-
contributor: "2",
|
|
35
|
-
member: "3",
|
|
36
|
-
unknown: "3"
|
|
37
|
-
};
|
|
38
|
-
var InjectionFlagSchema = z.enum([
|
|
39
|
-
"authority_claim",
|
|
40
|
-
"instruction_pattern",
|
|
41
|
-
"system_prompt_manipulation",
|
|
42
|
-
"hidden_content",
|
|
43
|
-
"urgency_manipulation",
|
|
44
|
-
"fake_conversation",
|
|
45
|
-
"base64_encoded",
|
|
46
|
-
"external_link_instruction"
|
|
47
|
-
]);
|
|
48
|
-
var StrippedElementSchema = z.object({
|
|
49
|
-
/** Type of element stripped. */
|
|
50
|
-
tag: z.string().min(1),
|
|
51
|
-
/** Reason for stripping. */
|
|
52
|
-
reason: z.string().min(1),
|
|
53
|
-
/** Start index in original content. */
|
|
54
|
-
startIndex: z.number().int().nonnegative(),
|
|
55
|
-
/** Length of stripped content. */
|
|
56
|
-
length: z.number().int().positive()
|
|
57
|
-
});
|
|
58
|
-
var SanitizedInputSchema = z.object({
|
|
59
|
-
/** Sanitized content with dangerous elements removed. */
|
|
60
|
-
content: z.string(),
|
|
61
|
-
/** Original content before sanitization (for audit). */
|
|
62
|
-
originalLength: z.number().int().nonnegative(),
|
|
63
|
-
/** Assigned trust tier based on user role and content analysis. */
|
|
64
|
-
trustTier: TrustTierSchema,
|
|
65
|
-
/** GitHub user role of the input source. */
|
|
66
|
-
userRole: GitHubUserRoleSchema,
|
|
67
|
-
/** Injection patterns detected in content. */
|
|
68
|
-
injectionFlags: z.array(InjectionFlagSchema),
|
|
69
|
-
/** Elements stripped during sanitization (audit trail). */
|
|
70
|
-
strippedElements: z.array(StrippedElementSchema),
|
|
71
|
-
/** Whether any dangerous content was detected and stripped. */
|
|
72
|
-
wasModified: z.boolean(),
|
|
73
|
-
/** Timestamp of sanitization (ISO 8601). */
|
|
74
|
-
sanitizedAt: z.iso.datetime()
|
|
75
|
-
});
|
|
76
|
-
var SanitizerConfigSchema = z.object({
|
|
77
|
-
/** GitHub usernames that are always Tier 1 (allowlisted maintainers). */
|
|
78
|
-
allowlistedMaintainers: z.array(z.string().min(1)).default([]),
|
|
79
|
-
/** Whether to fail open (log only) or fail closed (block). Phase 1 = open. */
|
|
80
|
-
failOpen: z.boolean().default(true),
|
|
81
|
-
/** Maximum input length before truncation. */
|
|
82
|
-
maxInputLength: z.number().int().positive().default(5e4)
|
|
83
|
-
});
|
|
84
|
-
|
|
85
|
-
// src/security/input-sanitizer.ts
|
|
86
|
-
var DANGEROUS_HTML_PATTERN = /<(picture|source|img)\b[^>]*>[\s\S]*?<\/\1>|<(picture|source|img)\b[^>]*\/?>/gi;
|
|
87
|
-
var XML_INJECTION_PATTERN = /<\/?(system|human|assistant|instructions|user|prompt|context|tool_use|tool_result)\b[^>]*>/gi;
|
|
88
|
-
var HTML_COMMENT_PATTERN = /<!--[\s\S]*?-->/g;
|
|
89
|
-
var INJECTION_PATTERNS = [
|
|
90
|
-
{
|
|
91
|
-
flag: "authority_claim",
|
|
92
|
-
pattern: /\b(as (?:a|the) (?:maintainer|admin|owner|security lead|repo owner|developer))\b/i
|
|
93
|
-
},
|
|
94
|
-
{
|
|
95
|
-
flag: "authority_claim",
|
|
96
|
-
pattern: /\b(i(?:'m| am) the (?:repo |project )?(?:owner|maintainer|admin))\b/i
|
|
97
|
-
},
|
|
98
|
-
{
|
|
99
|
-
flag: "instruction_pattern",
|
|
100
|
-
pattern: /\b(please (?:close|merge|label|mark|apply|delete|remove|approve|reject))\b/i
|
|
101
|
-
},
|
|
102
|
-
{
|
|
103
|
-
flag: "instruction_pattern",
|
|
104
|
-
pattern: /\b(you (?:should|must|need to) (?:close|merge|label|apply|delete))\b/i
|
|
105
|
-
},
|
|
106
|
-
{
|
|
107
|
-
flag: "system_prompt_manipulation",
|
|
108
|
-
pattern: /\b(ignore (?:all )?previous (?:instructions|rules|prompts))\b/i
|
|
109
|
-
},
|
|
110
|
-
{
|
|
111
|
-
flag: "system_prompt_manipulation",
|
|
112
|
-
pattern: /\b(forget (?:your |all )?(?:instructions|rules|safety))\b/i
|
|
113
|
-
},
|
|
114
|
-
{
|
|
115
|
-
flag: "system_prompt_manipulation",
|
|
116
|
-
pattern: /\b(new (?:instructions|rules|system prompt|directives))\b/i
|
|
117
|
-
},
|
|
118
|
-
{
|
|
119
|
-
flag: "urgency_manipulation",
|
|
120
|
-
pattern: /\b(critical|emergency|urgent|must act now|immediately|time[- ]?sensitive)\b/i
|
|
121
|
-
},
|
|
122
|
-
{
|
|
123
|
-
flag: "fake_conversation",
|
|
124
|
-
pattern: /<(?:assistant|human|user|system)>/i
|
|
125
|
-
},
|
|
126
|
-
// base64_encoded is detected separately by `looksLikeBase64Payload` below.
|
|
127
|
-
// The lookahead-based regex it replaced exhibited catastrophic backtracking
|
|
128
|
-
// on long hex-only inputs (#2191) — see `looksLikeBase64Payload` for the
|
|
129
|
-
// two-phase rewrite that preserves the #1811 SHA-hash false-positive guard.
|
|
130
|
-
{
|
|
131
|
-
flag: "external_link_instruction",
|
|
132
|
-
pattern: /(?:apply|run|execute|install)\s+(?:this\s+)?(?:from\s+)?https?:\/\//i
|
|
133
|
-
}
|
|
134
|
-
];
|
|
135
|
-
var BASE64_RUN_GLOBAL = /[A-Za-z0-9+/]{40,}={0,2}/g;
|
|
136
|
-
var BASE64_DISCRIMINATOR = /[g-zG-Z+/=]/;
|
|
137
|
-
function looksLikeBase64Payload(content) {
|
|
138
|
-
for (const match of content.matchAll(BASE64_RUN_GLOBAL)) {
|
|
139
|
-
if (BASE64_DISCRIMINATOR.test(match[0])) return true;
|
|
140
|
-
}
|
|
141
|
-
return false;
|
|
142
|
-
}
|
|
143
|
-
var DANGEROUS_TAG_NAMES = "picture|source|img|system|human|assistant|instructions|user|prompt|context|tool_use|tool_result";
|
|
144
|
-
var ENCODED_DANGEROUS_TAG_PATTERN = new RegExp(
|
|
145
|
-
`&(?:lt|#0*60|#x0*3c);\\s*\\/?\\s*(?:${DANGEROUS_TAG_NAMES})\\b`,
|
|
146
|
-
"i"
|
|
147
|
-
);
|
|
148
|
-
function decodeEntities(content) {
|
|
149
|
-
return content.replace(/&#(\d+);/g, (_match, dec) => {
|
|
150
|
-
const code = Number.parseInt(dec, 10);
|
|
151
|
-
return Number.isFinite(code) ? String.fromCodePoint(code) : _match;
|
|
152
|
-
}).replace(/&#x([0-9a-f]+);/gi, (_match, hex) => {
|
|
153
|
-
const code = Number.parseInt(hex, 16);
|
|
154
|
-
return Number.isFinite(code) ? String.fromCodePoint(code) : _match;
|
|
155
|
-
}).replace(/</gi, "<").replace(/>/gi, ">").replace(/"/gi, '"').replace(/'/gi, "'").replace(/&/gi, "&");
|
|
156
|
-
}
|
|
157
|
-
function applyEntityEvasionDefense(content) {
|
|
158
|
-
if (!ENCODED_DANGEROUS_TAG_PATTERN.test(content)) {
|
|
159
|
-
return { cleaned: content, stripped: [] };
|
|
160
|
-
}
|
|
161
|
-
const decoded = decodeEntities(content);
|
|
162
|
-
return {
|
|
163
|
-
cleaned: decoded,
|
|
164
|
-
stripped: [
|
|
165
|
-
{
|
|
166
|
-
tag: "&\u2026;",
|
|
167
|
-
reason: "HTML entity-encoded dangerous tag decoded for stripping (CWE-79)",
|
|
168
|
-
startIndex: 0,
|
|
169
|
-
length: content.length
|
|
170
|
-
}
|
|
171
|
-
]
|
|
172
|
-
};
|
|
173
|
-
}
|
|
174
|
-
function stripDangerousHtml(content) {
|
|
175
|
-
const stripped = [];
|
|
176
|
-
let cleaned = content;
|
|
177
|
-
const MAX_PASSES = 5;
|
|
178
|
-
for (let pass = 0; pass < MAX_PASSES; pass++) {
|
|
179
|
-
DANGEROUS_HTML_PATTERN.lastIndex = 0;
|
|
180
|
-
if (!DANGEROUS_HTML_PATTERN.test(cleaned)) break;
|
|
181
|
-
cleaned = cleaned.replace(DANGEROUS_HTML_PATTERN, (match, _g1, _g2, offset) => {
|
|
182
|
-
stripped.push({
|
|
183
|
-
tag: match.slice(0, 30) + (match.length > 30 ? "..." : ""),
|
|
184
|
-
reason: "Dangerous HTML tag (Trail of Bits injection vector)",
|
|
185
|
-
startIndex: offset,
|
|
186
|
-
length: match.length
|
|
187
|
-
});
|
|
188
|
-
return "";
|
|
189
|
-
});
|
|
190
|
-
}
|
|
191
|
-
return { cleaned, stripped };
|
|
192
|
-
}
|
|
193
|
-
function stripXmlTags(content) {
|
|
194
|
-
const stripped = [];
|
|
195
|
-
let cleaned = content;
|
|
196
|
-
const MAX_PASSES = 5;
|
|
197
|
-
for (let pass = 0; pass < MAX_PASSES; pass++) {
|
|
198
|
-
XML_INJECTION_PATTERN.lastIndex = 0;
|
|
199
|
-
if (!XML_INJECTION_PATTERN.test(cleaned)) break;
|
|
200
|
-
cleaned = cleaned.replace(XML_INJECTION_PATTERN, (match, _g1, offset) => {
|
|
201
|
-
stripped.push({
|
|
202
|
-
tag: match,
|
|
203
|
-
reason: "XML-like conversation injection tag",
|
|
204
|
-
startIndex: offset,
|
|
205
|
-
length: match.length
|
|
206
|
-
});
|
|
207
|
-
return "";
|
|
208
|
-
});
|
|
209
|
-
}
|
|
210
|
-
return { cleaned, stripped };
|
|
211
|
-
}
|
|
212
|
-
function stripHtmlComments(content) {
|
|
213
|
-
const stripped = [];
|
|
214
|
-
let cleaned = content;
|
|
215
|
-
const MAX_PASSES = 5;
|
|
216
|
-
for (let pass = 0; pass < MAX_PASSES; pass++) {
|
|
217
|
-
HTML_COMMENT_PATTERN.lastIndex = 0;
|
|
218
|
-
const prevLength = cleaned.length;
|
|
219
|
-
cleaned = cleaned.replace(HTML_COMMENT_PATTERN, (match, offset) => {
|
|
220
|
-
const hasInstruction = /\b(ignore|execute|close|merge|delete|apply)\b/i.test(match);
|
|
221
|
-
if (!hasInstruction) return match;
|
|
222
|
-
stripped.push({
|
|
223
|
-
tag: "<!-- ... -->",
|
|
224
|
-
reason: "HTML comment with instruction-like content",
|
|
225
|
-
startIndex: offset,
|
|
226
|
-
length: match.length
|
|
227
|
-
});
|
|
228
|
-
return "";
|
|
229
|
-
});
|
|
230
|
-
if (cleaned.length === prevLength) break;
|
|
231
|
-
}
|
|
232
|
-
let searchFrom = 0;
|
|
233
|
-
while (searchFrom < cleaned.length) {
|
|
234
|
-
const openIdx = cleaned.indexOf("<!--", searchFrom);
|
|
235
|
-
if (openIdx === -1) break;
|
|
236
|
-
const closeIdx = cleaned.indexOf("-->", openIdx + 4);
|
|
237
|
-
if (closeIdx === -1) {
|
|
238
|
-
stripped.push({
|
|
239
|
-
tag: "<!--",
|
|
240
|
-
reason: "Unclosed HTML comment (potential injection vector)",
|
|
241
|
-
startIndex: openIdx,
|
|
242
|
-
length: cleaned.length - openIdx
|
|
243
|
-
});
|
|
244
|
-
cleaned = cleaned.slice(0, openIdx);
|
|
245
|
-
break;
|
|
246
|
-
}
|
|
247
|
-
searchFrom = closeIdx + 3;
|
|
248
|
-
}
|
|
249
|
-
return { cleaned, stripped };
|
|
250
|
-
}
|
|
251
|
-
function detectInjectionPatterns(content) {
|
|
252
|
-
const flags = /* @__PURE__ */ new Set();
|
|
253
|
-
for (const { flag, pattern } of INJECTION_PATTERNS) {
|
|
254
|
-
pattern.lastIndex = 0;
|
|
255
|
-
if (pattern.test(content)) {
|
|
256
|
-
flags.add(flag);
|
|
257
|
-
}
|
|
258
|
-
}
|
|
259
|
-
if (looksLikeBase64Payload(content)) {
|
|
260
|
-
flags.add("base64_encoded");
|
|
261
|
-
}
|
|
262
|
-
return Array.from(flags);
|
|
263
|
-
}
|
|
264
|
-
function normalizeRole(userRole) {
|
|
265
|
-
const lower = userRole.toLowerCase();
|
|
266
|
-
if (lower in ROLE_DEFAULT_TRUST) return lower;
|
|
267
|
-
return "unknown";
|
|
268
|
-
}
|
|
269
|
-
function assignTrustTier(userRole, injectionFlags, allowlisted) {
|
|
270
|
-
if (allowlisted) return "1";
|
|
271
|
-
const normalizedRole = normalizeRole(userRole);
|
|
272
|
-
const baseTier = ROLE_DEFAULT_TRUST[normalizedRole];
|
|
273
|
-
const hostileFlags = ["system_prompt_manipulation", "fake_conversation"];
|
|
274
|
-
if (injectionFlags.some((f) => hostileFlags.includes(f))) return "4";
|
|
275
|
-
if (injectionFlags.includes("authority_claim") && normalizedRole !== "owner" && normalizedRole !== "maintainer") {
|
|
276
|
-
return "4";
|
|
277
|
-
}
|
|
278
|
-
return baseTier;
|
|
279
|
-
}
|
|
280
|
-
function sanitizeInput(content, userRole, username, config) {
|
|
281
|
-
const cfg = SanitizerConfigSchema.parse(config ?? {});
|
|
282
|
-
const truncated = content.slice(0, cfg.maxInputLength);
|
|
283
|
-
const allowlisted = cfg.allowlistedMaintainers.includes(username);
|
|
284
|
-
try {
|
|
285
|
-
const entityDecoded = applyEntityEvasionDefense(truncated);
|
|
286
|
-
const html = stripDangerousHtml(entityDecoded.cleaned);
|
|
287
|
-
const xml = stripXmlTags(html.cleaned);
|
|
288
|
-
const comments = stripHtmlComments(xml.cleaned);
|
|
289
|
-
const allStripped = [
|
|
290
|
-
...entityDecoded.stripped,
|
|
291
|
-
...html.stripped,
|
|
292
|
-
...xml.stripped,
|
|
293
|
-
...comments.stripped
|
|
294
|
-
];
|
|
295
|
-
const injectionFlags = detectInjectionPatterns(truncated);
|
|
296
|
-
const trustTier = assignTrustTier(userRole, injectionFlags, allowlisted);
|
|
297
|
-
return {
|
|
298
|
-
content: comments.cleaned,
|
|
299
|
-
originalLength: content.length,
|
|
300
|
-
trustTier,
|
|
301
|
-
userRole,
|
|
302
|
-
injectionFlags,
|
|
303
|
-
strippedElements: allStripped,
|
|
304
|
-
wasModified: allStripped.length > 0,
|
|
305
|
-
sanitizedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
306
|
-
};
|
|
307
|
-
} catch (err2) {
|
|
308
|
-
return buildFailClosedResult(err2, content, truncated, userRole, allowlisted);
|
|
309
|
-
}
|
|
310
|
-
}
|
|
311
|
-
function buildFailClosedResult(err2, originalContent, truncated, userRole, allowlisted) {
|
|
312
|
-
const message = err2 instanceof Error ? err2.message : String(err2);
|
|
313
|
-
return {
|
|
314
|
-
content: "",
|
|
315
|
-
originalLength: originalContent.length,
|
|
316
|
-
trustTier: allowlisted ? "1" : "4",
|
|
317
|
-
userRole,
|
|
318
|
-
injectionFlags: [],
|
|
319
|
-
strippedElements: [
|
|
320
|
-
{
|
|
321
|
-
tag: "(pipeline-failure)",
|
|
322
|
-
reason: `Sanitizer pipeline threw; input discarded as fail-closed: ${message}`,
|
|
323
|
-
startIndex: 0,
|
|
324
|
-
length: truncated.length
|
|
325
|
-
}
|
|
326
|
-
],
|
|
327
|
-
wasModified: true,
|
|
328
|
-
sanitizedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
329
|
-
};
|
|
330
|
-
}
|
|
331
|
-
|
|
332
22
|
// src/security/trust-classifier.ts
|
|
333
23
|
function mapAuthorAssociation(association) {
|
|
334
24
|
switch (association.toUpperCase()) {
|
|
@@ -413,106 +103,106 @@ function getRequiredTrustTier(actionType) {
|
|
|
413
103
|
}
|
|
414
104
|
|
|
415
105
|
// src/security/policy-gate.ts
|
|
416
|
-
import { z as
|
|
106
|
+
import { z as z2 } from "zod";
|
|
417
107
|
|
|
418
108
|
// src/security/action-schema.ts
|
|
419
|
-
import { z
|
|
420
|
-
var RepoFileSource =
|
|
421
|
-
type:
|
|
422
|
-
path:
|
|
423
|
-
line:
|
|
424
|
-
commit:
|
|
109
|
+
import { z } from "zod";
|
|
110
|
+
var RepoFileSource = z.object({
|
|
111
|
+
type: z.literal("repoFile"),
|
|
112
|
+
path: z.string().min(1),
|
|
113
|
+
line: z.number().int().positive().optional(),
|
|
114
|
+
commit: z.string().regex(/^[a-f0-9]{7,40}$/).optional()
|
|
425
115
|
});
|
|
426
|
-
var IssueCommentSource =
|
|
427
|
-
type:
|
|
428
|
-
issueNumber:
|
|
429
|
-
commentId:
|
|
430
|
-
author:
|
|
116
|
+
var IssueCommentSource = z.object({
|
|
117
|
+
type: z.literal("issueComment"),
|
|
118
|
+
issueNumber: z.number().int().positive(),
|
|
119
|
+
commentId: z.number().int().positive(),
|
|
120
|
+
author: z.string().min(1),
|
|
431
121
|
authorTrustTier: TrustTierSchema
|
|
432
122
|
});
|
|
433
|
-
var CIResultSource =
|
|
434
|
-
type:
|
|
435
|
-
runId:
|
|
436
|
-
status:
|
|
437
|
-
job:
|
|
123
|
+
var CIResultSource = z.object({
|
|
124
|
+
type: z.literal("ciResult"),
|
|
125
|
+
runId: z.number().int().positive(),
|
|
126
|
+
status: z.enum(["pass", "fail"]),
|
|
127
|
+
job: z.string().min(1)
|
|
438
128
|
});
|
|
439
|
-
var PolicyDocSource =
|
|
440
|
-
type:
|
|
441
|
-
path:
|
|
442
|
-
section:
|
|
129
|
+
var PolicyDocSource = z.object({
|
|
130
|
+
type: z.literal("policyDoc"),
|
|
131
|
+
path: z.string().min(1),
|
|
132
|
+
section: z.string().min(1)
|
|
443
133
|
});
|
|
444
|
-
var MaintainerCommandSource =
|
|
445
|
-
type:
|
|
446
|
-
username:
|
|
447
|
-
commentId:
|
|
134
|
+
var MaintainerCommandSource = z.object({
|
|
135
|
+
type: z.literal("maintainerCommand"),
|
|
136
|
+
username: z.string().min(1),
|
|
137
|
+
commentId: z.number().int().positive()
|
|
448
138
|
});
|
|
449
|
-
var SourceCitationSchema =
|
|
139
|
+
var SourceCitationSchema = z.discriminatedUnion("type", [
|
|
450
140
|
RepoFileSource,
|
|
451
141
|
IssueCommentSource,
|
|
452
142
|
CIResultSource,
|
|
453
143
|
PolicyDocSource,
|
|
454
144
|
MaintainerCommandSource
|
|
455
145
|
]);
|
|
456
|
-
var SummarizeIssueAction =
|
|
457
|
-
type:
|
|
458
|
-
summary:
|
|
459
|
-
sources:
|
|
146
|
+
var SummarizeIssueAction = z.object({
|
|
147
|
+
type: z.literal("SummarizeIssue"),
|
|
148
|
+
summary: z.string().min(10).max(2e3),
|
|
149
|
+
sources: z.array(SourceCitationSchema).min(1).max(20)
|
|
460
150
|
});
|
|
461
|
-
var ProposeLabelsAction =
|
|
462
|
-
type:
|
|
463
|
-
labels:
|
|
464
|
-
reason:
|
|
465
|
-
sources:
|
|
151
|
+
var ProposeLabelsAction = z.object({
|
|
152
|
+
type: z.literal("ProposeLabels"),
|
|
153
|
+
labels: z.array(z.string()).min(1).max(5),
|
|
154
|
+
reason: z.string().min(10).max(500),
|
|
155
|
+
sources: z.array(SourceCitationSchema).min(1).max(20)
|
|
466
156
|
});
|
|
467
|
-
var DraftReplyAction =
|
|
468
|
-
type:
|
|
469
|
-
body:
|
|
470
|
-
requiresApproval:
|
|
471
|
-
sources:
|
|
157
|
+
var DraftReplyAction = z.object({
|
|
158
|
+
type: z.literal("DraftReply"),
|
|
159
|
+
body: z.string().min(10).max(2e3),
|
|
160
|
+
requiresApproval: z.literal(true),
|
|
161
|
+
sources: z.array(SourceCitationSchema).min(1).max(20)
|
|
472
162
|
});
|
|
473
|
-
var RequestHumanApprovalAction =
|
|
474
|
-
type:
|
|
475
|
-
reason:
|
|
476
|
-
context:
|
|
163
|
+
var RequestHumanApprovalAction = z.object({
|
|
164
|
+
type: z.literal("RequestHumanApproval"),
|
|
165
|
+
reason: z.string().min(10).max(500),
|
|
166
|
+
context: z.string().min(10).max(2e3)
|
|
477
167
|
});
|
|
478
|
-
var GeneratePatchPlanAction =
|
|
479
|
-
type:
|
|
480
|
-
files:
|
|
481
|
-
|
|
482
|
-
path:
|
|
483
|
-
operation:
|
|
484
|
-
description:
|
|
168
|
+
var GeneratePatchPlanAction = z.object({
|
|
169
|
+
type: z.literal("GeneratePatchPlan"),
|
|
170
|
+
files: z.array(
|
|
171
|
+
z.object({
|
|
172
|
+
path: z.string().min(1),
|
|
173
|
+
operation: z.enum(["modify", "create", "delete"]),
|
|
174
|
+
description: z.string().min(10).max(500)
|
|
485
175
|
})
|
|
486
176
|
).min(1).max(10),
|
|
487
|
-
rationale:
|
|
488
|
-
requiresApproval:
|
|
489
|
-
sources:
|
|
177
|
+
rationale: z.string().min(10).max(1e3),
|
|
178
|
+
requiresApproval: z.literal(true),
|
|
179
|
+
sources: z.array(SourceCitationSchema).min(2).max(20)
|
|
490
180
|
});
|
|
491
|
-
var ClassifyIssueAction =
|
|
492
|
-
type:
|
|
493
|
-
category:
|
|
494
|
-
confidence:
|
|
495
|
-
sources:
|
|
181
|
+
var ClassifyIssueAction = z.object({
|
|
182
|
+
type: z.literal("ClassifyIssue"),
|
|
183
|
+
category: z.enum(["bug", "feature", "question", "documentation", "security", "performance"]),
|
|
184
|
+
confidence: z.number().min(0).max(1),
|
|
185
|
+
sources: z.array(SourceCitationSchema).min(1).max(20)
|
|
496
186
|
});
|
|
497
|
-
var IdentifyDuplicatesAction =
|
|
498
|
-
type:
|
|
499
|
-
candidates:
|
|
500
|
-
similarity:
|
|
501
|
-
sources:
|
|
187
|
+
var IdentifyDuplicatesAction = z.object({
|
|
188
|
+
type: z.literal("IdentifyDuplicates"),
|
|
189
|
+
candidates: z.array(z.number().int().positive()).min(1).max(10),
|
|
190
|
+
similarity: z.array(z.number().min(0).max(1)),
|
|
191
|
+
sources: z.array(SourceCitationSchema).min(1).max(20)
|
|
502
192
|
});
|
|
503
|
-
var RefuseActionAction =
|
|
504
|
-
type:
|
|
505
|
-
reason:
|
|
506
|
-
escalateTo:
|
|
193
|
+
var RefuseActionAction = z.object({
|
|
194
|
+
type: z.literal("RefuseAction"),
|
|
195
|
+
reason: z.string().min(10).max(500),
|
|
196
|
+
escalateTo: z.enum(["maintainer", "security"])
|
|
507
197
|
});
|
|
508
|
-
var HandoffMessageAction =
|
|
509
|
-
type:
|
|
510
|
-
targetCapability:
|
|
511
|
-
reason:
|
|
512
|
-
inputTrustTier:
|
|
513
|
-
sources:
|
|
198
|
+
var HandoffMessageAction = z.object({
|
|
199
|
+
type: z.literal("HandoffMessage"),
|
|
200
|
+
targetCapability: z.string().min(1).max(100),
|
|
201
|
+
reason: z.string().min(5).max(500),
|
|
202
|
+
inputTrustTier: z.enum(["1", "2", "3", "4"]),
|
|
203
|
+
sources: z.array(SourceCitationSchema).min(1).max(20)
|
|
514
204
|
});
|
|
515
|
-
var AgentActionSchema =
|
|
205
|
+
var AgentActionSchema = z.discriminatedUnion("type", [
|
|
516
206
|
SummarizeIssueAction,
|
|
517
207
|
ProposeLabelsAction,
|
|
518
208
|
DraftReplyAction,
|
|
@@ -564,13 +254,13 @@ function requiresCitation(actionType) {
|
|
|
564
254
|
}
|
|
565
255
|
|
|
566
256
|
// src/security/policy-gate.ts
|
|
567
|
-
var ViolationSchema =
|
|
257
|
+
var ViolationSchema = z2.object({
|
|
568
258
|
/** Machine-readable rule identifier. */
|
|
569
|
-
rule:
|
|
259
|
+
rule: z2.string().min(1),
|
|
570
260
|
/** Human-readable description of the violation. */
|
|
571
|
-
message:
|
|
261
|
+
message: z2.string().min(1),
|
|
572
262
|
/** Severity: 'block' prevents execution, 'warn' logs only. */
|
|
573
|
-
severity:
|
|
263
|
+
severity: z2.enum(["block", "warn"])
|
|
574
264
|
});
|
|
575
265
|
function getActionSources(action) {
|
|
576
266
|
if ("sources" in action) {
|
|
@@ -802,211 +492,6 @@ function getCorroborationRules(actionType) {
|
|
|
802
492
|
return ACTION_CORROBORATION_RULES[actionType];
|
|
803
493
|
}
|
|
804
494
|
|
|
805
|
-
// src/security/reputation-model.ts
|
|
806
|
-
import { z as z4 } from "zod";
|
|
807
|
-
var SuspiciousSignalSchema = z4.enum([
|
|
808
|
-
"new_account",
|
|
809
|
-
"no_prior_contributions",
|
|
810
|
-
"injection_patterns_detected",
|
|
811
|
-
"rapid_comments",
|
|
812
|
-
"mismatched_authority_claim"
|
|
813
|
-
]);
|
|
814
|
-
var DAYS_PER_YEAR_APPROX = 36.5;
|
|
815
|
-
var SUSPICIOUS_THRESHOLDS = {
|
|
816
|
-
/** Account younger than this (days) is flagged. */
|
|
817
|
-
newAccountDays: 30,
|
|
818
|
-
/** Fewer contributions than this is flagged. */
|
|
819
|
-
minContributions: 1,
|
|
820
|
-
/** More comments than this in the window triggers rapid-comment flag. */
|
|
821
|
-
rapidCommentThreshold: 5,
|
|
822
|
-
/** Time window (minutes) for rapid comment detection. */
|
|
823
|
-
rapidCommentWindowMinutes: 10
|
|
824
|
-
};
|
|
825
|
-
var DEFAULT_TTL_MS = CACHE_TIMEOUTS.reputationTtlMs;
|
|
826
|
-
var DEFAULT_MAX_SIZE = 1e3;
|
|
827
|
-
var ReputationCache = class {
|
|
828
|
-
cache = /* @__PURE__ */ new Map();
|
|
829
|
-
ttlMs;
|
|
830
|
-
maxSize;
|
|
831
|
-
constructor(ttlMs = DEFAULT_TTL_MS, maxSize = DEFAULT_MAX_SIZE) {
|
|
832
|
-
this.ttlMs = ttlMs;
|
|
833
|
-
this.maxSize = maxSize;
|
|
834
|
-
}
|
|
835
|
-
get(username) {
|
|
836
|
-
const entry = this.cache.get(username);
|
|
837
|
-
if (entry === void 0) return void 0;
|
|
838
|
-
if (getTimeProvider().now() > entry.expiresAt) {
|
|
839
|
-
this.cache.delete(username);
|
|
840
|
-
return void 0;
|
|
841
|
-
}
|
|
842
|
-
return entry.assessment;
|
|
843
|
-
}
|
|
844
|
-
set(username, assessment) {
|
|
845
|
-
if (this.cache.size >= this.maxSize && !this.cache.has(username)) {
|
|
846
|
-
this.evictOldest();
|
|
847
|
-
}
|
|
848
|
-
this.cache.set(username, {
|
|
849
|
-
assessment,
|
|
850
|
-
expiresAt: getTimeProvider().now() + this.ttlMs
|
|
851
|
-
});
|
|
852
|
-
}
|
|
853
|
-
/** Evict a batch of oldest entries (10% of maxSize, minimum 1). */
|
|
854
|
-
evictOldest() {
|
|
855
|
-
const batchSize = Math.max(1, Math.floor(this.maxSize * 0.1));
|
|
856
|
-
const keys = this.cache.keys();
|
|
857
|
-
for (let i = 0; i < batchSize; i++) {
|
|
858
|
-
const next = keys.next();
|
|
859
|
-
if (next.done === true) break;
|
|
860
|
-
this.cache.delete(next.value);
|
|
861
|
-
}
|
|
862
|
-
}
|
|
863
|
-
clear() {
|
|
864
|
-
this.cache.clear();
|
|
865
|
-
}
|
|
866
|
-
get size() {
|
|
867
|
-
return this.cache.size;
|
|
868
|
-
}
|
|
869
|
-
};
|
|
870
|
-
var HOSTILE_INJECTION_FLAGS = [
|
|
871
|
-
"system_prompt_manipulation",
|
|
872
|
-
"fake_conversation",
|
|
873
|
-
"authority_claim",
|
|
874
|
-
"hidden_content"
|
|
875
|
-
];
|
|
876
|
-
function hasHostileInjection(flags) {
|
|
877
|
-
return flags.some((f) => HOSTILE_INJECTION_FLAGS.includes(f));
|
|
878
|
-
}
|
|
879
|
-
function isRapidCommenting(m) {
|
|
880
|
-
return m.recentCommentCount !== void 0 && m.recentCommentWindowMinutes !== void 0 && m.recentCommentCount > SUSPICIOUS_THRESHOLDS.rapidCommentThreshold && m.recentCommentWindowMinutes <= SUSPICIOUS_THRESHOLDS.rapidCommentWindowMinutes;
|
|
881
|
-
}
|
|
882
|
-
function isMismatchedAuthority(m) {
|
|
883
|
-
if (!m.injectionFlags.includes("authority_claim")) return false;
|
|
884
|
-
const association = m.authorAssociation.toUpperCase();
|
|
885
|
-
return association !== "OWNER" && association !== "MEMBER";
|
|
886
|
-
}
|
|
887
|
-
function detectSuspiciousSignals(metadata) {
|
|
888
|
-
const signals = [];
|
|
889
|
-
const { accountAgeDays, priorContributions } = metadata;
|
|
890
|
-
if (accountAgeDays !== void 0 && accountAgeDays < SUSPICIOUS_THRESHOLDS.newAccountDays) {
|
|
891
|
-
signals.push("new_account");
|
|
892
|
-
}
|
|
893
|
-
if (priorContributions !== void 0 && priorContributions < SUSPICIOUS_THRESHOLDS.minContributions) {
|
|
894
|
-
signals.push("no_prior_contributions");
|
|
895
|
-
}
|
|
896
|
-
if (hasHostileInjection(metadata.injectionFlags)) signals.push("injection_patterns_detected");
|
|
897
|
-
if (isRapidCommenting(metadata)) signals.push("rapid_comments");
|
|
898
|
-
if (isMismatchedAuthority(metadata)) signals.push("mismatched_authority_claim");
|
|
899
|
-
return signals;
|
|
900
|
-
}
|
|
901
|
-
function calculateReputationScore(metadata, signals, userRole) {
|
|
902
|
-
let score = 50;
|
|
903
|
-
const roleBonus = {
|
|
904
|
-
owner: 40,
|
|
905
|
-
maintainer: 35,
|
|
906
|
-
collaborator: 25,
|
|
907
|
-
contributor: 15,
|
|
908
|
-
member: 5,
|
|
909
|
-
unknown: 0
|
|
910
|
-
};
|
|
911
|
-
score += roleBonus[userRole];
|
|
912
|
-
if (metadata.accountAgeDays !== void 0) {
|
|
913
|
-
score += Math.min(metadata.accountAgeDays / DAYS_PER_YEAR_APPROX, 10);
|
|
914
|
-
}
|
|
915
|
-
if (metadata.priorContributions !== void 0) {
|
|
916
|
-
score += Math.min(metadata.priorContributions, 10);
|
|
917
|
-
}
|
|
918
|
-
const signalPenalty = {
|
|
919
|
-
new_account: -15,
|
|
920
|
-
no_prior_contributions: -10,
|
|
921
|
-
injection_patterns_detected: -25,
|
|
922
|
-
rapid_comments: -20,
|
|
923
|
-
mismatched_authority_claim: -30
|
|
924
|
-
};
|
|
925
|
-
for (const signal of signals) {
|
|
926
|
-
score += signalPenalty[signal];
|
|
927
|
-
}
|
|
928
|
-
return Math.max(0, Math.min(100, Math.round(score)));
|
|
929
|
-
}
|
|
930
|
-
function mapRole(association) {
|
|
931
|
-
switch (association.toUpperCase()) {
|
|
932
|
-
case "OWNER":
|
|
933
|
-
return "owner";
|
|
934
|
-
case "MEMBER":
|
|
935
|
-
return "member";
|
|
936
|
-
case "COLLABORATOR":
|
|
937
|
-
return "collaborator";
|
|
938
|
-
case "CONTRIBUTOR":
|
|
939
|
-
return "contributor";
|
|
940
|
-
default:
|
|
941
|
-
return "unknown";
|
|
942
|
-
}
|
|
943
|
-
}
|
|
944
|
-
function determineEffectiveTier(userRole, signals) {
|
|
945
|
-
const baseTier = ROLE_DEFAULT_TRUST[userRole];
|
|
946
|
-
const hostileSignals = [
|
|
947
|
-
"injection_patterns_detected",
|
|
948
|
-
"mismatched_authority_claim"
|
|
949
|
-
];
|
|
950
|
-
if (signals.some((s) => hostileSignals.includes(s))) return "4";
|
|
951
|
-
if (signals.length >= 2) {
|
|
952
|
-
const baseNumeric = TRUST_TIER_NUMERIC[baseTier];
|
|
953
|
-
const downgraded = Math.min(baseNumeric + 1, 4);
|
|
954
|
-
return String(downgraded);
|
|
955
|
-
}
|
|
956
|
-
return baseTier;
|
|
957
|
-
}
|
|
958
|
-
function assessReputation(metadata, cache) {
|
|
959
|
-
const cached = cache?.get(metadata.username);
|
|
960
|
-
if (cached !== void 0) return cached;
|
|
961
|
-
const userRole = mapRole(metadata.authorAssociation);
|
|
962
|
-
const signals = detectSuspiciousSignals(metadata);
|
|
963
|
-
const score = calculateReputationScore(metadata, signals, userRole);
|
|
964
|
-
const effectiveTier = determineEffectiveTier(userRole, signals);
|
|
965
|
-
const assessment = {
|
|
966
|
-
username: metadata.username,
|
|
967
|
-
userRole,
|
|
968
|
-
suspiciousSignals: signals,
|
|
969
|
-
isSuspicious: signals.length > 0,
|
|
970
|
-
effectiveTrustTier: effectiveTier,
|
|
971
|
-
reputationScore: score,
|
|
972
|
-
reason: buildReason(userRole, signals, effectiveTier),
|
|
973
|
-
assessedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
974
|
-
};
|
|
975
|
-
cache?.set(metadata.username, assessment);
|
|
976
|
-
return assessment;
|
|
977
|
-
}
|
|
978
|
-
function reconcileTrustTier(classifierTier, reputation) {
|
|
979
|
-
if (classifierTier === "1") return "1";
|
|
980
|
-
const repTier = reputation?.effectiveTrustTier;
|
|
981
|
-
if (repTier === void 0) return classifierTier;
|
|
982
|
-
return TRUST_TIER_NUMERIC[repTier] > TRUST_TIER_NUMERIC[classifierTier] ? repTier : classifierTier;
|
|
983
|
-
}
|
|
984
|
-
var ReputationGatingModeSchema = z4.enum(["off", "audit", "enforce"]);
|
|
985
|
-
var DEFAULT_REPUTATION_GATING_MODE = "audit";
|
|
986
|
-
function resolveReputationGatingMode(env = process.env) {
|
|
987
|
-
const raw = env["NEXUS_REPUTATION_GATING"];
|
|
988
|
-
if (typeof raw !== "string" || raw.length === 0) return DEFAULT_REPUTATION_GATING_MODE;
|
|
989
|
-
const parsed = ReputationGatingModeSchema.safeParse(raw.toLowerCase());
|
|
990
|
-
return parsed.success ? parsed.data : DEFAULT_REPUTATION_GATING_MODE;
|
|
991
|
-
}
|
|
992
|
-
function gateWithReputation(classifierTier, reputation, mode) {
|
|
993
|
-
const reconciledTier = mode === "off" ? classifierTier : reconcileTrustTier(classifierTier, reputation);
|
|
994
|
-
const enforcedTier = mode === "enforce" ? reconciledTier : classifierTier;
|
|
995
|
-
return {
|
|
996
|
-
enforcedTier,
|
|
997
|
-
reconciledTier,
|
|
998
|
-
demotionSuppressed: mode !== "enforce" && reconciledTier !== classifierTier,
|
|
999
|
-
mode
|
|
1000
|
-
};
|
|
1001
|
-
}
|
|
1002
|
-
function buildReason(role, signals, tier) {
|
|
1003
|
-
if (signals.length === 0) {
|
|
1004
|
-
return `Role ${role} \u2192 Tier ${tier} (no suspicious signals)`;
|
|
1005
|
-
}
|
|
1006
|
-
const signalList = signals.join(", ");
|
|
1007
|
-
return `Role ${role} \u2192 Tier ${tier} (signals: ${signalList})`;
|
|
1008
|
-
}
|
|
1009
|
-
|
|
1010
495
|
// src/scm/url-parsers.ts
|
|
1011
496
|
function parsePRUrl(url) {
|
|
1012
497
|
const httpPattern = /github\.com\/([^/]+)\/([^/]+)\/pull\/(\d+)/;
|
|
@@ -1262,7 +747,7 @@ function createFullGitHubProvider(repo) {
|
|
|
1262
747
|
}
|
|
1263
748
|
|
|
1264
749
|
// src/dogfooding/issue-triage-types.ts
|
|
1265
|
-
import { z as
|
|
750
|
+
import { z as z3 } from "zod";
|
|
1266
751
|
var CATEGORY_DISPLAY_NAMES = {
|
|
1267
752
|
bug: "Bug Report",
|
|
1268
753
|
feature: "Feature Request",
|
|
@@ -1284,11 +769,11 @@ var DEFAULT_ISSUE_TRIAGE_CONFIG = {
|
|
|
1284
769
|
maxComments: 50,
|
|
1285
770
|
enableReputation: true
|
|
1286
771
|
};
|
|
1287
|
-
var IssueTriageConfigSchema =
|
|
1288
|
-
dryRun:
|
|
1289
|
-
githubToken:
|
|
1290
|
-
maxComments:
|
|
1291
|
-
enableReputation:
|
|
772
|
+
var IssueTriageConfigSchema = z3.object({
|
|
773
|
+
dryRun: z3.boolean().default(true),
|
|
774
|
+
githubToken: z3.string().optional(),
|
|
775
|
+
maxComments: z3.number().int().min(1).max(100).default(50),
|
|
776
|
+
enableReputation: z3.boolean().default(true)
|
|
1292
777
|
});
|
|
1293
778
|
|
|
1294
779
|
// src/dogfooding/issue-triage-helpers.ts
|
|
@@ -1686,15 +1171,6 @@ function createIssueTriage(config) {
|
|
|
1686
1171
|
}
|
|
1687
1172
|
|
|
1688
1173
|
export {
|
|
1689
|
-
TrustTierSchema,
|
|
1690
|
-
TRUST_TIER_NUMERIC,
|
|
1691
|
-
GitHubUserRoleSchema,
|
|
1692
|
-
ROLE_DEFAULT_TRUST,
|
|
1693
|
-
InjectionFlagSchema,
|
|
1694
|
-
StrippedElementSchema,
|
|
1695
|
-
SanitizedInputSchema,
|
|
1696
|
-
SanitizerConfigSchema,
|
|
1697
|
-
sanitizeInput,
|
|
1698
1174
|
mapAuthorAssociation,
|
|
1699
1175
|
classifyTrust,
|
|
1700
1176
|
canInfluenceDecisions,
|
|
@@ -1711,10 +1187,6 @@ export {
|
|
|
1711
1187
|
canProceed,
|
|
1712
1188
|
validateCorroboration,
|
|
1713
1189
|
getCorroborationRules,
|
|
1714
|
-
SuspiciousSignalSchema,
|
|
1715
|
-
ReputationCache,
|
|
1716
|
-
assessReputation,
|
|
1717
|
-
reconcileTrustTier,
|
|
1718
1190
|
parsePRUrl,
|
|
1719
1191
|
GitHubReviewer,
|
|
1720
1192
|
GitHubUserInfo,
|
|
@@ -1723,4 +1195,4 @@ export {
|
|
|
1723
1195
|
IssueTriage,
|
|
1724
1196
|
createIssueTriage
|
|
1725
1197
|
};
|
|
1726
|
-
//# sourceMappingURL=chunk-
|
|
1198
|
+
//# sourceMappingURL=chunk-L6JZCAFH.js.map
|