@owasp-aghast/aghast 0.8.4 → 0.9.0-rc.1

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.
Files changed (107) hide show
  1. package/dist/build-config.d.ts.map +1 -1
  2. package/dist/build-config.js +101 -2
  3. package/dist/build-config.js.map +1 -1
  4. package/dist/check-library.d.ts +25 -0
  5. package/dist/check-library.d.ts.map +1 -1
  6. package/dist/check-library.js +165 -0
  7. package/dist/check-library.js.map +1 -1
  8. package/dist/ci-metadata.d.ts +42 -0
  9. package/dist/ci-metadata.d.ts.map +1 -0
  10. package/dist/ci-metadata.js +131 -0
  11. package/dist/ci-metadata.js.map +1 -0
  12. package/dist/cli.js +4 -1
  13. package/dist/cli.js.map +1 -1
  14. package/dist/concurrency.d.ts +18 -0
  15. package/dist/concurrency.d.ts.map +1 -0
  16. package/dist/concurrency.js +39 -0
  17. package/dist/concurrency.js.map +1 -0
  18. package/dist/cost-tracker.d.ts +45 -0
  19. package/dist/cost-tracker.d.ts.map +1 -0
  20. package/dist/cost-tracker.js +56 -0
  21. package/dist/cost-tracker.js.map +1 -0
  22. package/dist/discoveries/glob-discovery.d.ts +21 -0
  23. package/dist/discoveries/glob-discovery.d.ts.map +1 -0
  24. package/dist/discoveries/glob-discovery.js +215 -0
  25. package/dist/discoveries/glob-discovery.js.map +1 -0
  26. package/dist/discoveries/script-discovery.d.ts +117 -0
  27. package/dist/discoveries/script-discovery.d.ts.map +1 -0
  28. package/dist/discoveries/script-discovery.js +593 -0
  29. package/dist/discoveries/script-discovery.js.map +1 -0
  30. package/dist/docs-url.d.ts +14 -0
  31. package/dist/docs-url.d.ts.map +1 -0
  32. package/dist/docs-url.js +17 -0
  33. package/dist/docs-url.js.map +1 -0
  34. package/dist/error-codes.d.ts +12 -1
  35. package/dist/error-codes.d.ts.map +1 -1
  36. package/dist/error-codes.js +16 -2
  37. package/dist/error-codes.js.map +1 -1
  38. package/dist/formatters/csv-formatter.d.ts +12 -1
  39. package/dist/formatters/csv-formatter.d.ts.map +1 -1
  40. package/dist/formatters/csv-formatter.js +64 -2
  41. package/dist/formatters/csv-formatter.js.map +1 -1
  42. package/dist/formatters/html-formatter.d.ts.map +1 -1
  43. package/dist/formatters/html-formatter.js +43 -3
  44. package/dist/formatters/html-formatter.js.map +1 -1
  45. package/dist/formatters/index.d.ts.map +1 -1
  46. package/dist/formatters/index.js +2 -0
  47. package/dist/formatters/index.js.map +1 -1
  48. package/dist/formatters/markdown-formatter.d.ts +61 -0
  49. package/dist/formatters/markdown-formatter.d.ts.map +1 -0
  50. package/dist/formatters/markdown-formatter.js +557 -0
  51. package/dist/formatters/markdown-formatter.js.map +1 -0
  52. package/dist/formatters/sarif-formatter.d.ts +7 -0
  53. package/dist/formatters/sarif-formatter.d.ts.map +1 -1
  54. package/dist/formatters/sarif-formatter.js +80 -0
  55. package/dist/formatters/sarif-formatter.js.map +1 -1
  56. package/dist/formatters/types.d.ts +24 -0
  57. package/dist/formatters/types.d.ts.map +1 -1
  58. package/dist/index.d.ts +4 -0
  59. package/dist/index.d.ts.map +1 -1
  60. package/dist/index.js +342 -9
  61. package/dist/index.js.map +1 -1
  62. package/dist/issue-file-writer.d.ts +21 -0
  63. package/dist/issue-file-writer.d.ts.map +1 -0
  64. package/dist/issue-file-writer.js +307 -0
  65. package/dist/issue-file-writer.js.map +1 -0
  66. package/dist/judge.d.ts +62 -0
  67. package/dist/judge.d.ts.map +1 -0
  68. package/dist/judge.js +305 -0
  69. package/dist/judge.js.map +1 -0
  70. package/dist/mock-agent-provider.d.ts +3 -0
  71. package/dist/mock-agent-provider.d.ts.map +1 -1
  72. package/dist/mock-agent-provider.js +13 -0
  73. package/dist/mock-agent-provider.js.map +1 -1
  74. package/dist/new-check.d.ts.map +1 -1
  75. package/dist/new-check.js +322 -13
  76. package/dist/new-check.js.map +1 -1
  77. package/dist/opencode-provider.d.ts.map +1 -1
  78. package/dist/opencode-provider.js +33 -0
  79. package/dist/opencode-provider.js.map +1 -1
  80. package/dist/repo-scan.d.ts +80 -0
  81. package/dist/repo-scan.d.ts.map +1 -0
  82. package/dist/repo-scan.js +250 -0
  83. package/dist/repo-scan.js.map +1 -0
  84. package/dist/result-handlers/pr-comment-handler.d.ts +136 -0
  85. package/dist/result-handlers/pr-comment-handler.d.ts.map +1 -0
  86. package/dist/result-handlers/pr-comment-handler.js +381 -0
  87. package/dist/result-handlers/pr-comment-handler.js.map +1 -0
  88. package/dist/retry.d.ts +128 -0
  89. package/dist/retry.d.ts.map +1 -0
  90. package/dist/retry.js +261 -0
  91. package/dist/retry.js.map +1 -0
  92. package/dist/runtime-config.d.ts.map +1 -1
  93. package/dist/runtime-config.js +62 -0
  94. package/dist/runtime-config.js.map +1 -1
  95. package/dist/scan-runner.d.ts +31 -22
  96. package/dist/scan-runner.d.ts.map +1 -1
  97. package/dist/scan-runner.js +145 -100
  98. package/dist/scan-runner.js.map +1 -1
  99. package/dist/semgrep-runner.js +6 -4
  100. package/dist/semgrep-runner.js.map +1 -1
  101. package/dist/stats.d.ts.map +1 -1
  102. package/dist/stats.js +4 -1
  103. package/dist/stats.js.map +1 -1
  104. package/dist/types.d.ts +149 -2
  105. package/dist/types.d.ts.map +1 -1
  106. package/dist/types.js.map +1 -1
  107. package/package.json +1 -1
@@ -0,0 +1,381 @@
1
+ /**
2
+ * GitHub PR comment result handler (Phase 1 of Spec E.7).
3
+ *
4
+ * Posts aghast scan findings as inline review comments on a GitHub pull request.
5
+ * Issues that fall outside the PR diff are skipped (with a debug log). Existing
6
+ * comments authored by aghast are deduplicated using a hidden HTML marker
7
+ * embedded in the comment body.
8
+ *
9
+ * Phase-1 limitations (intentional):
10
+ * - Multi-line findings (`endLine > startLine`) are anchored at `startLine`
11
+ * only; we do not yet emit GitHub's multi-line `start_line`/`line` form.
12
+ * - Dedup is best-effort and per-run: two parallel runs (e.g. CI matrix
13
+ * shards) on the same PR can each pass the dedup check and post duplicate
14
+ * comments. Confine `--pr` to a single matrix shard, or run after the
15
+ * matrix completes. A future phase could PATCH instead of POST.
16
+ *
17
+ * Future phases (issue tracker integration, AI remediation, IDE/LSP, Slack/email
18
+ * notifications) are intentionally out of scope here — see GitHub issue #119.
19
+ */
20
+ import { spawn } from 'node:child_process';
21
+ import { createHash } from 'node:crypto';
22
+ import { appendFileSync } from 'node:fs';
23
+ import { logDebug, logProgress, logWarn } from '../logging.js';
24
+ const TAG = 'pr-comment-handler';
25
+ // ─── Default executor (real `gh` shell-out) ──────────────────────────────────
26
+ /**
27
+ * Test-only fake executor selector.
28
+ *
29
+ * When `AGHAST_PR_COMMENT_FAKE_FILES` is set, `gh api .../pulls/<n>/files` calls
30
+ * resolve to the JSON contents of that file. When `AGHAST_PR_COMMENT_FAKE_LOG`
31
+ * is set, every captured call (including POST bodies) is appended as JSON-lines
32
+ * to that path. The real `gh` is never spawned. This is used by CLI integration
33
+ * tests so we can exercise the full --pr code path without hitting GitHub.
34
+ */
35
+ function maybeFakeExecutor() {
36
+ const filesPath = process.env.AGHAST_PR_COMMENT_FAKE_FILES;
37
+ const logPath = process.env.AGHAST_PR_COMMENT_FAKE_LOG;
38
+ const commentsPath = process.env.AGHAST_PR_COMMENT_FAKE_COMMENTS;
39
+ if (!filesPath && !logPath && !commentsPath)
40
+ return undefined;
41
+ return async (command, args) => {
42
+ if (logPath) {
43
+ try {
44
+ appendFileSync(logPath, JSON.stringify({ command, args }) + '\n', 'utf-8');
45
+ }
46
+ catch {
47
+ // Best-effort logging only.
48
+ }
49
+ }
50
+ const apiPath = args[args.length - 1] ?? '';
51
+ if (/\/files(\?|$)/.test(apiPath) && filesPath) {
52
+ const { readFileSync } = await import('node:fs');
53
+ return { stdout: readFileSync(filesPath, 'utf-8'), stderr: '', exitCode: 0 };
54
+ }
55
+ if (/\/comments(\?|$)/.test(apiPath)) {
56
+ if (commentsPath) {
57
+ const { readFileSync } = await import('node:fs');
58
+ return { stdout: readFileSync(commentsPath, 'utf-8'), stderr: '', exitCode: 0 };
59
+ }
60
+ return { stdout: '[]', stderr: '', exitCode: 0 };
61
+ }
62
+ if (/\/reviews(\?|$)/.test(apiPath)) {
63
+ return { stdout: '{"id":1}', stderr: '', exitCode: 0 };
64
+ }
65
+ return { stdout: '[]', stderr: '', exitCode: 0 };
66
+ };
67
+ }
68
+ export const defaultExecutor = (command, args, options = {}) => {
69
+ return new Promise((resolvePromise, reject) => {
70
+ const child = spawn(command, args, {
71
+ env: { ...process.env, ...(options.env ?? {}) },
72
+ shell: false,
73
+ });
74
+ let stdout = '';
75
+ let stderr = '';
76
+ child.stdout.on('data', (chunk) => {
77
+ stdout += chunk.toString('utf-8');
78
+ });
79
+ child.stderr.on('data', (chunk) => {
80
+ stderr += chunk.toString('utf-8');
81
+ });
82
+ child.on('error', (err) => {
83
+ // Translate the most common failure (gh CLI not installed) into an
84
+ // actionable message. Without this the user sees `Error: spawn gh ENOENT`
85
+ // and has to guess what to install.
86
+ if (err.code === 'ENOENT') {
87
+ reject(new Error(`gh CLI not found on PATH. Install it from https://cli.github.com or set GH_TOKEN and ensure 'gh' is reachable. (original: ${err.message})`));
88
+ return;
89
+ }
90
+ reject(err);
91
+ });
92
+ child.on('close', (code) => {
93
+ resolvePromise({ stdout, stderr, exitCode: code ?? 1 });
94
+ });
95
+ if (options.input !== undefined) {
96
+ child.stdin.end(options.input);
97
+ }
98
+ else {
99
+ child.stdin.end();
100
+ }
101
+ });
102
+ };
103
+ // ─── Patch parsing ───────────────────────────────────────────────────────────
104
+ /**
105
+ * Parse a unified-diff `patch` (as returned by GitHub's PR files API) into the
106
+ * set of new-file line numbers that were added or modified by the PR.
107
+ *
108
+ * GitHub PR review comments can target either the LEFT (base) or RIGHT (head)
109
+ * side. We only post comments on lines that exist on the head side — i.e. lines
110
+ * actually changed (added) by this PR — to avoid leaving stale comments on
111
+ * unchanged code.
112
+ */
113
+ export function parsePatchAddedLines(patch) {
114
+ const addedLines = new Set();
115
+ if (!patch)
116
+ return addedLines;
117
+ const lines = patch.split('\n');
118
+ let newLine = 0;
119
+ let inHunk = false;
120
+ // Hunk header format: @@ -<oldStart>,<oldLen> +<newStart>,<newLen> @@
121
+ const hunkRegex = /^@@ -\d+(?:,\d+)? \+(\d+)(?:,\d+)? @@/;
122
+ for (const line of lines) {
123
+ const hunkMatch = hunkRegex.exec(line);
124
+ if (hunkMatch) {
125
+ newLine = Number(hunkMatch[1]);
126
+ inHunk = true;
127
+ continue;
128
+ }
129
+ // Defensive: ignore patch content (including stray `+` lines from a
130
+ // malformed patch) until we see the first hunk header. This prevents us
131
+ // from ever recording line 0 — which would later masquerade as a real
132
+ // diff line and either skip or anchor a comment at a nonexistent line.
133
+ if (!inHunk)
134
+ continue;
135
+ if (line.startsWith('+') && !line.startsWith('+++')) {
136
+ addedLines.add(newLine);
137
+ newLine++;
138
+ }
139
+ else if (line.startsWith('-') && !line.startsWith('---')) {
140
+ // Removed line — does not advance newLine
141
+ }
142
+ else if (line.startsWith('\\')) {
143
+ // "" — ignore, doesn't advance
144
+ }
145
+ else {
146
+ // Context line: present in both old and new.
147
+ newLine++;
148
+ }
149
+ }
150
+ return addedLines;
151
+ }
152
+ /**
153
+ * Build a map of file path → set of new-side line numbers changed by the PR.
154
+ */
155
+ export function buildDiffLineMap(files) {
156
+ const map = new Map();
157
+ for (const file of files) {
158
+ if (!file.patch)
159
+ continue;
160
+ map.set(file.filename, parsePatchAddedLines(file.patch));
161
+ }
162
+ return map;
163
+ }
164
+ // ─── Comment building ────────────────────────────────────────────────────────
165
+ /**
166
+ * Stable hash identifying a single (check + file + line + description) tuple.
167
+ * Embedded as an HTML comment in the body so future runs can recognise — and
168
+ * skip — comments they previously posted.
169
+ */
170
+ export function issueHash(issue) {
171
+ // Description is deliberately NOT in the hash. AI-generated descriptions are
172
+ // non-deterministic (the LLM may rephrase the same finding across runs), so
173
+ // including the description would defeat dedup on re-runs. The trade-off: if
174
+ // an issue at the same (checkId, file, startLine) genuinely changes meaning,
175
+ // the original comment will not be refreshed — Phase 1 accepts that staleness
176
+ // in exchange for idempotent re-runs. A future phase can PATCH the existing
177
+ // comment to refresh stale descriptions.
178
+ const payload = [
179
+ issue.checkId,
180
+ issue.file,
181
+ issue.startLine,
182
+ ].join('');
183
+ return createHash('sha256').update(payload).digest('hex').slice(0, 16);
184
+ }
185
+ const MARKER_PREFIX = '<!-- aghast-issue-id: ';
186
+ const MARKER_SUFFIX = ' -->';
187
+ export function buildCommentBody(issue) {
188
+ const hash = issueHash(issue);
189
+ const severity = issue.severity ? ` (${issue.severity})` : '';
190
+ const lines = [];
191
+ lines.push(`**aghast: ${issue.checkName}**${severity}`);
192
+ lines.push('');
193
+ lines.push(issue.description);
194
+ if (issue.recommendation) {
195
+ lines.push('');
196
+ lines.push(`**Recommendation:** ${issue.recommendation}`);
197
+ }
198
+ lines.push('');
199
+ lines.push(`${MARKER_PREFIX}${hash}${MARKER_SUFFIX}`);
200
+ return lines.join('\n');
201
+ }
202
+ export function extractMarkerHash(body) {
203
+ const idx = body.indexOf(MARKER_PREFIX);
204
+ if (idx === -1)
205
+ return undefined;
206
+ const start = idx + MARKER_PREFIX.length;
207
+ const end = body.indexOf(MARKER_SUFFIX, start);
208
+ if (end === -1)
209
+ return undefined;
210
+ return body.slice(start, end).trim();
211
+ }
212
+ /**
213
+ * Map a SecurityIssue onto a ReviewComment, or return undefined if the issue's
214
+ * line is not part of the PR diff.
215
+ */
216
+ export function issueToReviewComment(issue, diffLines) {
217
+ const fileLines = diffLines.get(issue.file);
218
+ if (!fileLines || !fileLines.has(issue.startLine)) {
219
+ return undefined;
220
+ }
221
+ return {
222
+ path: issue.file,
223
+ line: issue.startLine,
224
+ side: 'RIGHT',
225
+ body: buildCommentBody(issue),
226
+ };
227
+ }
228
+ async function ghApi(executor, path, options = {}) {
229
+ const args = ['api'];
230
+ if (options.method && options.method !== 'GET') {
231
+ args.push('--method', options.method);
232
+ }
233
+ if (options.paginate)
234
+ args.push('--paginate');
235
+ args.push('-H', 'Accept: application/vnd.github+json');
236
+ args.push(path);
237
+ let input;
238
+ if (options.body !== undefined) {
239
+ args.push('--input', '-');
240
+ input = JSON.stringify(options.body);
241
+ }
242
+ // Pass a token via env if provided; we never log or echo it.
243
+ const env = {};
244
+ if (options.token)
245
+ env.GH_TOKEN = options.token;
246
+ const result = await executor('gh', args, { input, env });
247
+ if (result.exitCode !== 0) {
248
+ throw new Error(`gh api ${path} failed (exit ${result.exitCode}): ${result.stderr.trim() || result.stdout.trim()}`);
249
+ }
250
+ if (result.stdout.trim() === '') {
251
+ return undefined;
252
+ }
253
+ return JSON.parse(result.stdout);
254
+ }
255
+ async function listPullRequestFiles(executor, ctx) {
256
+ const path = `repos/${ctx.owner}/${ctx.repo}/pulls/${ctx.prNumber}/files?per_page=100`;
257
+ return ghApi(executor, path, {
258
+ paginate: true,
259
+ token: ctx.githubToken,
260
+ });
261
+ }
262
+ async function listExistingReviewComments(executor, ctx) {
263
+ const path = `repos/${ctx.owner}/${ctx.repo}/pulls/${ctx.prNumber}/comments?per_page=100`;
264
+ return ghApi(executor, path, {
265
+ paginate: true,
266
+ token: ctx.githubToken,
267
+ });
268
+ }
269
+ /**
270
+ * Upper bound on inline comments in a single review.
271
+ *
272
+ * A noisy check against a large diff could otherwise post hundreds of comments
273
+ * in one API call — unreviewable for the author, and large enough to risk the
274
+ * request being rejected outright, which loses every comment rather than some.
275
+ * Truncating is the lesser harm, and it is surfaced in the review body rather
276
+ * than dropped silently. Mirrors `limitTargets` in the SARIF parser.
277
+ */
278
+ const DEFAULT_MAX_INLINE_COMMENTS = 50;
279
+ async function postReview(executor, ctx, comments, omitted = 0) {
280
+ const path = `repos/${ctx.owner}/${ctx.repo}/pulls/${ctx.prNumber}/reviews`;
281
+ const summary = `aghast posted ${comments.length} finding${comments.length === 1 ? '' : 's'}.`;
282
+ const body = {
283
+ event: 'COMMENT',
284
+ body: omitted > 0
285
+ ? `${summary} ${omitted} further finding${omitted === 1 ? '' : 's'} ${omitted === 1 ? 'was' : 'were'} not posted inline (capped at ${comments.length} per review) — see the full scan report for the complete list.`
286
+ : summary,
287
+ comments,
288
+ };
289
+ if (ctx.headSha)
290
+ body.commit_id = ctx.headSha;
291
+ await ghApi(executor, path, {
292
+ method: 'POST',
293
+ body,
294
+ token: ctx.githubToken,
295
+ });
296
+ }
297
+ /**
298
+ * Post aghast scan issues as inline GitHub PR review comments.
299
+ *
300
+ * Workflow:
301
+ * 1. Fetch the PR's changed-file list and parse each file's diff hunks.
302
+ * 2. For each issue, compute the corresponding (path, line, RIGHT) target
303
+ * and skip issues whose line is outside the diff.
304
+ * 3. Fetch existing review comments and skip any whose hidden marker hash
305
+ * already matches an in-flight comment.
306
+ * 4. Post the remainder as a single review with `event=COMMENT`.
307
+ */
308
+ export async function postPRComments(results, ctx, options = {}) {
309
+ const executor = options.executor ?? maybeFakeExecutor() ?? defaultExecutor;
310
+ const issues = results.issues ?? [];
311
+ if (issues.length === 0) {
312
+ logProgress(TAG, 'No issues to post — skipping PR comment phase');
313
+ return { posted: 0, skipped: 0 };
314
+ }
315
+ logProgress(TAG, `Posting up to ${issues.length} finding(s) to ${ctx.owner}/${ctx.repo}#${ctx.prNumber}`);
316
+ const files = await listPullRequestFiles(executor, ctx);
317
+ const diffLines = buildDiffLineMap(files);
318
+ logDebug(TAG, `PR diff covers ${diffLines.size} file(s) with patch hunks`);
319
+ // Map issues onto review comments + bucket the skipped ones.
320
+ const candidates = [];
321
+ let skippedOutsideDiff = 0;
322
+ for (const issue of issues) {
323
+ const comment = issueToReviewComment(issue, diffLines);
324
+ if (!comment) {
325
+ skippedOutsideDiff++;
326
+ logDebug(TAG, `Skipping issue outside PR diff: ${issue.checkId} ${issue.file}:${issue.startLine}`);
327
+ continue;
328
+ }
329
+ candidates.push(comment);
330
+ }
331
+ if (candidates.length === 0) {
332
+ logProgress(TAG, `No issues fell within the PR diff (${skippedOutsideDiff} skipped)`);
333
+ return { posted: 0, skipped: skippedOutsideDiff };
334
+ }
335
+ // Deduplicate against existing review comments.
336
+ let existing = [];
337
+ try {
338
+ existing = await listExistingReviewComments(executor, ctx);
339
+ }
340
+ catch (err) {
341
+ logWarn(TAG, `Failed to list existing PR comments — skipping dedup: ${err instanceof Error ? err.message : String(err)}`);
342
+ }
343
+ const existingHashes = new Set();
344
+ for (const c of existing) {
345
+ const h = extractMarkerHash(c.body);
346
+ if (h)
347
+ existingHashes.add(h);
348
+ }
349
+ const toPost = [];
350
+ let skippedDuplicate = 0;
351
+ for (const comment of candidates) {
352
+ const h = extractMarkerHash(comment.body);
353
+ if (h && existingHashes.has(h)) {
354
+ skippedDuplicate++;
355
+ logDebug(TAG, `Skipping duplicate comment ${h} on ${comment.path}:${comment.line}`);
356
+ continue;
357
+ }
358
+ toPost.push(comment);
359
+ }
360
+ const totalSkipped = skippedOutsideDiff + skippedDuplicate;
361
+ if (toPost.length === 0) {
362
+ logProgress(TAG, `Nothing new to post (${skippedOutsideDiff} outside diff, ${skippedDuplicate} duplicate)`);
363
+ return { posted: 0, skipped: totalSkipped };
364
+ }
365
+ const maxComments = options.maxComments ?? DEFAULT_MAX_INLINE_COMMENTS;
366
+ const capped = toPost.slice(0, maxComments);
367
+ const omitted = toPost.length - capped.length;
368
+ if (omitted > 0) {
369
+ logWarn(TAG, `${toPost.length} comments to post exceeds the cap of ${maxComments}; ` +
370
+ `posting the first ${capped.length} and noting the remaining ${omitted} in the review summary. ` +
371
+ 'All findings remain in the scan report.');
372
+ }
373
+ await postReview(executor, ctx, capped, omitted);
374
+ logProgress(TAG, `Posted ${capped.length} comment(s); skipped ${totalSkipped} (${skippedOutsideDiff} outside diff, ${skippedDuplicate} duplicate)`);
375
+ // Only present when something was actually omitted, so the result shape is
376
+ // unchanged for the overwhelmingly common case (and for existing consumers).
377
+ return omitted > 0
378
+ ? { posted: capped.length, skipped: totalSkipped, omitted }
379
+ : { posted: capped.length, skipped: totalSkipped };
380
+ }
381
+ //# sourceMappingURL=pr-comment-handler.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pr-comment-handler.js","sourceRoot":"","sources":["../../src/result-handlers/pr-comment-handler.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,128 @@
1
+ /**
2
+ * Retry helper with exponential backoff and an optional shared circuit breaker.
3
+ *
4
+ * Spec Appendix E.6 — Retry and Resilience.
5
+ *
6
+ * Default classifier retries on rate-limit (429), 5xx, network/timeout errors;
7
+ * does NOT retry auth errors (401/403) or validation errors. Callers can override
8
+ * via `isRetryable`.
9
+ *
10
+ * The circuit breaker is shared across every `withRetry` invocation that
11
+ * receives the same `CircuitBreaker` instance. Within a single scan that
12
+ * means concurrent target analyses share one breaker; across multiple
13
+ * `runMultiScan` invocations the scope depends on whether the caller hoists
14
+ * the breaker to module scope or constructs a fresh one per scan (currently
15
+ * `runMultiScan` does the latter, so the breaker is per-scan, not per-process).
16
+ * When `consecutiveFailures` reaches the configured threshold, subsequent
17
+ * calls fail-fast with `CircuitOpenError` without invoking the wrapped
18
+ * function. A successful call resets the counter.
19
+ */
20
+ export interface RetryOptions {
21
+ /** Maximum total attempts (initial + retries). Must be >= 1. */
22
+ maxAttempts: number;
23
+ /** Initial backoff delay in ms (before jitter). */
24
+ baseDelayMs: number;
25
+ /** Cap on the backoff delay in ms (before jitter). */
26
+ maxDelayMs: number;
27
+ /** Optional classifier — return true to retry, false to give up immediately. */
28
+ isRetryable?: (err: unknown) => boolean;
29
+ /** Optional shared circuit breaker. */
30
+ breaker?: CircuitBreaker;
31
+ /** Optional sleep override (for testing). Defaults to setTimeout. */
32
+ sleep?: (ms: number) => Promise<void>;
33
+ /** Optional jitter source returning [0, 1). Defaults to Math.random. */
34
+ random?: () => number;
35
+ /** Optional label for log lines. */
36
+ label?: string;
37
+ }
38
+ /**
39
+ * Default retry options used when callers don't supply a value.
40
+ *
41
+ * `maxAttempts: 1` means retry is OFF unless explicitly configured — one
42
+ * attempt, no backoff, the original error rethrown untouched. This keeps a
43
+ * plain `aghast scan` behaving exactly as it did before retry existed.
44
+ * Opt in with `--retry-max-attempts`, `AGHAST_RETRY_MAX_ATTEMPTS`, or
45
+ * `retry.maxAttempts` in runtime config.
46
+ *
47
+ * The backoff values below are the defaults used *once retry is enabled*;
48
+ * they have no effect at `maxAttempts: 1`.
49
+ */
50
+ export declare const DEFAULT_RETRY: Required<Pick<RetryOptions, 'maxAttempts' | 'baseDelayMs' | 'maxDelayMs'>>;
51
+ /** Attempts at or below this mean the caller has not opted into retry. */
52
+ export declare const RETRY_DISABLED_ATTEMPTS = 1;
53
+ /** True when the resolved options leave retry switched off. */
54
+ export declare function isRetryEnabled(maxAttempts: number): boolean;
55
+ /**
56
+ * Default retryable-error classifier.
57
+ *
58
+ * Decision order (first match wins):
59
+ * 1. HTTP status / statusCode property — authoritative when present.
60
+ * 4xx (except 429) → not retryable; 429 and 5xx → retryable.
61
+ * 2. errno-style `code` property (ECONNRESET, ETIMEDOUT, etc.) → retryable.
62
+ * 3. Free-text message fallback — only consulted when neither status nor
63
+ * code matched. Looks for explicit substrings like "rate limit",
64
+ * "timed out", "socket hang up". We deliberately do NOT pattern-match
65
+ * bare 5xx digits in messages (too prone to false positives — e.g.
66
+ * "failed at 500ms timeout" would otherwise be classified retryable).
67
+ *
68
+ * Does NOT retry:
69
+ * - 401 / 403 (auth)
70
+ * - 400 / 404 / 422 (client/validation)
71
+ * - Anything classified as fatal by the agent provider (the scan runner
72
+ * handles FatalProviderError separately — those never reach this classifier).
73
+ * - Unknown errors (callers can pass a custom `isRetryable` to widen this).
74
+ */
75
+ export declare function defaultIsRetryable(err: unknown): boolean;
76
+ export interface CircuitBreakerOptions {
77
+ /** Maximum consecutive total failures before tripping. Must be >= 1. */
78
+ threshold: number;
79
+ }
80
+ /**
81
+ * Thrown when aghast's own per-call guard fires because a provider call did not
82
+ * return in time. Distinct from a provider-reported network timeout.
83
+ *
84
+ * Named rather than message-matched so the retry decision is deliberate: before
85
+ * this existed, the classification depended on the word "timeout" appearing in
86
+ * a free-text message, which is exactly the incidental matching the classifier
87
+ * warns against elsewhere.
88
+ *
89
+ * It IS retryable — a hung stream frequently succeeds on a fresh call — but the
90
+ * cost is visible: with `maxAttempts: n` a genuinely wedged provider takes up to
91
+ * n × the timeout to give up. That trade is documented in
92
+ * docs/configuration.md so anyone enabling retry knows what they are buying.
93
+ */
94
+ export declare class AgentTimeoutError extends Error {
95
+ constructor(message: string);
96
+ }
97
+ /** Thrown by `withRetry` when the breaker is open (failing fast). */
98
+ export declare class CircuitOpenError extends Error {
99
+ constructor(consecutiveFailures: number, threshold: number);
100
+ }
101
+ /**
102
+ * Process-level circuit breaker. Shared across all `withRetry` calls that
103
+ * receive the same instance. Counts only TOTAL failures (after all retries
104
+ * for a single call exhausted), not individual attempts.
105
+ */
106
+ export declare class CircuitBreaker {
107
+ private consecutiveFailures;
108
+ readonly threshold: number;
109
+ constructor(options: CircuitBreakerOptions);
110
+ /** True when the breaker is open (further calls should fail-fast). */
111
+ isOpen(): boolean;
112
+ /** Increment the failure counter. */
113
+ recordFailure(): void;
114
+ /** Reset the failure counter. Called on any successful `withRetry` call. */
115
+ recordSuccess(): void;
116
+ /** Force-reset the counter (useful for tests or manual recovery). */
117
+ reset(): void;
118
+ /** Current consecutive-failure count (for diagnostics/tests). */
119
+ getConsecutiveFailures(): number;
120
+ }
121
+ /** Compute the next backoff delay given an attempt number (1-based). */
122
+ export declare function computeBackoff(attempt: number, baseDelayMs: number, maxDelayMs: number, random?: () => number): number;
123
+ /**
124
+ * Run `fn` with retry on transient errors. Returns its result on success;
125
+ * throws on the final failure (or `CircuitOpenError` if the breaker is open).
126
+ */
127
+ export declare function withRetry<T>(fn: () => Promise<T>, options: RetryOptions): Promise<T>;
128
+ //# sourceMappingURL=retry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"retry.d.ts","sourceRoot":"","sources":["../src/retry.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAMH,MAAM,WAAW,YAAY;IAC3B,gEAAgE;IAChE,WAAW,EAAE,MAAM,CAAC;IACpB,mDAAmD;IACnD,WAAW,EAAE,MAAM,CAAC;IACpB,sDAAsD;IACtD,UAAU,EAAE,MAAM,CAAC;IACnB,gFAAgF;IAChF,WAAW,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,OAAO,CAAC;IACxC,uCAAuC;IACvC,OAAO,CAAC,EAAE,cAAc,CAAC;IACzB,qEAAqE;IACrE,KAAK,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACtC,wEAAwE;IACxE,MAAM,CAAC,EAAE,MAAM,MAAM,CAAC;IACtB,oCAAoC;IACpC,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,aAAa,EAAE,QAAQ,CAAC,IAAI,CAAC,YAAY,EAAE,aAAa,GAAG,aAAa,GAAG,YAAY,CAAC,CAIpG,CAAC;AAEF,0EAA0E;AAC1E,eAAO,MAAM,uBAAuB,IAAI,CAAC;AAEzC,+DAA+D;AAC/D,wBAAgB,cAAc,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAE3D;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAkExD;AAID,MAAM,WAAW,qBAAqB;IACpC,wEAAwE;IACxE,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;;;;;;;;;;;;GAaG;AACH,qBAAa,iBAAkB,SAAQ,KAAK;gBAC9B,OAAO,EAAE,MAAM;CAI5B;AAED,qEAAqE;AACrE,qBAAa,gBAAiB,SAAQ,KAAK;gBAC7B,mBAAmB,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM;CAM3D;AAED;;;;GAIG;AACH,qBAAa,cAAc;IACzB,OAAO,CAAC,mBAAmB,CAAK;IAChC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;gBAEf,OAAO,EAAE,qBAAqB;IAO1C,sEAAsE;IACtE,MAAM,IAAI,OAAO;IAIjB,qCAAqC;IACrC,aAAa,IAAI,IAAI;IAIrB,4EAA4E;IAC5E,aAAa,IAAI,IAAI;IAIrB,qEAAqE;IACrE,KAAK,IAAI,IAAI;IAIb,iEAAiE;IACjE,sBAAsB,IAAI,MAAM;CAGjC;AAID,wEAAwE;AACxE,wBAAgB,cAAc,CAC5B,OAAO,EAAE,MAAM,EACf,WAAW,EAAE,MAAM,EACnB,UAAU,EAAE,MAAM,EAClB,MAAM,GAAE,MAAM,MAAoB,GACjC,MAAM,CAOR;AAKD;;;GAGG;AACH,wBAAsB,SAAS,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,CAAC,CAAC,CAiE1F"}