@clawvard/sdk 0.14.0 → 0.15.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.
@@ -0,0 +1,585 @@
1
+ /**
2
+ * `cv.security.pentestRun` — hand-written orchestration for the
3
+ * `agent-pentest` course.
4
+ *
5
+ * The pentest agent (open-source `strix-agent`, Apache-2.0) runs
6
+ * locally against a target the user owns — usually a self-hosted
7
+ * OWASP Juice Shop or a staging build of their own web app. The SDK
8
+ * mints a one-time LLM credential scoped to this run via
9
+ * `service.clawvard`, spawns the `strix` CLI as a subprocess with the
10
+ * credential exposed as `LLM_API_KEY`, and normalises the
11
+ * `strix_runs/<name>/` directory into the typed
12
+ * `SecurityPentestRunOutput` shape when the process exits.
13
+ *
14
+ * Node-only. Attempting to call `pentestRun` in a browser (or any
15
+ * environment without `child_process`) throws
16
+ * `SandboxMissingError` early — Strix launches its agent sandbox in a
17
+ * local Docker container.
18
+ *
19
+ * The credential handed to Strix expires when the run resolves or
20
+ * hits the wall-clock timeout. The SDK does NOT hard-code an
21
+ * OpenAI-compatible relay base URL in the run — the endpoint is
22
+ * chosen by `service.clawvard` at mint time.
23
+ *
24
+ * Callers can pattern-match on the SDK-side error subclasses:
25
+ *
26
+ * try {
27
+ * await cv.security.pentestRun({...});
28
+ * } catch (err) {
29
+ * if (err instanceof TargetUnreachableError) { ... }
30
+ * if (err instanceof InvalidTargetHostError) { ... }
31
+ * if (err instanceof SandboxMissingError) { ... }
32
+ * if (err instanceof OwnershipUnconfirmedError) { ... }
33
+ * if (err instanceof LlmQuotaError) { ... }
34
+ * if (err instanceof PentestTimeoutError) { ... }
35
+ * if (err instanceof StrixCrashedError) { ... }
36
+ * }
37
+ */
38
+ import { ClawvardError } from "./errors.js";
39
+ import { SecurityNamespace } from "./generated.js";
40
+ /** Public error codes surfaced on `PentestError.code`. Uppercase `E_`-
41
+ * prefixed shape per the course PRD so consumers can pattern-match
42
+ * without normalising case. See `mapPentestError` for the two-way
43
+ * mapping against server-side envelopes (which may still use the
44
+ * lowercase `snake_case` form to be robust to older backends). */
45
+ export const PENTEST_ERROR_CODES = [
46
+ "E_TARGET_UNREACHABLE",
47
+ "E_INVALID_TARGET_HOST",
48
+ "E_SANDBOX_MISSING",
49
+ "E_OWNERSHIP_UNCONFIRMED",
50
+ "E_LLM_QUOTA",
51
+ "E_TIMEOUT",
52
+ "E_STRIX_CRASHED",
53
+ "E_SERVICE_NOT_LIVE",
54
+ ];
55
+ /** Base class for any error surfaced by `cv.security.pentestRun`. */
56
+ export class PentestError extends ClawvardError {
57
+ constructor(code, message) {
58
+ super(message, code);
59
+ this.name = "PentestError";
60
+ }
61
+ }
62
+ /** `HEAD <targetUrl>` failed before Strix could be spawned. Usually
63
+ * means the user forgot to `docker run` the target, or bound it to a
64
+ * port other than the one they wrote in the prompt. */
65
+ export class TargetUnreachableError extends PentestError {
66
+ constructor(message = "Target URL could not be reached — start the target (e.g. `docker run --rm -p 3000:3000 bkimminich/juice-shop`) and retry.") {
67
+ super("E_TARGET_UNREACHABLE", message);
68
+ this.name = "TargetUnreachableError";
69
+ }
70
+ }
71
+ /** Either the input `targetUrl` isn't in `allowedHosts`, or Strix
72
+ * tried to reach a host outside the whitelist mid-run. The SDK kills
73
+ * the run instead of letting a coerced prompt escape the sandbox. */
74
+ export class InvalidTargetHostError extends PentestError {
75
+ constructor(message = "Target host is not in `allowedHosts`. Add the exact host:port shown in `targetUrl` and retry.") {
76
+ super("E_INVALID_TARGET_HOST", message);
77
+ this.name = "InvalidTargetHostError";
78
+ }
79
+ }
80
+ /** Local Docker daemon is missing or unreachable, `strix` is not on
81
+ * PATH, or the environment cannot spawn subprocesses (browser). */
82
+ export class SandboxMissingError extends PentestError {
83
+ constructor(message = "Local sandbox is not ready — install strix-agent (`pipx install strix-agent`), start Docker, and retry.") {
84
+ super("E_SANDBOX_MISSING", message);
85
+ this.name = "SandboxMissingError";
86
+ }
87
+ }
88
+ /** `ownership.confirmed` was missing or `false`. The service will not
89
+ * attack a target the caller has not declared they own or are
90
+ * authorised to test. */
91
+ export class OwnershipUnconfirmedError extends PentestError {
92
+ constructor(message = "Set `ownership.confirmed = true` and a truthful `ownership.statement` to run against your own target.") {
93
+ super("E_OWNERSHIP_UNCONFIRMED", message);
94
+ this.name = "OwnershipUnconfirmedError";
95
+ }
96
+ }
97
+ /** The caller's Clawvard credits balance is below the per-run budget
98
+ * the SDK reserved. Top up at https://clawvard.school and retry. */
99
+ export class LlmQuotaError extends PentestError {
100
+ constructor(message = "Not enough Clawvard credits reserved for this run — top up at https://clawvard.school and retry.") {
101
+ super("E_LLM_QUOTA", message);
102
+ this.name = "LlmQuotaError";
103
+ }
104
+ }
105
+ /** Wall-clock timeout hit before Strix marked the run complete. The
106
+ * scoped LLM credential is revoked at this point so the run cannot
107
+ * keep grinding credits in the background. */
108
+ export class PentestTimeoutError extends PentestError {
109
+ constructor(message = "Pentest run exceeded its wall-clock budget. Split by iteration cap or retry with a smaller target.") {
110
+ super("E_TIMEOUT", message);
111
+ this.name = "PentestTimeoutError";
112
+ }
113
+ }
114
+ /** Strix subprocess exited non-zero. Message carries the tail of
115
+ * Strix's stderr so the caller can see what actually broke — usually
116
+ * a docker-in-docker permissions issue or an interrupted run. */
117
+ export class StrixCrashedError extends PentestError {
118
+ constructor(message) {
119
+ super("E_STRIX_CRASHED", message);
120
+ this.name = "StrixCrashedError";
121
+ }
122
+ }
123
+ /** The Clawvard tenant the caller's key resolves to does not yet have
124
+ * the pentest service live — usually means the closed beta hasn't
125
+ * been rolled out to their account. Contact support at
126
+ * https://clawvard.school to request access. Callers can pattern-
127
+ * match this to fall back to a manual Strix invocation if they need
128
+ * a result today. */
129
+ export class PentestServiceNotLiveError extends PentestError {
130
+ constructor(message = "cv.security.pentestRun is currently in closed beta — request access at https://clawvard.school and we'll enable it for your account.") {
131
+ super("E_SERVICE_NOT_LIVE", message);
132
+ this.name = "PentestServiceNotLiveError";
133
+ }
134
+ }
135
+ /** Map a backend error envelope (`{ error: code, message }`) to the
136
+ * matching SDK class. Accepts both the canonical `E_UPPERCASE_SNAKE`
137
+ * form and the older `lowercase_snake` shape some backends still
138
+ * emit, so a mid-flight backend rollout doesn't spike into generic
139
+ * `PentestError`. `service_not_found` (dispatcher-level 404 for a
140
+ * route the tenant hasn't been enrolled in yet) is folded into
141
+ * `PentestServiceNotLiveError` so callers get an actionable message
142
+ * instead of a raw dispatcher code. */
143
+ export function mapPentestError(code, message) {
144
+ const normalised = normalisePentestCode(code);
145
+ switch (normalised) {
146
+ case "E_TARGET_UNREACHABLE":
147
+ return new TargetUnreachableError(message);
148
+ case "E_INVALID_TARGET_HOST":
149
+ return new InvalidTargetHostError(message);
150
+ case "E_SANDBOX_MISSING":
151
+ return new SandboxMissingError(message);
152
+ case "E_OWNERSHIP_UNCONFIRMED":
153
+ return new OwnershipUnconfirmedError(message);
154
+ case "E_LLM_QUOTA":
155
+ return new LlmQuotaError(message);
156
+ case "E_TIMEOUT":
157
+ return new PentestTimeoutError(message);
158
+ case "E_STRIX_CRASHED":
159
+ return new StrixCrashedError(message);
160
+ case "E_SERVICE_NOT_LIVE":
161
+ return new PentestServiceNotLiveError(message);
162
+ default:
163
+ return new PentestError(code, message);
164
+ }
165
+ }
166
+ /** Fold both legacy `lowercase_snake` and canonical `E_UPPERCASE_SNAKE`
167
+ * server codes to the same canonical value used on `PentestError.code`.
168
+ * Exposed so the pentest-run wrapper can also recognise dispatcher-
169
+ * level codes (`service_not_found` → `E_SERVICE_NOT_LIVE`). */
170
+ function normalisePentestCode(code) {
171
+ if (PENTEST_ERROR_CODES.includes(code)) {
172
+ return code;
173
+ }
174
+ const upperE = code.startsWith("E_")
175
+ ? code
176
+ : `E_${code.toUpperCase()}`;
177
+ if (PENTEST_ERROR_CODES.includes(upperE)) {
178
+ return upperE;
179
+ }
180
+ // Dispatcher-level 404 when the caller's tenant isn't enrolled in the
181
+ // pentest beta yet. Emit the friendlier typed error so users know
182
+ // where to request access rather than a raw dispatcher code.
183
+ if (code === "service_not_found" || code === "not_found") {
184
+ return "E_SERVICE_NOT_LIVE";
185
+ }
186
+ return code;
187
+ }
188
+ /** Attach the hand-written `pentestRun` to the SecurityNamespace
189
+ * prototype. Called from the Clawvard constructor so browsers still
190
+ * see the method (even if calling it throws SandboxMissingError). */
191
+ export function installPentestRun(_client) {
192
+ if (Object.prototype.hasOwnProperty.call(SecurityNamespace.prototype, "pentestRun")) {
193
+ return;
194
+ }
195
+ async function pentestRunImpl(input) {
196
+ validateOwnership(input.ownership);
197
+ validateAllowedHosts(input.targetUrl, input.allowedHosts);
198
+ const nodeApis = await loadNodeApis();
199
+ if (!nodeApis) {
200
+ throw new SandboxMissingError("cv.security.pentestRun requires Node.js — it spawns the local `strix` CLI. Browser environments are not supported.");
201
+ }
202
+ await preflightTarget(input.targetUrl);
203
+ const outputDir = await resolveOutputDir(input, nodeApis);
204
+ const credential = await rethrowAsPentest(this.mintPentestCredential(input));
205
+ return runStrix(input, credential, outputDir, nodeApis);
206
+ }
207
+ Object.defineProperty(SecurityNamespace.prototype, "pentestRun", {
208
+ value: pentestRunImpl,
209
+ writable: true,
210
+ configurable: true,
211
+ });
212
+ }
213
+ async function loadNodeApis() {
214
+ try {
215
+ const [cp, fs, path, os] = await Promise.all([
216
+ import("node:child_process"),
217
+ import("node:fs/promises"),
218
+ import("node:path"),
219
+ import("node:os"),
220
+ ]);
221
+ return { spawn: cp.spawn, fs, path, os };
222
+ }
223
+ catch {
224
+ return null;
225
+ }
226
+ }
227
+ /** Throw `OwnershipUnconfirmedError` before hitting the server if the
228
+ * caller has not confirmed they own the target. */
229
+ function validateOwnership(ownership) {
230
+ if (!ownership || ownership.confirmed !== true) {
231
+ throw new OwnershipUnconfirmedError();
232
+ }
233
+ const statement = ownership.statement?.trim();
234
+ if (!statement || statement.length < 4) {
235
+ throw new OwnershipUnconfirmedError("Set a truthful `ownership.statement` describing why you are authorised to test this target.");
236
+ }
237
+ }
238
+ /** Throw `InvalidTargetHostError` when `targetUrl` isn't covered by
239
+ * `allowedHosts`. Matches on host+port literals — this is not a
240
+ * wildcard matcher; the whitelist has to name the exact origin the
241
+ * caller wrote into `targetUrl`. */
242
+ function validateAllowedHosts(targetUrl, allowedHosts) {
243
+ if (!Array.isArray(allowedHosts) || allowedHosts.length === 0) {
244
+ throw new InvalidTargetHostError("Provide at least one entry in `allowedHosts` — the SDK will not run against a target without a scoped whitelist.");
245
+ }
246
+ let parsed;
247
+ try {
248
+ parsed = new URL(targetUrl);
249
+ }
250
+ catch {
251
+ throw new InvalidTargetHostError("targetUrl is not a parseable URL. Use `http://<host>:<port>` or `https://<host>`.");
252
+ }
253
+ if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
254
+ throw new InvalidTargetHostError("Only http and https schemes are accepted for `targetUrl`.");
255
+ }
256
+ const originShape = parsed.port
257
+ ? `${parsed.hostname}:${parsed.port}`
258
+ : parsed.hostname;
259
+ if (!allowedHosts.includes(originShape) && !allowedHosts.includes(parsed.hostname)) {
260
+ throw new InvalidTargetHostError(`targetUrl origin \`${originShape}\` is not in allowedHosts. Include it verbatim and retry.`);
261
+ }
262
+ }
263
+ /** `HEAD` the target so we fail fast before spending credits on a
264
+ * Strix startup that will just crash. 3s hard budget. */
265
+ async function preflightTarget(targetUrl) {
266
+ const ctrl = new AbortController();
267
+ const timer = setTimeout(() => ctrl.abort(), 3000);
268
+ try {
269
+ const res = await fetch(targetUrl, { method: "HEAD", signal: ctrl.signal, redirect: "follow" });
270
+ if (!res.ok && res.status >= 500) {
271
+ throw new TargetUnreachableError(`Target responded with ${res.status}. Check the app is up and try again.`);
272
+ }
273
+ }
274
+ catch (err) {
275
+ if (err instanceof TargetUnreachableError)
276
+ throw err;
277
+ throw new TargetUnreachableError(`Could not reach ${targetUrl}. Start the target and retry (docker run bkimminich/juice-shop for the demo).`);
278
+ }
279
+ finally {
280
+ clearTimeout(timer);
281
+ }
282
+ }
283
+ /** Resolve the working directory for `strix_runs/<name>/` output.
284
+ * Defaults to `./strix_runs/` under the caller's CWD when
285
+ * `outputDir` is unset. */
286
+ async function resolveOutputDir(input, nodeApis) {
287
+ const dir = input.outputDir ?? nodeApis.path.join(process.cwd(), "strix_runs");
288
+ await nodeApis.fs.mkdir(dir, { recursive: true });
289
+ return dir;
290
+ }
291
+ /** Spawn the `strix` CLI, pipe stdio into log files under
292
+ * `outputDir/<runId>/`, and resolve to the typed run envelope on
293
+ * clean exit. Enforces `maxIterations`-derived wall-clock timeout
294
+ * (2 min per iteration). */
295
+ async function runStrix(input, cred, outputDir, nodeApis) {
296
+ const runDir = nodeApis.path.join(outputDir, cred.runId);
297
+ await nodeApis.fs.mkdir(runDir, { recursive: true });
298
+ const stdoutPath = nodeApis.path.join(runDir, "strix.stdout.log");
299
+ const stderrPath = nodeApis.path.join(runDir, "strix.stderr.log");
300
+ const env = {
301
+ ...process.env,
302
+ LLM_API_KEY: cred.scopedApiKey,
303
+ STRIX_LLM: `${cred.provider}/${cred.model}`,
304
+ ...(cred.scopedApiBase ? { LLM_API_BASE: cred.scopedApiBase } : {}),
305
+ STRIX_RUN_NAME: cred.runId,
306
+ STRIX_OUTPUT_DIR: runDir,
307
+ };
308
+ const args = [
309
+ "--target",
310
+ input.targetUrl,
311
+ "--scan-mode",
312
+ "standard",
313
+ "--non-interactive",
314
+ ];
315
+ const maxIterations = input.maxIterations ?? 40;
316
+ const timeoutMs = Math.min(maxIterations * 120_000, 4 * 60 * 60 * 1000);
317
+ return new Promise((resolve, reject) => {
318
+ let killed = false;
319
+ const child = nodeApis.spawn("strix", args, {
320
+ env,
321
+ cwd: outputDir,
322
+ stdio: ["ignore", "pipe", "pipe"],
323
+ });
324
+ // Pipe strix stdio into on-disk logs so the user can inspect them
325
+ // after the fact; we tail stderr into the crash message.
326
+ let stderrTail = "";
327
+ child.stdout?.on("data", async (chunk) => {
328
+ try {
329
+ await nodeApis.fs.appendFile(stdoutPath, chunk);
330
+ }
331
+ catch { /* best effort */ }
332
+ });
333
+ child.stderr?.on("data", async (chunk) => {
334
+ try {
335
+ await nodeApis.fs.appendFile(stderrPath, chunk);
336
+ }
337
+ catch { /* best effort */ }
338
+ stderrTail = (stderrTail + chunk.toString()).slice(-2048);
339
+ });
340
+ const timer = setTimeout(() => {
341
+ killed = true;
342
+ child.kill("SIGKILL");
343
+ }, timeoutMs);
344
+ child.on("error", (err) => {
345
+ clearTimeout(timer);
346
+ if (err.code === "ENOENT") {
347
+ reject(new SandboxMissingError("The `strix` CLI is not on PATH. Install with `pipx install strix-agent` and retry."));
348
+ return;
349
+ }
350
+ reject(new StrixCrashedError(err.message));
351
+ });
352
+ child.on("exit", async (code) => {
353
+ clearTimeout(timer);
354
+ if (killed) {
355
+ reject(new PentestTimeoutError());
356
+ return;
357
+ }
358
+ if (code !== 0) {
359
+ reject(new StrixCrashedError(`strix exited with code ${code}. Stderr tail:\n${stderrTail || "(empty)"}`));
360
+ return;
361
+ }
362
+ try {
363
+ const out = await hydrateOutput(cred, input, runDir, nodeApis);
364
+ resolve(out);
365
+ }
366
+ catch (err) {
367
+ reject(new StrixCrashedError(`strix finished but its output could not be parsed: ${err.message}`));
368
+ }
369
+ });
370
+ });
371
+ }
372
+ /** Read `<runDir>/report.json` (findings/pocs/patch) plus the
373
+ * self-contained `report.html` and pack them into the typed run
374
+ * envelope. Falls back to reading `findings.json` for older Strix
375
+ * versions that write per-artefact files. */
376
+ async function hydrateOutput(cred, input, runDir, nodeApis) {
377
+ const reportJson = await readJsonSafely(nodeApis.path.join(runDir, "report.json"), nodeApis);
378
+ const findings = reportJson?.findings ?? [];
379
+ const pocs = reportJson?.pocs ?? [];
380
+ const patch = reportJson?.patch ?? {
381
+ diff: "",
382
+ branchSuggestion: `pentest/${cred.runId}`,
383
+ };
384
+ const reportHtmlPath = nodeApis.path.join(runDir, "report.html");
385
+ if (!(await pathExists(reportHtmlPath, nodeApis))) {
386
+ // Compose a minimal HTML report from the JSON when Strix didn't
387
+ // emit one — deterministic, no-network fallback.
388
+ await nodeApis.fs.writeFile(reportHtmlPath, renderPentestReportHtml({
389
+ runId: cred.runId,
390
+ target: input.targetUrl,
391
+ findings,
392
+ pocs,
393
+ patch,
394
+ reportHtmlPath: "",
395
+ costCredits: cred.budgetCredits,
396
+ }));
397
+ }
398
+ return {
399
+ runId: cred.runId,
400
+ target: input.targetUrl,
401
+ findings,
402
+ pocs,
403
+ patch,
404
+ reportHtmlPath,
405
+ costCredits: cred.budgetCredits,
406
+ };
407
+ }
408
+ async function readJsonSafely(filePath, nodeApis) {
409
+ try {
410
+ const text = await nodeApis.fs.readFile(filePath, "utf-8");
411
+ return JSON.parse(text);
412
+ }
413
+ catch {
414
+ return null;
415
+ }
416
+ }
417
+ async function pathExists(filePath, nodeApis) {
418
+ try {
419
+ await nodeApis.fs.stat(filePath);
420
+ return true;
421
+ }
422
+ catch {
423
+ return false;
424
+ }
425
+ }
426
+ /** Backend errors land as either `ClawvardError` (dispatcher envelope
427
+ * with `.code`) or a plain `Error` from `toApiError` (which folds the
428
+ * server's `error: "..."` string into `err.message`). We accept both:
429
+ * a code that matches a `PentestErrorCode` (in either case shape) is
430
+ * rewrapped in the typed subclass; a dispatcher-level 404
431
+ * (`service_not_found`) becomes `PentestServiceNotLiveError` so
432
+ * callers get an actionable message instead of a raw route code. */
433
+ async function rethrowAsPentest(p) {
434
+ try {
435
+ return await p;
436
+ }
437
+ catch (err) {
438
+ if (err instanceof PentestError)
439
+ throw err;
440
+ const remapped = tryRemapPentestError(err);
441
+ if (remapped)
442
+ throw remapped;
443
+ throw err;
444
+ }
445
+ }
446
+ /** Best-effort mapping of any thrown value to a `PentestError`
447
+ * subclass. Returns `null` when the caller should rethrow as-is. */
448
+ function tryRemapPentestError(err) {
449
+ if (err instanceof ClawvardError) {
450
+ return mapPentestError(err.code, err.message);
451
+ }
452
+ if (err instanceof Error) {
453
+ const parsed = parsePentestCodeFromMessage(err.message);
454
+ if (parsed)
455
+ return mapPentestError(parsed.code, parsed.message);
456
+ // Dispatcher / http-client fallback: the API-error string is
457
+ // stashed in `err.message` verbatim. Catch the two flavours we
458
+ // know Clawvard emits for a route the tenant isn't enrolled in.
459
+ const raw = err.message.toLowerCase();
460
+ if (raw.includes("service_not_found") || raw.includes("not_found")) {
461
+ return new PentestServiceNotLiveError();
462
+ }
463
+ }
464
+ return null;
465
+ }
466
+ /** Pull `{ code, message }` out of a `"<code>|<message>"` job-failure
467
+ * message. Mirrors the id-photo convention so a future job-runtime
468
+ * handler can surface typed failures via the same channel. Accepts
469
+ * both canonical `E_UPPERCASE_SNAKE` and legacy `lowercase_snake`
470
+ * prefixes and returns the canonical form. */
471
+ export function parsePentestCodeFromMessage(msg) {
472
+ const pipeIdx = msg.indexOf("|");
473
+ if (pipeIdx <= 0)
474
+ return null;
475
+ const candidate = msg.slice(0, pipeIdx);
476
+ const canonical = normalisePentestCode(candidate);
477
+ if (!PENTEST_ERROR_CODES.includes(canonical))
478
+ return null;
479
+ return {
480
+ code: canonical,
481
+ message: msg.slice(pipeIdx + 1),
482
+ };
483
+ }
484
+ /** Deterministic renderer — turns a `SecurityPentestRunOutput` into
485
+ * the three-panel HTML report the course showcase renders (findings
486
+ * card / PoC replay accordion / patch diff viewer). Exposed on the
487
+ * SDK so course pipelines don't have to re-derive the layout in user
488
+ * code. Emits a self-contained HTML document: inlined CSS, no
489
+ * external dependencies, deterministic node order, safe to
490
+ * `iframe`-embed. */
491
+ export function renderPentestReportHtml(out, options) {
492
+ const title = escapeHtml(options?.title ?? `Pen-Test Report · ${out.target}`);
493
+ const findings = out.findings.map(renderFindingCard).join("\n");
494
+ const pocs = out.pocs.map(renderPocCard).join("\n");
495
+ const patch = renderPatchCard(out.patch);
496
+ return [
497
+ `<!DOCTYPE html>`,
498
+ `<html lang="en">`,
499
+ `<head>`,
500
+ `<meta charset="utf-8"/>`,
501
+ `<meta name="viewport" content="width=device-width, initial-scale=1"/>`,
502
+ `<title>${title}</title>`,
503
+ `<style>${PENTEST_REPORT_CSS}</style>`,
504
+ `</head>`,
505
+ `<body>`,
506
+ `<main class="pt-report">`,
507
+ `<header><h1>${title}</h1><p class="meta">${out.findings.length} findings · ${out.pocs.length} PoCs · ${out.costCredits} credits</p></header>`,
508
+ `<section class="findings"><h2>Findings</h2>${findings}</section>`,
509
+ `<section class="pocs"><h2>Proof-of-concept replay</h2>${pocs}</section>`,
510
+ `<section class="patch"><h2>Remediation patch</h2>${patch}</section>`,
511
+ `</main>`,
512
+ `</body>`,
513
+ `</html>`,
514
+ ].join("\n");
515
+ }
516
+ function renderFindingCard(f) {
517
+ const badge = escapeHtml(f.cvss.severity);
518
+ const score = f.cvss.score.toFixed(1);
519
+ return [
520
+ `<article class="finding sev-${badge}">`,
521
+ `<header>`,
522
+ `<span class="badge">${badge.toUpperCase()} · CVSS ${escapeHtml(score)}</span>`,
523
+ `<h3>${escapeHtml(f.title)}</h3>`,
524
+ `<p class="cat">${escapeHtml(f.owaspCategory)} · <code>${escapeHtml(f.affectedEndpoint)}</code></p>`,
525
+ `</header>`,
526
+ `<p class="impact">${escapeHtml(f.impact)}</p>`,
527
+ `<p class="fix"><strong>Fix:</strong> ${escapeHtml(f.remediation)}</p>`,
528
+ `</article>`,
529
+ ].join("");
530
+ }
531
+ function renderPocCard(p) {
532
+ return [
533
+ `<details class="poc">`,
534
+ `<summary><code>${escapeHtml(p.findingId)}</code> · ${escapeHtml(p.kind)}</summary>`,
535
+ `<pre>${escapeHtml(p.body)}</pre>`,
536
+ `<p class="indicator"><strong>Look for:</strong> ${escapeHtml(p.expectedIndicator)}</p>`,
537
+ `</details>`,
538
+ ].join("");
539
+ }
540
+ function renderPatchCard(patch) {
541
+ const branch = escapeHtml(patch.branchSuggestion);
542
+ return [
543
+ `<p class="branch">Suggested branch: <code>${branch}</code></p>`,
544
+ `<pre class="diff">${escapeHtml(patch.diff)}</pre>`,
545
+ ].join("");
546
+ }
547
+ /** Minimal HTML escape — enough for user-supplied strings coming out
548
+ * of the pentest run. We escape `&`, `<`, `>`, `"`, `'`. */
549
+ function escapeHtml(s) {
550
+ return s
551
+ .replace(/&/g, "&amp;")
552
+ .replace(/</g, "&lt;")
553
+ .replace(/>/g, "&gt;")
554
+ .replace(/"/g, "&quot;")
555
+ .replace(/'/g, "&#39;");
556
+ }
557
+ const PENTEST_REPORT_CSS = `
558
+ :root { --fg: #0f172a; --muted: #64748b; --card: #ffffff; --edge: #e2e8f0; --crit: #b91c1c; --high: #c2410c; --med: #a16207; --low: #0369a1; }
559
+ body { margin: 0; font: 15px/1.55 system-ui, -apple-system, "Segoe UI", sans-serif; color: var(--fg); background: #f8fafc; }
560
+ .pt-report { max-width: 960px; margin: 0 auto; padding: 32px 24px 64px; }
561
+ .pt-report > header h1 { font-size: 22px; margin: 0 0 4px; }
562
+ .pt-report .meta { color: var(--muted); margin: 0 0 24px; }
563
+ .pt-report section { margin-top: 28px; }
564
+ .pt-report section > h2 { font-size: 14px; text-transform: uppercase; letter-spacing: .08em; color: var(--muted); margin: 0 0 12px; }
565
+ .finding { background: var(--card); border: 1px solid var(--edge); border-radius: 12px; padding: 16px 18px; margin-bottom: 12px; }
566
+ .finding header { display: flex; flex-direction: column; gap: 4px; margin-bottom: 8px; }
567
+ .finding .badge { font-size: 11px; padding: 3px 8px; border-radius: 999px; background: #f1f5f9; color: var(--fg); align-self: flex-start; font-weight: 600; }
568
+ .finding.sev-critical .badge { background: #fee2e2; color: var(--crit); }
569
+ .finding.sev-high .badge { background: #ffedd5; color: var(--high); }
570
+ .finding.sev-medium .badge { background: #fef3c7; color: var(--med); }
571
+ .finding.sev-low .badge { background: #e0f2fe; color: var(--low); }
572
+ .finding h3 { margin: 0; font-size: 16px; }
573
+ .finding .cat { margin: 0; color: var(--muted); font-size: 13px; }
574
+ .finding .cat code { background: #f1f5f9; padding: 2px 6px; border-radius: 6px; }
575
+ .finding .impact { margin: 6px 0 8px; }
576
+ .finding .fix { margin: 0; color: var(--muted); }
577
+ .poc { background: var(--card); border: 1px solid var(--edge); border-radius: 10px; padding: 10px 14px; margin-bottom: 10px; }
578
+ .poc summary { cursor: pointer; font-weight: 500; }
579
+ .poc pre { background: #0f172a; color: #e2e8f0; padding: 12px 14px; border-radius: 8px; overflow-x: auto; font: 13px/1.5 ui-monospace, SFMono-Regular, monospace; }
580
+ .poc .indicator { margin: 8px 0 0; color: var(--muted); font-size: 13px; }
581
+ .patch .branch { margin: 0 0 8px; color: var(--muted); }
582
+ .patch .branch code { background: #f1f5f9; padding: 2px 6px; border-radius: 6px; }
583
+ .patch pre.diff { background: #0f172a; color: #e2e8f0; padding: 14px 16px; border-radius: 10px; overflow-x: auto; font: 13px/1.55 ui-monospace, SFMono-Regular, monospace; }
584
+ `;
585
+ //# sourceMappingURL=pentest.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pentest.js","sourceRoot":"","sources":["../src/pentest.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AAGH,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAUnD;;;;mEAImE;AACnE,MAAM,CAAC,MAAM,mBAAmB,GAAG;IACjC,sBAAsB;IACtB,uBAAuB;IACvB,mBAAmB;IACnB,yBAAyB;IACzB,aAAa;IACb,WAAW;IACX,iBAAiB;IACjB,oBAAoB;CACZ,CAAC;AAGX,qEAAqE;AACrE,MAAM,OAAO,YAAa,SAAQ,aAAa;IAC7C,YAAY,IAAsB,EAAE,OAAe;QACjD,KAAK,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;QACrB,IAAI,CAAC,IAAI,GAAG,cAAc,CAAC;IAC7B,CAAC;CACF;AAED;;wDAEwD;AACxD,MAAM,OAAO,sBAAuB,SAAQ,YAAY;IACtD,YACE,OAAO,GAAG,2HAA2H;QAErI,KAAK,CAAC,sBAAsB,EAAE,OAAO,CAAC,CAAC;QACvC,IAAI,CAAC,IAAI,GAAG,wBAAwB,CAAC;IACvC,CAAC;CACF;AAED;;sEAEsE;AACtE,MAAM,OAAO,sBAAuB,SAAQ,YAAY;IACtD,YACE,OAAO,GAAG,+FAA+F;QAEzG,KAAK,CAAC,uBAAuB,EAAE,OAAO,CAAC,CAAC;QACxC,IAAI,CAAC,IAAI,GAAG,wBAAwB,CAAC;IACvC,CAAC;CACF;AAED;oEACoE;AACpE,MAAM,OAAO,mBAAoB,SAAQ,YAAY;IACnD,YACE,OAAO,GAAG,yGAAyG;QAEnH,KAAK,CAAC,mBAAmB,EAAE,OAAO,CAAC,CAAC;QACpC,IAAI,CAAC,IAAI,GAAG,qBAAqB,CAAC;IACpC,CAAC;CACF;AAED;;0BAE0B;AAC1B,MAAM,OAAO,yBAA0B,SAAQ,YAAY;IACzD,YACE,OAAO,GAAG,uGAAuG;QAEjH,KAAK,CAAC,yBAAyB,EAAE,OAAO,CAAC,CAAC;QAC1C,IAAI,CAAC,IAAI,GAAG,2BAA2B,CAAC;IAC1C,CAAC;CACF;AAED;qEACqE;AACrE,MAAM,OAAO,aAAc,SAAQ,YAAY;IAC7C,YACE,OAAO,GAAG,kGAAkG;QAE5G,KAAK,CAAC,aAAa,EAAE,OAAO,CAAC,CAAC;QAC9B,IAAI,CAAC,IAAI,GAAG,eAAe,CAAC;IAC9B,CAAC;CACF;AAED;;+CAE+C;AAC/C,MAAM,OAAO,mBAAoB,SAAQ,YAAY;IACnD,YACE,OAAO,GAAG,oGAAoG;QAE9G,KAAK,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC;QAC5B,IAAI,CAAC,IAAI,GAAG,qBAAqB,CAAC;IACpC,CAAC;CACF;AAED;;kEAEkE;AAClE,MAAM,OAAO,iBAAkB,SAAQ,YAAY;IACjD,YAAY,OAAe;QACzB,KAAK,CAAC,iBAAiB,EAAE,OAAO,CAAC,CAAC;QAClC,IAAI,CAAC,IAAI,GAAG,mBAAmB,CAAC;IAClC,CAAC;CACF;AAED;;;;;sBAKsB;AACtB,MAAM,OAAO,0BAA2B,SAAQ,YAAY;IAC1D,YACE,OAAO,GAAG,sIAAsI;QAEhJ,KAAK,CAAC,oBAAoB,EAAE,OAAO,CAAC,CAAC;QACrC,IAAI,CAAC,IAAI,GAAG,4BAA4B,CAAC;IAC3C,CAAC;CACF;AAED;;;;;;;wCAOwC;AACxC,MAAM,UAAU,eAAe,CAAC,IAAY,EAAE,OAAe;IAC3D,MAAM,UAAU,GAAG,oBAAoB,CAAC,IAAI,CAAC,CAAC;IAC9C,QAAQ,UAAU,EAAE,CAAC;QACnB,KAAK,sBAAsB;YACzB,OAAO,IAAI,sBAAsB,CAAC,OAAO,CAAC,CAAC;QAC7C,KAAK,uBAAuB;YAC1B,OAAO,IAAI,sBAAsB,CAAC,OAAO,CAAC,CAAC;QAC7C,KAAK,mBAAmB;YACtB,OAAO,IAAI,mBAAmB,CAAC,OAAO,CAAC,CAAC;QAC1C,KAAK,yBAAyB;YAC5B,OAAO,IAAI,yBAAyB,CAAC,OAAO,CAAC,CAAC;QAChD,KAAK,aAAa;YAChB,OAAO,IAAI,aAAa,CAAC,OAAO,CAAC,CAAC;QACpC,KAAK,WAAW;YACd,OAAO,IAAI,mBAAmB,CAAC,OAAO,CAAC,CAAC;QAC1C,KAAK,iBAAiB;YACpB,OAAO,IAAI,iBAAiB,CAAC,OAAO,CAAC,CAAC;QACxC,KAAK,oBAAoB;YACvB,OAAO,IAAI,0BAA0B,CAAC,OAAO,CAAC,CAAC;QACjD;YACE,OAAO,IAAI,YAAY,CAAC,IAAwB,EAAE,OAAO,CAAC,CAAC;IAC/D,CAAC;AACH,CAAC;AAED;;;gEAGgE;AAChE,SAAS,oBAAoB,CAAC,IAAY;IACxC,IAAK,mBAAyC,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;QAC9D,OAAO,IAAwB,CAAC;IAClC,CAAC;IACD,MAAM,MAAM,GAAG,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC;QAClC,CAAC,CAAC,IAAI;QACN,CAAC,CAAC,KAAK,IAAI,CAAC,WAAW,EAAE,EAAE,CAAC;IAC9B,IAAK,mBAAyC,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;QAChE,OAAO,MAA0B,CAAC;IACpC,CAAC;IACD,sEAAsE;IACtE,kEAAkE;IAClE,6DAA6D;IAC7D,IAAI,IAAI,KAAK,mBAAmB,IAAI,IAAI,KAAK,WAAW,EAAE,CAAC;QACzD,OAAO,oBAAoB,CAAC;IAC9B,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAsBD;;sEAEsE;AACtE,MAAM,UAAU,iBAAiB,CAAC,OAAmB;IACnD,IAAI,MAAM,CAAC,SAAS,CAAC,cAAc,CAAC,IAAI,CAAC,iBAAiB,CAAC,SAAS,EAAE,YAAY,CAAC,EAAE,CAAC;QACpF,OAAO;IACT,CAAC;IACD,KAAK,UAAU,cAAc,CAE3B,KAA8B;QAE9B,iBAAiB,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;QACnC,oBAAoB,CAAC,KAAK,CAAC,SAAS,EAAE,KAAK,CAAC,YAAY,CAAC,CAAC;QAC1D,MAAM,QAAQ,GAAG,MAAM,YAAY,EAAE,CAAC;QACtC,IAAI,CAAC,QAAQ,EAAE,CAAC;YACd,MAAM,IAAI,mBAAmB,CAC3B,oHAAoH,CACrH,CAAC;QACJ,CAAC;QACD,MAAM,eAAe,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;QACvC,MAAM,SAAS,GAAG,MAAM,gBAAgB,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;QAC1D,MAAM,UAAU,GAAG,MAAM,gBAAgB,CAAC,IAAI,CAAC,qBAAqB,CAAC,KAAK,CAAC,CAAC,CAAC;QAC7E,OAAO,QAAQ,CAAC,KAAK,EAAE,UAAU,EAAE,SAAS,EAAE,QAAQ,CAAC,CAAC;IAC1D,CAAC;IACD,MAAM,CAAC,cAAc,CAAC,iBAAiB,CAAC,SAAS,EAAE,YAAY,EAAE;QAC/D,KAAK,EAAE,cAAc;QACrB,QAAQ,EAAE,IAAI;QACd,YAAY,EAAE,IAAI;KACnB,CAAC,CAAC;AACL,CAAC;AAWD,KAAK,UAAU,YAAY;IACzB,IAAI,CAAC;QACH,MAAM,CAAC,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,EAAE,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;YAC3C,MAAM,CAAC,oBAAoB,CAAC;YAC5B,MAAM,CAAC,kBAAkB,CAAC;YAC1B,MAAM,CAAC,WAAW,CAAC;YACnB,MAAM,CAAC,SAAS,CAAC;SAClB,CAAC,CAAC;QACH,OAAO,EAAE,KAAK,EAAE,EAAE,CAAC,KAAK,EAAE,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC;IAC3C,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED;oDACoD;AACpD,SAAS,iBAAiB,CAAC,SAA+C;IACxE,IAAI,CAAC,SAAS,IAAI,SAAS,CAAC,SAAS,KAAK,IAAI,EAAE,CAAC;QAC/C,MAAM,IAAI,yBAAyB,EAAE,CAAC;IACxC,CAAC;IACD,MAAM,SAAS,GAAG,SAAS,CAAC,SAAS,EAAE,IAAI,EAAE,CAAC;IAC9C,IAAI,CAAC,SAAS,IAAI,SAAS,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACvC,MAAM,IAAI,yBAAyB,CACjC,6FAA6F,CAC9F,CAAC;IACJ,CAAC;AACH,CAAC;AAED;;;qCAGqC;AACrC,SAAS,oBAAoB,CAAC,SAAiB,EAAE,YAAsB;IACrE,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,YAAY,CAAC,IAAI,YAAY,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC9D,MAAM,IAAI,sBAAsB,CAC9B,kHAAkH,CACnH,CAAC;IACJ,CAAC;IACD,IAAI,MAAW,CAAC;IAChB,IAAI,CAAC;QACH,MAAM,GAAG,IAAI,GAAG,CAAC,SAAS,CAAC,CAAC;IAC9B,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,IAAI,sBAAsB,CAC9B,mFAAmF,CACpF,CAAC;IACJ,CAAC;IACD,IAAI,MAAM,CAAC,QAAQ,KAAK,OAAO,IAAI,MAAM,CAAC,QAAQ,KAAK,QAAQ,EAAE,CAAC;QAChE,MAAM,IAAI,sBAAsB,CAC9B,2DAA2D,CAC5D,CAAC;IACJ,CAAC;IACD,MAAM,WAAW,GAAG,MAAM,CAAC,IAAI;QAC7B,CAAC,CAAC,GAAG,MAAM,CAAC,QAAQ,IAAI,MAAM,CAAC,IAAI,EAAE;QACrC,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC;IACpB,IAAI,CAAC,YAAY,CAAC,QAAQ,CAAC,WAAW,CAAC,IAAI,CAAC,YAAY,CAAC,QAAQ,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC;QACnF,MAAM,IAAI,sBAAsB,CAC9B,sBAAsB,WAAW,2DAA2D,CAC7F,CAAC;IACJ,CAAC;AACH,CAAC;AAED;0DAC0D;AAC1D,KAAK,UAAU,eAAe,CAAC,SAAiB;IAC9C,MAAM,IAAI,GAAG,IAAI,eAAe,EAAE,CAAC;IACnC,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,KAAK,EAAE,EAAE,IAAI,CAAC,CAAC;IACnD,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,SAAS,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,CAAC,CAAC;QAChG,IAAI,CAAC,GAAG,CAAC,EAAE,IAAI,GAAG,CAAC,MAAM,IAAI,GAAG,EAAE,CAAC;YACjC,MAAM,IAAI,sBAAsB,CAC9B,yBAAyB,GAAG,CAAC,MAAM,sCAAsC,CAC1E,CAAC;QACJ,CAAC;IACH,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,IAAI,GAAG,YAAY,sBAAsB;YAAE,MAAM,GAAG,CAAC;QACrD,MAAM,IAAI,sBAAsB,CAC9B,mBAAmB,SAAS,+EAA+E,CAC5G,CAAC;IACJ,CAAC;YAAS,CAAC;QACT,YAAY,CAAC,KAAK,CAAC,CAAC;IACtB,CAAC;AACH,CAAC;AAED;;4BAE4B;AAC5B,KAAK,UAAU,gBAAgB,CAC7B,KAA8B,EAC9B,QAAkB;IAElB,MAAM,GAAG,GAAG,KAAK,CAAC,SAAS,IAAI,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,YAAY,CAAC,CAAC;IAC/E,MAAM,QAAQ,CAAC,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAClD,OAAO,GAAG,CAAC;AACb,CAAC;AAED;;;6BAG6B;AAC7B,KAAK,UAAU,QAAQ,CACrB,KAA8B,EAC9B,IAAyC,EACzC,SAAiB,EACjB,QAAkB;IAElB,MAAM,MAAM,GAAG,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC;IACzD,MAAM,QAAQ,CAAC,EAAE,CAAC,KAAK,CAAC,MAAM,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACrD,MAAM,UAAU,GAAG,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,kBAAkB,CAAC,CAAC;IAClE,MAAM,UAAU,GAAG,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,kBAAkB,CAAC,CAAC;IAElE,MAAM,GAAG,GAAG;QACV,GAAG,OAAO,CAAC,GAAG;QACd,WAAW,EAAE,IAAI,CAAC,YAAY;QAC9B,SAAS,EAAE,GAAG,IAAI,CAAC,QAAQ,IAAI,IAAI,CAAC,KAAK,EAAE;QAC3C,GAAG,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,EAAE,YAAY,EAAE,IAAI,CAAC,aAAa,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACnE,cAAc,EAAE,IAAI,CAAC,KAAK;QAC1B,gBAAgB,EAAE,MAAM;KACzB,CAAC;IAEF,MAAM,IAAI,GAAG;QACX,UAAU;QACV,KAAK,CAAC,SAAS;QACf,aAAa;QACb,UAAU;QACV,mBAAmB;KACpB,CAAC;IAEF,MAAM,aAAa,GAAG,KAAK,CAAC,aAAa,IAAI,EAAE,CAAC;IAChD,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,CAAC,aAAa,GAAG,OAAO,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC;IAExE,OAAO,IAAI,OAAO,CAA2B,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QAC/D,IAAI,MAAM,GAAG,KAAK,CAAC;QACnB,MAAM,KAAK,GAAG,QAAQ,CAAC,KAAK,CAAC,OAAO,EAAE,IAAI,EAAE;YAC1C,GAAG;YACH,GAAG,EAAE,SAAS;YACd,KAAK,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC;SAClC,CAAC,CAAC;QAEH,kEAAkE;QAClE,yDAAyD;QACzD,IAAI,UAAU,GAAG,EAAE,CAAC;QACpB,KAAK,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,EAAE,KAAK,EAAE,KAAa,EAAE,EAAE;YAC/C,IAAI,CAAC;gBAAC,MAAM,QAAQ,CAAC,EAAE,CAAC,UAAU,CAAC,UAAU,EAAE,KAAK,CAAC,CAAC;YAAC,CAAC;YAAC,MAAM,CAAC,CAAC,iBAAiB,CAAC,CAAC;QACtF,CAAC,CAAC,CAAC;QACH,KAAK,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,EAAE,KAAK,EAAE,KAAa,EAAE,EAAE;YAC/C,IAAI,CAAC;gBAAC,MAAM,QAAQ,CAAC,EAAE,CAAC,UAAU,CAAC,UAAU,EAAE,KAAK,CAAC,CAAC;YAAC,CAAC;YAAC,MAAM,CAAC,CAAC,iBAAiB,CAAC,CAAC;YACpF,UAAU,GAAG,CAAC,UAAU,GAAG,KAAK,CAAC,QAAQ,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,CAAC;QAC5D,CAAC,CAAC,CAAC;QAEH,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE;YAC5B,MAAM,GAAG,IAAI,CAAC;YACd,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QACxB,CAAC,EAAE,SAAS,CAAC,CAAC;QAEd,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,EAAE;YACxB,YAAY,CAAC,KAAK,CAAC,CAAC;YACpB,IAAK,GAA6B,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;gBACrD,MAAM,CAAC,IAAI,mBAAmB,CAC5B,oFAAoF,CACrF,CAAC,CAAC;gBACH,OAAO;YACT,CAAC;YACD,MAAM,CAAC,IAAI,iBAAiB,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC;QAC7C,CAAC,CAAC,CAAC;QAEH,KAAK,CAAC,EAAE,CAAC,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE;YAC9B,YAAY,CAAC,KAAK,CAAC,CAAC;YACpB,IAAI,MAAM,EAAE,CAAC;gBACX,MAAM,CAAC,IAAI,mBAAmB,EAAE,CAAC,CAAC;gBAClC,OAAO;YACT,CAAC;YACD,IAAI,IAAI,KAAK,CAAC,EAAE,CAAC;gBACf,MAAM,CAAC,IAAI,iBAAiB,CAC1B,0BAA0B,IAAI,mBAAmB,UAAU,IAAI,SAAS,EAAE,CAC3E,CAAC,CAAC;gBACH,OAAO;YACT,CAAC;YACD,IAAI,CAAC;gBACH,MAAM,GAAG,GAAG,MAAM,aAAa,CAAC,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAC;gBAC/D,OAAO,CAAC,GAAG,CAAC,CAAC;YACf,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,MAAM,CAAC,IAAI,iBAAiB,CAC1B,sDAAuD,GAAa,CAAC,OAAO,EAAE,CAC/E,CAAC,CAAC;YACL,CAAC;QACH,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC;AAED;;;8CAG8C;AAC9C,KAAK,UAAU,aAAa,CAC1B,IAAyC,EACzC,KAA8B,EAC9B,MAAc,EACd,QAAkB;IAElB,MAAM,UAAU,GAAG,MAAM,cAAc,CAIpC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,aAAa,CAAC,EAAE,QAAQ,CAAC,CAAC;IAExD,MAAM,QAAQ,GAAG,UAAU,EAAE,QAAQ,IAAI,EAAE,CAAC;IAC5C,MAAM,IAAI,GAAG,UAAU,EAAE,IAAI,IAAI,EAAE,CAAC;IACpC,MAAM,KAAK,GAAyB,UAAU,EAAE,KAAK,IAAI;QACvD,IAAI,EAAE,EAAE;QACR,gBAAgB,EAAE,WAAW,IAAI,CAAC,KAAK,EAAE;KAC1C,CAAC;IAEF,MAAM,cAAc,GAAG,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;IACjE,IAAI,CAAC,CAAC,MAAM,UAAU,CAAC,cAAc,EAAE,QAAQ,CAAC,CAAC,EAAE,CAAC;QAClD,gEAAgE;QAChE,iDAAiD;QACjD,MAAM,QAAQ,CAAC,EAAE,CAAC,SAAS,CAAC,cAAc,EAAE,uBAAuB,CAAC;YAClE,KAAK,EAAE,IAAI,CAAC,KAAK;YACjB,MAAM,EAAE,KAAK,CAAC,SAAS;YACvB,QAAQ;YACR,IAAI;YACJ,KAAK;YACL,cAAc,EAAE,EAAE;YAClB,WAAW,EAAE,IAAI,CAAC,aAAa;SAChC,CAAC,CAAC,CAAC;IACN,CAAC;IAED,OAAO;QACL,KAAK,EAAE,IAAI,CAAC,KAAK;QACjB,MAAM,EAAE,KAAK,CAAC,SAAS;QACvB,QAAQ;QACR,IAAI;QACJ,KAAK;QACL,cAAc;QACd,WAAW,EAAE,IAAI,CAAC,aAAa;KAChC,CAAC;AACJ,CAAC;AAED,KAAK,UAAU,cAAc,CAC3B,QAAgB,EAChB,QAAkB;IAElB,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,EAAE,CAAC,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;QAC3D,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAM,CAAC;IAC/B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,KAAK,UAAU,UAAU,CAAC,QAAgB,EAAE,QAAkB;IAC5D,IAAI,CAAC;QACH,MAAM,QAAQ,CAAC,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QACjC,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED;;;;;;qEAMqE;AACrE,KAAK,UAAU,gBAAgB,CAAI,CAAa;IAC9C,IAAI,CAAC;QACH,OAAO,MAAM,CAAC,CAAC;IACjB,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,IAAI,GAAG,YAAY,YAAY;YAAE,MAAM,GAAG,CAAC;QAC3C,MAAM,QAAQ,GAAG,oBAAoB,CAAC,GAAG,CAAC,CAAC;QAC3C,IAAI,QAAQ;YAAE,MAAM,QAAQ,CAAC;QAC7B,MAAM,GAAG,CAAC;IACZ,CAAC;AACH,CAAC;AAED;qEACqE;AACrE,SAAS,oBAAoB,CAAC,GAAY;IACxC,IAAI,GAAG,YAAY,aAAa,EAAE,CAAC;QACjC,OAAO,eAAe,CAAC,GAAG,CAAC,IAAI,EAAE,GAAG,CAAC,OAAO,CAAC,CAAC;IAChD,CAAC;IACD,IAAI,GAAG,YAAY,KAAK,EAAE,CAAC;QACzB,MAAM,MAAM,GAAG,2BAA2B,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QACxD,IAAI,MAAM;YAAE,OAAO,eAAe,CAAC,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,OAAO,CAAC,CAAC;QAChE,6DAA6D;QAC7D,+DAA+D;QAC/D,gEAAgE;QAChE,MAAM,GAAG,GAAG,GAAG,CAAC,OAAO,CAAC,WAAW,EAAE,CAAC;QACtC,IAAI,GAAG,CAAC,QAAQ,CAAC,mBAAmB,CAAC,IAAI,GAAG,CAAC,QAAQ,CAAC,WAAW,CAAC,EAAE,CAAC;YACnE,OAAO,IAAI,0BAA0B,EAAE,CAAC;QAC1C,CAAC;IACH,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;;+CAI+C;AAC/C,MAAM,UAAU,2BAA2B,CACzC,GAAW;IAEX,MAAM,OAAO,GAAG,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IACjC,IAAI,OAAO,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC;IAC9B,MAAM,SAAS,GAAG,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC;IACxC,MAAM,SAAS,GAAG,oBAAoB,CAAC,SAAS,CAAC,CAAC;IAClD,IAAI,CAAE,mBAAyC,CAAC,QAAQ,CAAC,SAAS,CAAC;QAAE,OAAO,IAAI,CAAC;IACjF,OAAO;QACL,IAAI,EAAE,SAA6B;QACnC,OAAO,EAAE,GAAG,CAAC,KAAK,CAAC,OAAO,GAAG,CAAC,CAAC;KAChC,CAAC;AACJ,CAAC;AAED;;;;;;sBAMsB;AACtB,MAAM,UAAU,uBAAuB,CACrC,GAA6B,EAC7B,OAA4B;IAE5B,MAAM,KAAK,GAAG,UAAU,CAAC,OAAO,EAAE,KAAK,IAAI,qBAAqB,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC;IAC9E,MAAM,QAAQ,GAAG,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,iBAAiB,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAChE,MAAM,IAAI,GAAG,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACpD,MAAM,KAAK,GAAG,eAAe,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;IACzC,OAAO;QACL,iBAAiB;QACjB,kBAAkB;QAClB,QAAQ;QACR,yBAAyB;QACzB,uEAAuE;QACvE,UAAU,KAAK,UAAU;QACzB,UAAU,kBAAkB,UAAU;QACtC,SAAS;QACT,QAAQ;QACR,0BAA0B;QAC1B,eAAe,KAAK,wBAAwB,GAAG,CAAC,QAAQ,CAAC,MAAM,eAAe,GAAG,CAAC,IAAI,CAAC,MAAM,WAAW,GAAG,CAAC,WAAW,uBAAuB;QAC9I,8CAA8C,QAAQ,YAAY;QAClE,yDAAyD,IAAI,YAAY;QACzE,oDAAoD,KAAK,YAAY;QACrE,SAAS;QACT,SAAS;QACT,SAAS;KACV,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACf,CAAC;AAED,SAAS,iBAAiB,CAAC,CAAyB;IAClD,MAAM,KAAK,GAAG,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IAC1C,MAAM,KAAK,GAAG,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;IACtC,OAAO;QACL,+BAA+B,KAAK,IAAI;QACxC,UAAU;QACV,uBAAuB,KAAK,CAAC,WAAW,EAAE,WAAW,UAAU,CAAC,KAAK,CAAC,SAAS;QAC/E,OAAO,UAAU,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO;QACjC,kBAAkB,UAAU,CAAC,CAAC,CAAC,aAAa,CAAC,YAAY,UAAU,CAAC,CAAC,CAAC,gBAAgB,CAAC,aAAa;QACpG,WAAW;QACX,qBAAqB,UAAU,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM;QAC/C,wCAAwC,UAAU,CAAC,CAAC,CAAC,WAAW,CAAC,MAAM;QACvE,YAAY;KACb,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;AACb,CAAC;AAED,SAAS,aAAa,CAAC,CAAqB;IAC1C,OAAO;QACL,uBAAuB;QACvB,kBAAkB,UAAU,CAAC,CAAC,CAAC,SAAS,CAAC,aAAa,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,YAAY;QACpF,QAAQ,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ;QAClC,mDAAmD,UAAU,CAAC,CAAC,CAAC,iBAAiB,CAAC,MAAM;QACxF,YAAY;KACb,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;AACb,CAAC;AAED,SAAS,eAAe,CAAC,KAA2B;IAClD,MAAM,MAAM,GAAG,UAAU,CAAC,KAAK,CAAC,gBAAgB,CAAC,CAAC;IAClD,OAAO;QACL,6CAA6C,MAAM,aAAa;QAChE,qBAAqB,UAAU,CAAC,KAAK,CAAC,IAAI,CAAC,QAAQ;KACpD,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;AACb,CAAC;AAED;6DAC6D;AAC7D,SAAS,UAAU,CAAC,CAAS;IAC3B,OAAO,CAAC;SACL,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC;SACtB,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC;SACrB,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC;SACrB,OAAO,CAAC,IAAI,EAAE,QAAQ,CAAC;SACvB,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;AAC5B,CAAC;AAED,MAAM,kBAAkB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;CA2B1B,CAAC"}