@vigolium/piolium 0.0.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.
- package/LICENSE +21 -0
- package/README.md +117 -0
- package/agents/access-auditor.md +300 -0
- package/agents/assumption-breaker.md +154 -0
- package/agents/attack-designer.md +116 -0
- package/agents/code-scanner.md +139 -0
- package/agents/concurrency-auditor.md +238 -0
- package/agents/confirm-writer.md +257 -0
- package/agents/context-reviewer.md +274 -0
- package/agents/cross-verifier.md +165 -0
- package/agents/cve-scout.md +381 -0
- package/agents/env-builder.md +282 -0
- package/agents/env-profiler.md +205 -0
- package/agents/evidence-collector.md +140 -0
- package/agents/finding-grader.md +142 -0
- package/agents/finding-writer.md +148 -0
- package/agents/flow-tracer.md +106 -0
- package/agents/goal-backtracer.md +146 -0
- package/agents/history-miner.md +467 -0
- package/agents/independent-verifier.md +118 -0
- package/agents/intent-mapper.md +183 -0
- package/agents/longshot-collector.md +128 -0
- package/agents/longshot-prober.md +126 -0
- package/agents/patch-auditor.md +73 -0
- package/agents/poc-author.md +124 -0
- package/agents/poc-runner.md +194 -0
- package/agents/probe-lead.md +269 -0
- package/agents/red-challenger.md +101 -0
- package/agents/report-composer.md +208 -0
- package/agents/review-adjudicator.md +216 -0
- package/agents/spec-auditor.md +155 -0
- package/agents/taint-tracer.md +265 -0
- package/agents/test-locator.md +209 -0
- package/agents/threat-modeler.md +132 -0
- package/agents/variant-scanner.md +108 -0
- package/agents/variant-spotter.md +110 -0
- package/bin/piolium.mjs +376 -0
- package/extensions/piolium/_vendor/yaml.bundle.d.mts +6 -0
- package/extensions/piolium/_vendor/yaml.bundle.mjs +139 -0
- package/extensions/piolium/agent-runner.ts +322 -0
- package/extensions/piolium/agents.ts +266 -0
- package/extensions/piolium/audit-state.ts +522 -0
- package/extensions/piolium/bundled-resources.ts +97 -0
- package/extensions/piolium/candidate-scan.ts +966 -0
- package/extensions/piolium/command-target.ts +177 -0
- package/extensions/piolium/console-stream.ts +57 -0
- package/extensions/piolium/export-results.ts +380 -0
- package/extensions/piolium/findings.ts +448 -0
- package/extensions/piolium/heartbeat.ts +182 -0
- package/extensions/piolium/help.ts +234 -0
- package/extensions/piolium/index.ts +1865 -0
- package/extensions/piolium/longshot.ts +530 -0
- package/extensions/piolium/matcher-suggestions.ts +196 -0
- package/extensions/piolium/matcher-utils.ts +83 -0
- package/extensions/piolium/modes/balanced.ts +750 -0
- package/extensions/piolium/modes/confirm-bootstrap.ts +186 -0
- package/extensions/piolium/modes/confirm.ts +697 -0
- package/extensions/piolium/modes/deep.ts +917 -0
- package/extensions/piolium/modes/diff.ts +177 -0
- package/extensions/piolium/modes/lite.ts +540 -0
- package/extensions/piolium/modes/longshot.ts +595 -0
- package/extensions/piolium/modes/merge.ts +204 -0
- package/extensions/piolium/modes/phase-runner.ts +267 -0
- package/extensions/piolium/modes/reinvest.ts +546 -0
- package/extensions/piolium/modes/revisit.ts +279 -0
- package/extensions/piolium/modes.ts +48 -0
- package/extensions/piolium/phase-labels.ts +123 -0
- package/extensions/piolium/phase-status-strip.ts +92 -0
- package/extensions/piolium/prompt-prefix-editor.ts +39 -0
- package/extensions/piolium/providers/anthropic-vertex.ts +836 -0
- package/extensions/piolium/recon.ts +409 -0
- package/extensions/piolium/result-stats.ts +105 -0
- package/extensions/piolium/retry.ts +120 -0
- package/extensions/piolium/scheduler.ts +212 -0
- package/extensions/piolium/secrets.ts +368 -0
- package/extensions/piolium/tools/web-tools.ts +148 -0
- package/package.json +77 -0
- package/skills/agentic-actions-auditor/SKILL.md +327 -0
- package/skills/agentic-actions-auditor/references/action-profiles.md +186 -0
- package/skills/agentic-actions-auditor/references/cross-file-resolution.md +209 -0
- package/skills/agentic-actions-auditor/references/foundations.md +94 -0
- package/skills/agentic-actions-auditor/references/vector-a-env-var-intermediary.md +77 -0
- package/skills/agentic-actions-auditor/references/vector-b-direct-expression-injection.md +83 -0
- package/skills/agentic-actions-auditor/references/vector-c-cli-data-fetch.md +83 -0
- package/skills/agentic-actions-auditor/references/vector-d-pr-target-checkout.md +88 -0
- package/skills/agentic-actions-auditor/references/vector-e-error-log-injection.md +88 -0
- package/skills/agentic-actions-auditor/references/vector-f-subshell-expansion.md +82 -0
- package/skills/agentic-actions-auditor/references/vector-g-eval-of-ai-output.md +91 -0
- package/skills/agentic-actions-auditor/references/vector-h-dangerous-sandbox-configs.md +102 -0
- package/skills/agentic-actions-auditor/references/vector-i-wildcard-allowlists.md +88 -0
- package/skills/audit/SKILL.md +562 -0
- package/skills/audit/assets/icon.svg +7 -0
- package/skills/audit/hooks/scripts/validate_phase_output.py +550 -0
- package/skills/audit/references/adversarial-review.md +148 -0
- package/skills/audit/references/architecture-aware-sast.md +306 -0
- package/skills/audit/references/audit-workflow.md +737 -0
- package/skills/audit/references/chamber-protocol.md +384 -0
- package/skills/audit/references/creative-attack-modes.md +221 -0
- package/skills/audit/references/deep-analysis.md +273 -0
- package/skills/audit/references/domain-attack-playbooks.md +1129 -0
- package/skills/audit/references/knowledge-base-template.md +513 -0
- package/skills/audit/references/real-env-validation.md +191 -0
- package/skills/audit/references/report-templates.md +417 -0
- package/skills/audit/references/triage-and-prereqs.md +134 -0
- package/skills/audit/scripts/consolidate_drafts.py +554 -0
- package/skills/audit/scripts/partition_findings.py +152 -0
- package/skills/audit/scripts/rg-hotspots.sh +121 -0
- package/skills/audit/scripts/stamp_file_state.py +349 -0
- package/skills/code-reviewer/SKILL.md +65 -0
- package/skills/codeql/SKILL.md +281 -0
- package/skills/codeql/references/build-fixes.md +90 -0
- package/skills/codeql/references/diagnostic-query-templates.md +339 -0
- package/skills/codeql/references/extension-yaml-format.md +209 -0
- package/skills/codeql/references/important-only-suite.md +153 -0
- package/skills/codeql/references/language-details.md +207 -0
- package/skills/codeql/references/macos-arm64e-workaround.md +179 -0
- package/skills/codeql/references/performance-tuning.md +111 -0
- package/skills/codeql/references/quality-assessment.md +172 -0
- package/skills/codeql/references/ruleset-catalog.md +63 -0
- package/skills/codeql/references/run-all-suite.md +92 -0
- package/skills/codeql/references/sarif-processing.md +79 -0
- package/skills/codeql/references/threat-models.md +51 -0
- package/skills/codeql/workflows/build-database.md +280 -0
- package/skills/codeql/workflows/create-data-extensions.md +261 -0
- package/skills/codeql/workflows/run-analysis.md +301 -0
- package/skills/differential-review/SKILL.md +220 -0
- package/skills/differential-review/adversarial.md +203 -0
- package/skills/differential-review/methodology.md +234 -0
- package/skills/differential-review/patterns.md +300 -0
- package/skills/differential-review/reporting.md +369 -0
- package/skills/fp-check/SKILL.md +125 -0
- package/skills/fp-check/references/bug-class-verification.md +114 -0
- package/skills/fp-check/references/deep-verification.md +143 -0
- package/skills/fp-check/references/evidence-templates.md +91 -0
- package/skills/fp-check/references/false-positive-patterns.md +115 -0
- package/skills/fp-check/references/gate-reviews.md +27 -0
- package/skills/fp-check/references/standard-verification.md +78 -0
- package/skills/insecure-defaults/SKILL.md +117 -0
- package/skills/insecure-defaults/references/examples.md +409 -0
- package/skills/last30days/SKILL.md +444 -0
- package/skills/sarif-parsing/SKILL.md +483 -0
- package/skills/sarif-parsing/resources/jq-queries.md +162 -0
- package/skills/sarif-parsing/resources/sarif_helpers.py +331 -0
- package/skills/security-threat-model/LICENSE.txt +201 -0
- package/skills/security-threat-model/SKILL.md +81 -0
- package/skills/security-threat-model/agents/openai.yaml +4 -0
- package/skills/security-threat-model/references/prompt-template.md +255 -0
- package/skills/security-threat-model/references/security-controls-and-assets.md +32 -0
- package/skills/semgrep/SKILL.md +212 -0
- package/skills/semgrep/references/rulesets.md +162 -0
- package/skills/semgrep/references/scan-modes.md +110 -0
- package/skills/semgrep/references/scanner-task-prompt.md +140 -0
- package/skills/semgrep/scripts/merge_sarif.py +203 -0
- package/skills/semgrep/workflows/scan-workflow.md +311 -0
- package/skills/semgrep-rule-creator/SKILL.md +168 -0
- package/skills/semgrep-rule-creator/references/quick-reference.md +202 -0
- package/skills/semgrep-rule-creator/references/workflow.md +240 -0
- package/skills/semgrep-rule-variant-creator/SKILL.md +205 -0
- package/skills/semgrep-rule-variant-creator/references/applicability-analysis.md +250 -0
- package/skills/semgrep-rule-variant-creator/references/language-syntax-guide.md +324 -0
- package/skills/semgrep-rule-variant-creator/references/workflow.md +518 -0
- package/skills/sharp-edges/SKILL.md +292 -0
- package/skills/sharp-edges/references/auth-patterns.md +252 -0
- package/skills/sharp-edges/references/case-studies.md +274 -0
- package/skills/sharp-edges/references/config-patterns.md +333 -0
- package/skills/sharp-edges/references/crypto-apis.md +190 -0
- package/skills/sharp-edges/references/lang-c.md +205 -0
- package/skills/sharp-edges/references/lang-csharp.md +285 -0
- package/skills/sharp-edges/references/lang-go.md +270 -0
- package/skills/sharp-edges/references/lang-java.md +263 -0
- package/skills/sharp-edges/references/lang-javascript.md +269 -0
- package/skills/sharp-edges/references/lang-kotlin.md +265 -0
- package/skills/sharp-edges/references/lang-php.md +245 -0
- package/skills/sharp-edges/references/lang-python.md +274 -0
- package/skills/sharp-edges/references/lang-ruby.md +273 -0
- package/skills/sharp-edges/references/lang-rust.md +272 -0
- package/skills/sharp-edges/references/lang-swift.md +287 -0
- package/skills/sharp-edges/references/language-specific.md +588 -0
- package/skills/spec-to-code-compliance/SKILL.md +357 -0
- package/skills/spec-to-code-compliance/resources/COMPLETENESS_CHECKLIST.md +69 -0
- package/skills/spec-to-code-compliance/resources/IR_EXAMPLES.md +417 -0
- package/skills/spec-to-code-compliance/resources/OUTPUT_REQUIREMENTS.md +105 -0
- package/skills/supply-chain-risk-auditor/SKILL.md +67 -0
- package/skills/supply-chain-risk-auditor/resources/results-template.md +41 -0
- package/skills/variant-analysis/METHODOLOGY.md +327 -0
- package/skills/variant-analysis/SKILL.md +142 -0
- package/skills/variant-analysis/resources/codeql/cpp.ql +119 -0
- package/skills/variant-analysis/resources/codeql/go.ql +69 -0
- package/skills/variant-analysis/resources/codeql/java.ql +71 -0
- package/skills/variant-analysis/resources/codeql/javascript.ql +63 -0
- package/skills/variant-analysis/resources/codeql/python.ql +80 -0
- package/skills/variant-analysis/resources/semgrep/cpp.yaml +98 -0
- package/skills/variant-analysis/resources/semgrep/go.yaml +63 -0
- package/skills/variant-analysis/resources/semgrep/java.yaml +61 -0
- package/skills/variant-analysis/resources/semgrep/javascript.yaml +60 -0
- package/skills/variant-analysis/resources/semgrep/python.yaml +72 -0
- package/skills/variant-analysis/resources/variant-report-template.md +75 -0
- package/skills/vuln-report/SKILL.md +137 -0
- package/skills/vuln-report/agents/openai.yaml +4 -0
- package/skills/vuln-report/references/report-template.md +135 -0
- package/skills/wooyun-legacy/SKILL.md +367 -0
- package/skills/wooyun-legacy/references/bank-penetration.md +222 -0
- package/skills/wooyun-legacy/references/checklists/command-execution-checklist.md +119 -0
- package/skills/wooyun-legacy/references/checklists/csrf-checklist.md +74 -0
- package/skills/wooyun-legacy/references/checklists/file-upload-checklist.md +108 -0
- package/skills/wooyun-legacy/references/checklists/info-disclosure-checklist.md +114 -0
- package/skills/wooyun-legacy/references/checklists/logic-flaws-checklist.md +95 -0
- package/skills/wooyun-legacy/references/checklists/misconfig-checklist.md +124 -0
- package/skills/wooyun-legacy/references/checklists/path-traversal-checklist.md +87 -0
- package/skills/wooyun-legacy/references/checklists/rce-checklist.md +93 -0
- package/skills/wooyun-legacy/references/checklists/sql-injection-checklist.md +97 -0
- package/skills/wooyun-legacy/references/checklists/ssrf-checklist.md +99 -0
- package/skills/wooyun-legacy/references/checklists/unauthorized-access-checklist.md +89 -0
- package/skills/wooyun-legacy/references/checklists/weak-password-checklist.md +115 -0
- package/skills/wooyun-legacy/references/checklists/xss-checklist.md +103 -0
- package/skills/wooyun-legacy/references/checklists/xxe-checklist.md +130 -0
- package/skills/wooyun-legacy/references/info-disclosure.md +975 -0
- package/skills/wooyun-legacy/references/logic-flaws.md +721 -0
- package/skills/wooyun-legacy/references/path-traversal.md +1191 -0
- package/skills/wooyun-legacy/references/telecom-penetration.md +156 -0
- package/skills/wooyun-legacy/references/unauthorized-access.md +980 -0
- package/skills/wooyun-legacy/references/xss.md +746 -0
- package/skills/zeroize-audit/SKILL.md +371 -0
- package/skills/zeroize-audit/configs/c.yaml +21 -0
- package/skills/zeroize-audit/configs/default.yaml +128 -0
- package/skills/zeroize-audit/configs/rust.yaml +83 -0
- package/skills/zeroize-audit/prompts/report_template.md +238 -0
- package/skills/zeroize-audit/prompts/system.md +163 -0
- package/skills/zeroize-audit/prompts/task.md +97 -0
- package/skills/zeroize-audit/references/compile-commands.md +231 -0
- package/skills/zeroize-audit/references/detection-strategy.md +191 -0
- package/skills/zeroize-audit/references/ir-analysis.md +252 -0
- package/skills/zeroize-audit/references/mcp-analysis.md +221 -0
- package/skills/zeroize-audit/references/poc-generation.md +470 -0
- package/skills/zeroize-audit/references/rust-zeroization-patterns.md +867 -0
- package/skills/zeroize-audit/schemas/input.json +83 -0
- package/skills/zeroize-audit/schemas/output.json +140 -0
- package/skills/zeroize-audit/tools/analyze_asm.sh +202 -0
- package/skills/zeroize-audit/tools/analyze_cfg.py +381 -0
- package/skills/zeroize-audit/tools/analyze_heap.sh +211 -0
- package/skills/zeroize-audit/tools/analyze_ir_semantic.py +429 -0
- package/skills/zeroize-audit/tools/diff_ir.sh +135 -0
- package/skills/zeroize-audit/tools/diff_rust_mir.sh +189 -0
- package/skills/zeroize-audit/tools/emit_asm.sh +67 -0
- package/skills/zeroize-audit/tools/emit_ir.sh +77 -0
- package/skills/zeroize-audit/tools/emit_rust_asm.sh +178 -0
- package/skills/zeroize-audit/tools/emit_rust_ir.sh +150 -0
- package/skills/zeroize-audit/tools/emit_rust_mir.sh +158 -0
- package/skills/zeroize-audit/tools/extract_compile_flags.py +284 -0
- package/skills/zeroize-audit/tools/generate_poc.py +1329 -0
- package/skills/zeroize-audit/tools/mcp/apply_confidence_gates.py +113 -0
- package/skills/zeroize-audit/tools/mcp/check_mcp.sh +68 -0
- package/skills/zeroize-audit/tools/mcp/normalize_mcp_evidence.py +125 -0
- package/skills/zeroize-audit/tools/scripts/check_llvm_patterns.py +481 -0
- package/skills/zeroize-audit/tools/scripts/check_mir_patterns.py +554 -0
- package/skills/zeroize-audit/tools/scripts/check_rust_asm.py +424 -0
- package/skills/zeroize-audit/tools/scripts/check_rust_asm_aarch64.py +300 -0
- package/skills/zeroize-audit/tools/scripts/check_rust_asm_x86.py +283 -0
- package/skills/zeroize-audit/tools/scripts/find_dangerous_apis.py +375 -0
- package/skills/zeroize-audit/tools/scripts/semantic_audit.py +923 -0
- package/skills/zeroize-audit/tools/track_dataflow.sh +196 -0
- package/skills/zeroize-audit/tools/validate_rust_toolchain.sh +298 -0
- package/skills/zeroize-audit/workflows/phase-0-preflight.md +150 -0
- package/skills/zeroize-audit/workflows/phase-1-source-analysis.md +144 -0
- package/skills/zeroize-audit/workflows/phase-2-compiler-analysis.md +139 -0
- package/skills/zeroize-audit/workflows/phase-3-interim-report.md +46 -0
- package/skills/zeroize-audit/workflows/phase-4-poc-generation.md +46 -0
- package/skills/zeroize-audit/workflows/phase-5-poc-validation.md +136 -0
- package/skills/zeroize-audit/workflows/phase-6-final-report.md +44 -0
- package/skills/zeroize-audit/workflows/phase-7-test-generation.md +42 -0
- package/themes/piolium-srcery.json +94 -0
|
@@ -0,0 +1,540 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lite mode orchestrator (`/piolium-lite`).
|
|
3
|
+
*
|
|
4
|
+
* Pipeline (command-defs/lite.md, archon-audit @ 2026-05-16):
|
|
5
|
+
*
|
|
6
|
+
* L1 Recon Sweep → [L2 Secrets Scan ‖ L3 Fast Code Scan]
|
|
7
|
+
*
|
|
8
|
+
* All three phases have `agent: null` upstream — L1 and L2 run
|
|
9
|
+
* deterministically in-process, L3 spawns the `code-scanner` agent with a
|
|
10
|
+
* tightly-scoped lite-mode task. L2 and L3 race under the global cap.
|
|
11
|
+
*
|
|
12
|
+
* Upstream lite produces only draft findings and folds any consolidation /
|
|
13
|
+
* cleanup inline (no tracked PoC or cleanup phases — piolium's old Q3/Q4
|
|
14
|
+
* tail was dropped to match upstream exactly). piolium still consolidates
|
|
15
|
+
* surviving drafts into `findings/<ID>-<slug>/` and removes transient
|
|
16
|
+
* artifacts as an inline post-L3 pass so downstream modes have inputs.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
import { existsSync, mkdirSync, readdirSync, rmSync, writeFileSync } from "node:fs";
|
|
20
|
+
import { join } from "node:path";
|
|
21
|
+
import type { AgentRuntimeModel } from "../agent-runner.ts";
|
|
22
|
+
import { type AgentDefinition, loadAgents } from "../agents.ts";
|
|
23
|
+
import {
|
|
24
|
+
type AuditRunState,
|
|
25
|
+
applyPhaseStatus,
|
|
26
|
+
initAudit,
|
|
27
|
+
latestAudit,
|
|
28
|
+
markAuditStatus,
|
|
29
|
+
readAuditState,
|
|
30
|
+
} from "../audit-state.ts";
|
|
31
|
+
import {
|
|
32
|
+
candidateSummaryPath,
|
|
33
|
+
candidatesJsonlPath,
|
|
34
|
+
runCandidateScanAsync,
|
|
35
|
+
} from "../candidate-scan.ts";
|
|
36
|
+
import { consolidateDrafts, listFindingDirs } from "../findings.ts";
|
|
37
|
+
import { reconReportPath, runReconAsync } from "../recon.ts";
|
|
38
|
+
import { errorMessage, readNonNegativeIntEnv, readPositiveIntEnv, runWithRetry } from "../retry.ts";
|
|
39
|
+
import { Scheduler } from "../scheduler.ts";
|
|
40
|
+
import { Q1_SECRETS_SUMMARY, findingsDraftDir, runQ1SecretsScan } from "../secrets.ts";
|
|
41
|
+
import { type PhaseUiHooks, runAgentPhase } from "./phase-runner.ts";
|
|
42
|
+
|
|
43
|
+
export type LiteUiHooks = PhaseUiHooks;
|
|
44
|
+
|
|
45
|
+
export interface RunLiteOptions {
|
|
46
|
+
cwd: string;
|
|
47
|
+
signal?: AbortSignal;
|
|
48
|
+
ui?: LiteUiHooks;
|
|
49
|
+
/** When true, restart from scratch even if an in-progress audit exists. */
|
|
50
|
+
forceFresh?: boolean;
|
|
51
|
+
agentRuntime?: AgentRuntimeModel;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export interface RunLiteResult {
|
|
55
|
+
auditId: string;
|
|
56
|
+
status: "complete" | "failed";
|
|
57
|
+
phases: Record<string, "complete" | "failed" | "skipped">;
|
|
58
|
+
summaryPath: string;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export const LITE_ATTACK_SURFACE_DIR = "piolium/attack-surface";
|
|
62
|
+
export const L2_SUMMARY = Q1_SECRETS_SUMMARY;
|
|
63
|
+
export const L3_SUMMARY = `${LITE_ATTACK_SURFACE_DIR}/lite-l3-summary.md`;
|
|
64
|
+
export const LITE_CONSOLIDATION_MANIFEST = `${LITE_ATTACK_SURFACE_DIR}/lite-consolidation-manifest.json`;
|
|
65
|
+
export const LITE_VERIFICATION_SUMMARY = `${LITE_ATTACK_SURFACE_DIR}/lite-verification-summary.md`;
|
|
66
|
+
export const LITE_CLEANUP_SUMMARY = `${LITE_ATTACK_SURFACE_DIR}/lite-cleanup-summary.json`;
|
|
67
|
+
const LITE_TRANSIENT_PATHS = [
|
|
68
|
+
"piolium/tmp",
|
|
69
|
+
"piolium/chamber-workspace",
|
|
70
|
+
"piolium/probe-workspace",
|
|
71
|
+
"piolium/adversarial-reviews",
|
|
72
|
+
"piolium/bypass-analysis",
|
|
73
|
+
"piolium/codeql-artifacts",
|
|
74
|
+
"piolium/codeql-queries",
|
|
75
|
+
"piolium/semgrep-rules",
|
|
76
|
+
"piolium/agentic-actions-res",
|
|
77
|
+
"piolium/confirm-workspace",
|
|
78
|
+
"piolium/codeql-res",
|
|
79
|
+
"piolium/semgrep-res",
|
|
80
|
+
"piolium/real-env-evidence",
|
|
81
|
+
"piolium/raw",
|
|
82
|
+
"piolium/file-records",
|
|
83
|
+
"piolium/findings-draft",
|
|
84
|
+
"piolium/attack-surface/raw",
|
|
85
|
+
"piolium/attack-pattern-registry.json",
|
|
86
|
+
"piolium/authz-coverage-gaps.md",
|
|
87
|
+
"piolium/merged-results.sarif",
|
|
88
|
+
];
|
|
89
|
+
|
|
90
|
+
function litePhaseMaxRetries(): number {
|
|
91
|
+
return readNonNegativeIntEnv(
|
|
92
|
+
"PIOLIUM_LITE_PHASE_MAX_RETRIES",
|
|
93
|
+
readNonNegativeIntEnv("PIOLIUM_PHASE_MAX_RETRIES", 5),
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function litePhaseBackoffBaseMs(): number {
|
|
98
|
+
return readPositiveIntEnv(
|
|
99
|
+
"PIOLIUM_LITE_PHASE_BACKOFF_BASE_MS",
|
|
100
|
+
readPositiveIntEnv("PIOLIUM_PHASE_BACKOFF_BASE_MS", 5000),
|
|
101
|
+
);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function litePhaseBackoffMaxMs(): number {
|
|
105
|
+
return readPositiveIntEnv(
|
|
106
|
+
"PIOLIUM_LITE_PHASE_BACKOFF_MAX_MS",
|
|
107
|
+
readPositiveIntEnv("PIOLIUM_PHASE_BACKOFF_MAX_MS", 120_000),
|
|
108
|
+
);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function gatePass(cwd: string, phase: "L1" | "L2" | "L3"): boolean {
|
|
112
|
+
switch (phase) {
|
|
113
|
+
case "L1":
|
|
114
|
+
return (
|
|
115
|
+
existsSync(reconReportPath(cwd)) &&
|
|
116
|
+
existsSync(candidateSummaryPath(cwd)) &&
|
|
117
|
+
existsSync(candidatesJsonlPath(cwd))
|
|
118
|
+
);
|
|
119
|
+
case "L2":
|
|
120
|
+
return existsSync(join(cwd, L2_SUMMARY));
|
|
121
|
+
case "L3":
|
|
122
|
+
return existsSync(join(cwd, L3_SUMMARY));
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function pickResumeAudit(cwd: string, forceFresh: boolean): AuditRunState | undefined {
|
|
127
|
+
if (forceFresh) return undefined;
|
|
128
|
+
const state = readAuditState(cwd).state;
|
|
129
|
+
if (!state) return undefined;
|
|
130
|
+
const audit = latestAudit(state);
|
|
131
|
+
if (!audit) return undefined;
|
|
132
|
+
if (audit.mode !== "lite") return undefined;
|
|
133
|
+
if (audit.status === "complete") return undefined;
|
|
134
|
+
return audit;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function notify(
|
|
138
|
+
ui: LiteUiHooks | undefined,
|
|
139
|
+
level: Parameters<NonNullable<LiteUiHooks["notify"]>>[1],
|
|
140
|
+
msg: string,
|
|
141
|
+
) {
|
|
142
|
+
ui?.notify?.(msg, level);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function setStatus(ui: LiteUiHooks | undefined, key: string, text?: string) {
|
|
146
|
+
ui?.setStatus?.(key, text);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
interface LiteLocalPhaseResult {
|
|
150
|
+
artifacts: string[];
|
|
151
|
+
notifyText?: string;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
async function runLiteLocalPhase(
|
|
155
|
+
cwd: string,
|
|
156
|
+
audit: AuditRunState,
|
|
157
|
+
phaseName: "L1" | "L2",
|
|
158
|
+
statusLabel: string,
|
|
159
|
+
gate: () => boolean,
|
|
160
|
+
run: () => LiteLocalPhaseResult | Promise<LiteLocalPhaseResult>,
|
|
161
|
+
signal: AbortSignal | undefined,
|
|
162
|
+
ui?: LiteUiHooks,
|
|
163
|
+
): Promise<void> {
|
|
164
|
+
if (audit.phases[phaseName]?.status === "complete" && gate()) {
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
const maxRetries = litePhaseMaxRetries();
|
|
169
|
+
const maxAttempts = maxRetries + 1;
|
|
170
|
+
const backoffBaseMs = litePhaseBackoffBaseMs();
|
|
171
|
+
const backoffMaxMs = litePhaseBackoffMaxMs();
|
|
172
|
+
try {
|
|
173
|
+
const result = await runWithRetry(
|
|
174
|
+
async (attempt, attempts) => {
|
|
175
|
+
const attemptLabel = attempts > 1 ? `${statusLabel} (${attempt}/${attempts})` : statusLabel;
|
|
176
|
+
setStatus(ui, "piolium-lite", attemptLabel);
|
|
177
|
+
await applyPhaseStatus(cwd, audit, phaseName, {
|
|
178
|
+
status: "in_progress",
|
|
179
|
+
attempt,
|
|
180
|
+
max_attempts: attempts,
|
|
181
|
+
retry_backoff_ms: null,
|
|
182
|
+
next_retry_at: null,
|
|
183
|
+
...(attempt > 1 ? { error: `Retry attempt ${attempt}/${attempts} running.` } : {}),
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
try {
|
|
187
|
+
const phaseResult = await run();
|
|
188
|
+
if (!gate()) {
|
|
189
|
+
throw new Error(`Phase ${phaseName} gate failed — expected artifact missing.`);
|
|
190
|
+
}
|
|
191
|
+
await applyPhaseStatus(cwd, audit, phaseName, {
|
|
192
|
+
status: "complete",
|
|
193
|
+
artifacts: phaseResult.artifacts,
|
|
194
|
+
attempt,
|
|
195
|
+
max_attempts: attempts,
|
|
196
|
+
retry_backoff_ms: null,
|
|
197
|
+
next_retry_at: null,
|
|
198
|
+
last_error: null,
|
|
199
|
+
});
|
|
200
|
+
return phaseResult;
|
|
201
|
+
} catch (err) {
|
|
202
|
+
if (gate()) {
|
|
203
|
+
await applyPhaseStatus(cwd, audit, phaseName, {
|
|
204
|
+
status: "complete",
|
|
205
|
+
attempt,
|
|
206
|
+
max_attempts: attempts,
|
|
207
|
+
retry_backoff_ms: null,
|
|
208
|
+
next_retry_at: null,
|
|
209
|
+
last_error: null,
|
|
210
|
+
});
|
|
211
|
+
notify(
|
|
212
|
+
ui,
|
|
213
|
+
"warning",
|
|
214
|
+
`Phase ${phaseName} errored but its required artifact exists; treating it as complete.`,
|
|
215
|
+
);
|
|
216
|
+
return { artifacts: [] };
|
|
217
|
+
}
|
|
218
|
+
throw err;
|
|
219
|
+
}
|
|
220
|
+
},
|
|
221
|
+
{
|
|
222
|
+
maxRetries,
|
|
223
|
+
backoffBaseMs,
|
|
224
|
+
backoffMaxMs,
|
|
225
|
+
...(signal ? { signal } : {}),
|
|
226
|
+
onRetry: async (info) => {
|
|
227
|
+
await applyPhaseStatus(cwd, audit, phaseName, {
|
|
228
|
+
status: "in_progress",
|
|
229
|
+
error: `Attempt ${info.attempt}/${info.maxAttempts} failed: ${info.errorMessage}. Retrying at ${info.nextRetryAt}.`,
|
|
230
|
+
attempt: info.attempt,
|
|
231
|
+
max_attempts: info.maxAttempts,
|
|
232
|
+
retry_backoff_ms: info.backoffMs,
|
|
233
|
+
next_retry_at: info.nextRetryAt,
|
|
234
|
+
last_error: info.errorMessage,
|
|
235
|
+
});
|
|
236
|
+
notify(
|
|
237
|
+
ui,
|
|
238
|
+
"warning",
|
|
239
|
+
`Phase ${phaseName} attempt ${info.attempt}/${info.maxAttempts} failed; retrying in ${Math.ceil(info.backoffMs / 1000)}s.`,
|
|
240
|
+
);
|
|
241
|
+
setStatus(
|
|
242
|
+
ui,
|
|
243
|
+
"piolium-lite",
|
|
244
|
+
`${statusLabel} retrying in ${Math.ceil(info.backoffMs / 1000)}s`,
|
|
245
|
+
);
|
|
246
|
+
},
|
|
247
|
+
},
|
|
248
|
+
);
|
|
249
|
+
if (result.notifyText) notify(ui, "info", result.notifyText);
|
|
250
|
+
} catch (err) {
|
|
251
|
+
const message = errorMessage(err);
|
|
252
|
+
await applyPhaseStatus(cwd, audit, phaseName, {
|
|
253
|
+
status: "failed",
|
|
254
|
+
error: maxRetries > 0 ? `Failed after ${maxRetries} retries: ${message}` : message,
|
|
255
|
+
attempt: maxAttempts,
|
|
256
|
+
max_attempts: maxAttempts,
|
|
257
|
+
retry_backoff_ms: null,
|
|
258
|
+
next_retry_at: null,
|
|
259
|
+
last_error: message,
|
|
260
|
+
});
|
|
261
|
+
throw err;
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
async function runL1(
|
|
266
|
+
cwd: string,
|
|
267
|
+
audit: AuditRunState,
|
|
268
|
+
signal: AbortSignal | undefined,
|
|
269
|
+
ui?: LiteUiHooks,
|
|
270
|
+
): Promise<void> {
|
|
271
|
+
await runLiteLocalPhase(
|
|
272
|
+
cwd,
|
|
273
|
+
audit,
|
|
274
|
+
"L1",
|
|
275
|
+
"● L1 recon sweep",
|
|
276
|
+
() => gatePass(cwd, "L1"),
|
|
277
|
+
async () => {
|
|
278
|
+
const recon = await runReconAsync(cwd, { signal });
|
|
279
|
+
const candidates = await runCandidateScanAsync(cwd, { signal });
|
|
280
|
+
return {
|
|
281
|
+
artifacts: [recon.reportPath, candidates.summaryPath, candidates.candidatesPath],
|
|
282
|
+
notifyText: `L1 recon: ${candidates.candidateCount} candidate match(es) across ${candidates.candidateFiles} file(s).`,
|
|
283
|
+
};
|
|
284
|
+
},
|
|
285
|
+
signal,
|
|
286
|
+
ui,
|
|
287
|
+
);
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
async function runL2(
|
|
291
|
+
cwd: string,
|
|
292
|
+
audit: AuditRunState,
|
|
293
|
+
signal: AbortSignal | undefined,
|
|
294
|
+
ui?: LiteUiHooks,
|
|
295
|
+
): Promise<void> {
|
|
296
|
+
await runLiteLocalPhase(
|
|
297
|
+
cwd,
|
|
298
|
+
audit,
|
|
299
|
+
"L2",
|
|
300
|
+
"● L2 secrets scan",
|
|
301
|
+
() => gatePass(cwd, "L2"),
|
|
302
|
+
() => {
|
|
303
|
+
const result = runQ1SecretsScan(cwd);
|
|
304
|
+
return {
|
|
305
|
+
artifacts: [join(cwd, L2_SUMMARY), ...result.draftPaths],
|
|
306
|
+
notifyText: `L2 ${result.backend}: ${result.findings.length} draft finding(s).`,
|
|
307
|
+
};
|
|
308
|
+
},
|
|
309
|
+
signal,
|
|
310
|
+
ui,
|
|
311
|
+
);
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
function buildL3Task(cwd: string): string {
|
|
315
|
+
return [
|
|
316
|
+
"You are running Phase L3 (Fast Code Scan) of a /piolium-lite scan.",
|
|
317
|
+
"This is a TIGHTLY SCOPED lite-mode SAST pass — not a full audit. Constraints:",
|
|
318
|
+
" - Hard time budget: 5 minutes wall-clock.",
|
|
319
|
+
" - Do NOT build CodeQL/Semgrep databases. If those tools aren't already installed, fall back to grep + read.",
|
|
320
|
+
" - Focus on cheap, high-signal patterns: command injection, path traversal, SSRF, hardcoded crypto, broken authn/z.",
|
|
321
|
+
" - Read `piolium/attack-surface/lite-recon.md`, `piolium/attack-surface/candidates-summary.md`, `piolium/attack-surface/candidates.jsonl`, and `piolium/attack-surface/lite-q1-summary.md` if present.",
|
|
322
|
+
" - Prioritize precise/high-score candidate files first, but validate with source evidence before drafting.",
|
|
323
|
+
" - For each candidate issue, write a draft finding to `piolium/findings-draft/l3-NNN-<slug>.md`.",
|
|
324
|
+
` - Write a phase summary to \`${L3_SUMMARY}\` even when nothing is found.`,
|
|
325
|
+
" - Stop after at most 8 candidate findings — quality over quantity.",
|
|
326
|
+
"",
|
|
327
|
+
`Target repository: ${cwd}`,
|
|
328
|
+
"",
|
|
329
|
+
"Each finding draft should follow this frontmatter:",
|
|
330
|
+
" ---",
|
|
331
|
+
" id: l3-NNN",
|
|
332
|
+
" phase: L3",
|
|
333
|
+
" slug: <kebab-case>",
|
|
334
|
+
" severity: high|medium|low",
|
|
335
|
+
" ---",
|
|
336
|
+
"",
|
|
337
|
+
"Begin now.",
|
|
338
|
+
].join("\n");
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
async function runL3(
|
|
342
|
+
cwd: string,
|
|
343
|
+
audit: AuditRunState,
|
|
344
|
+
codeScanner: AgentDefinition | undefined,
|
|
345
|
+
signal: AbortSignal | undefined,
|
|
346
|
+
ui?: LiteUiHooks,
|
|
347
|
+
agentRuntime?: AgentRuntimeModel,
|
|
348
|
+
): Promise<void> {
|
|
349
|
+
await runAgentPhase({
|
|
350
|
+
cwd,
|
|
351
|
+
audit,
|
|
352
|
+
phaseName: "L3",
|
|
353
|
+
statusKey: "piolium-lite",
|
|
354
|
+
statusLabel: "● L3 fast code scan",
|
|
355
|
+
agent: codeScanner,
|
|
356
|
+
missingAgentMessage: "code-scanner agent not found in bundled agents/.",
|
|
357
|
+
task: buildL3Task(cwd),
|
|
358
|
+
runtimeExtras: {
|
|
359
|
+
outputPaths: [findingsDraftDir(cwd), join(cwd, L3_SUMMARY)],
|
|
360
|
+
notes: ["Lite mode — keep the run under 5 minutes wall-clock."],
|
|
361
|
+
},
|
|
362
|
+
gate: () => gatePass(cwd, "L3"),
|
|
363
|
+
mode: "lite",
|
|
364
|
+
ui,
|
|
365
|
+
agentRuntime,
|
|
366
|
+
...(signal ? { signal } : {}),
|
|
367
|
+
});
|
|
368
|
+
notify(ui, "info", `L3 produced ${listL3Drafts(cwd).length} draft finding(s).`);
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
function listL3Drafts(cwd: string): string[] {
|
|
372
|
+
const dir = findingsDraftDir(cwd);
|
|
373
|
+
if (!existsSync(dir)) return [];
|
|
374
|
+
return readdirSync(dir)
|
|
375
|
+
.filter((f) => f.startsWith("l3-") && f.endsWith(".md"))
|
|
376
|
+
.map((f) => join(dir, f));
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
export interface LiteCleanupResult {
|
|
380
|
+
summaryPath: string;
|
|
381
|
+
removed: string[];
|
|
382
|
+
missing: string[];
|
|
383
|
+
retained: string[];
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
export function cleanupLiteTransientArtifacts(cwd: string): LiteCleanupResult {
|
|
387
|
+
mkdirSync(join(cwd, LITE_ATTACK_SURFACE_DIR), { recursive: true });
|
|
388
|
+
const removed: string[] = [];
|
|
389
|
+
const missing: string[] = [];
|
|
390
|
+
for (const rel of LITE_TRANSIENT_PATHS) {
|
|
391
|
+
const abs = join(cwd, rel);
|
|
392
|
+
if (!existsSync(abs)) {
|
|
393
|
+
missing.push(rel);
|
|
394
|
+
continue;
|
|
395
|
+
}
|
|
396
|
+
rmSync(abs, { recursive: true, force: true });
|
|
397
|
+
removed.push(rel);
|
|
398
|
+
}
|
|
399
|
+
const result: LiteCleanupResult = {
|
|
400
|
+
summaryPath: LITE_CLEANUP_SUMMARY,
|
|
401
|
+
removed,
|
|
402
|
+
missing,
|
|
403
|
+
retained: ["piolium/attack-surface/", "piolium/findings/", "piolium/audit-state.json"],
|
|
404
|
+
};
|
|
405
|
+
writeFileSync(join(cwd, LITE_CLEANUP_SUMMARY), `${JSON.stringify(result, null, "\t")}\n`);
|
|
406
|
+
return result;
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
/**
|
|
410
|
+
* Inline post-L3 finalize (upstream lite has no tracked consolidation /
|
|
411
|
+
* cleanup phase): promote surviving l2-/l3- drafts into severity-prefixed
|
|
412
|
+
* finding dirs and remove transient artifacts.
|
|
413
|
+
*/
|
|
414
|
+
function finalizeLite(cwd: string): void {
|
|
415
|
+
mkdirSync(join(cwd, LITE_ATTACK_SURFACE_DIR), { recursive: true });
|
|
416
|
+
const consolidation = consolidateDrafts(cwd, ["l2-", "l3-", "q1-", "q2-"]);
|
|
417
|
+
writeFileSync(
|
|
418
|
+
join(cwd, LITE_CONSOLIDATION_MANIFEST),
|
|
419
|
+
`${JSON.stringify(
|
|
420
|
+
{
|
|
421
|
+
generated_at: new Date().toISOString(),
|
|
422
|
+
source_prefixes: ["l2-", "l3-"],
|
|
423
|
+
promoted: consolidation.promoted,
|
|
424
|
+
dropped: consolidation.dropped,
|
|
425
|
+
},
|
|
426
|
+
null,
|
|
427
|
+
"\t",
|
|
428
|
+
)}\n`,
|
|
429
|
+
);
|
|
430
|
+
const dirs = listFindingDirs(cwd);
|
|
431
|
+
const cleanup = cleanupLiteTransientArtifacts(cwd);
|
|
432
|
+
writeFileSync(
|
|
433
|
+
join(cwd, LITE_VERIFICATION_SUMMARY),
|
|
434
|
+
[
|
|
435
|
+
"# Lite Verification & Cleanup",
|
|
436
|
+
"",
|
|
437
|
+
`Generated: ${new Date().toISOString()}`,
|
|
438
|
+
"",
|
|
439
|
+
"## Verification",
|
|
440
|
+
"",
|
|
441
|
+
"- Scope: fast triage; PoC + live confirmation remain `/piolium-balanced` / `/piolium-confirm`.",
|
|
442
|
+
`- Promoted finding directories: ${dirs.length}`,
|
|
443
|
+
"- Durable review inputs remain under `piolium/attack-surface/`.",
|
|
444
|
+
"",
|
|
445
|
+
"## Cleanup",
|
|
446
|
+
"",
|
|
447
|
+
`- Removed: ${cleanup.removed.length > 0 ? cleanup.removed.map((p) => `\`${p}\``).join(", ") : "(none)"}`,
|
|
448
|
+
`- Cleanup summary: \`${cleanup.summaryPath}\``,
|
|
449
|
+
"",
|
|
450
|
+
].join("\n"),
|
|
451
|
+
);
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
export async function runLiteAudit(opts: RunLiteOptions): Promise<RunLiteResult> {
|
|
455
|
+
const { cwd, signal, ui } = opts;
|
|
456
|
+
mkdirSync(join(cwd, LITE_ATTACK_SURFACE_DIR), { recursive: true });
|
|
457
|
+
|
|
458
|
+
ui?.setStatus?.("piolium-lite", "● preparing recon");
|
|
459
|
+
const recon0 = await runReconAsync(cwd, { signal });
|
|
460
|
+
ui?.setStatus?.("piolium-lite", "● scanning candidate files");
|
|
461
|
+
const candidateScan = await runCandidateScanAsync(cwd, { signal });
|
|
462
|
+
ui?.notify?.(
|
|
463
|
+
`Candidate scan: ${candidateScan.candidateCount} match(es) across ${candidateScan.candidateFiles} file(s).`,
|
|
464
|
+
"info",
|
|
465
|
+
);
|
|
466
|
+
let audit = pickResumeAudit(cwd, opts.forceFresh ?? false);
|
|
467
|
+
if (!audit) {
|
|
468
|
+
audit = await initAudit(cwd, {
|
|
469
|
+
mode: "lite",
|
|
470
|
+
...(recon0.commit ? { commit: recon0.commit } : { commit: null }),
|
|
471
|
+
...(recon0.branch ? { branch: recon0.branch } : { branch: "nogit" }),
|
|
472
|
+
...(recon0.repository ? { repository: recon0.repository } : {}),
|
|
473
|
+
history_available: recon0.historyAvailable,
|
|
474
|
+
agent_sdk: "pi",
|
|
475
|
+
});
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
// L1 first, deterministic. Required for the L3 task to have target context.
|
|
479
|
+
try {
|
|
480
|
+
await runL1(cwd, audit, signal, ui);
|
|
481
|
+
} catch {
|
|
482
|
+
await markAuditStatus(cwd, audit.audit_id, "failed");
|
|
483
|
+
setStatus(ui, "piolium-lite", undefined);
|
|
484
|
+
return finalResult(audit, "failed", cwd);
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
// L2 + L3 race under the cap. Both are independent.
|
|
488
|
+
const scheduler = new Scheduler({ maxConcurrent: 3, ...(signal ? { signal } : {}) });
|
|
489
|
+
const { agents } = loadAgents({ cwd });
|
|
490
|
+
const codeScanner = agents.get("code-scanner");
|
|
491
|
+
|
|
492
|
+
const settled = await Promise.allSettled([
|
|
493
|
+
scheduler.enqueue({ id: "L2", run: (sig) => runL2(cwd, audit, sig, ui) }),
|
|
494
|
+
scheduler.enqueue({
|
|
495
|
+
id: "L3",
|
|
496
|
+
run: (sig) => runL3(cwd, audit, codeScanner, sig, ui, opts.agentRuntime),
|
|
497
|
+
}),
|
|
498
|
+
]);
|
|
499
|
+
scheduler.dispose();
|
|
500
|
+
|
|
501
|
+
const failed = settled.some((s) => s.status === "rejected");
|
|
502
|
+
if (!failed) {
|
|
503
|
+
try {
|
|
504
|
+
finalizeLite(cwd);
|
|
505
|
+
} catch (err) {
|
|
506
|
+
notify(ui, "warning", `Lite finalize failed: ${errorMessage(err)}`);
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
const final = await markAuditStatus(cwd, audit.audit_id, failed ? "failed" : "complete");
|
|
510
|
+
setStatus(ui, "piolium-lite", undefined);
|
|
511
|
+
if (failed) {
|
|
512
|
+
notify(
|
|
513
|
+
ui,
|
|
514
|
+
"error",
|
|
515
|
+
`Lite audit failed (${settled.filter((s) => s.status === "rejected").length} phase failure(s)).`,
|
|
516
|
+
);
|
|
517
|
+
} else {
|
|
518
|
+
notify(ui, "info", "Lite audit complete.");
|
|
519
|
+
}
|
|
520
|
+
return finalResult(final ?? audit, failed ? "failed" : "complete", cwd);
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
function finalResult(
|
|
524
|
+
audit: AuditRunState,
|
|
525
|
+
status: "complete" | "failed",
|
|
526
|
+
cwd: string,
|
|
527
|
+
): RunLiteResult {
|
|
528
|
+
const phases: Record<string, "complete" | "failed" | "skipped"> = {};
|
|
529
|
+
for (const [name, phase] of Object.entries(audit.phases)) {
|
|
530
|
+
if (phase.status === "complete" || phase.status === "failed" || phase.status === "skipped") {
|
|
531
|
+
phases[name] = phase.status;
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
return {
|
|
535
|
+
auditId: audit.audit_id,
|
|
536
|
+
status,
|
|
537
|
+
phases,
|
|
538
|
+
summaryPath: reconReportPath(cwd),
|
|
539
|
+
};
|
|
540
|
+
}
|