@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,750 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Balanced mode orchestrator (`/piolium-balanced`).
|
|
3
|
+
*
|
|
4
|
+
* Pipeline (command-defs/balanced.md, archon-audit @ 2026-05-16):
|
|
5
|
+
*
|
|
6
|
+
* B1 Intel Sweep (cve-scout)
|
|
7
|
+
* → B2 Threat Model (threat-modeler)
|
|
8
|
+
* → [B3 Code Scan ‖ B4 Targeted Probe] (code-scanner ‖ probe-lead)
|
|
9
|
+
* → B5 Review Panel + FP (review-adjudicator)
|
|
10
|
+
* → B6 Intent Reconciliation (context-reviewer; skip-and-continue)
|
|
11
|
+
* → B7 PoC Authoring (poc-author, per finding)
|
|
12
|
+
* → B8 Finding Finalize (finding-writer, per finding, both buckets)
|
|
13
|
+
* → B9 Report Compose (report-composer + inline cleanup/file-state)
|
|
14
|
+
*
|
|
15
|
+
* Differences from upstream (deliberate, MVP-grade):
|
|
16
|
+
* - Chamber B5 is a single review-adjudicator run doing inline Ideator +
|
|
17
|
+
* Devil's-Advocate work plus the FP-check / triage tail; we don't drive
|
|
18
|
+
* a real multi-round debate.
|
|
19
|
+
* - The probe team (B4) is a single probe-lead run that folds the
|
|
20
|
+
* reasoner roles into its own prompt.
|
|
21
|
+
* - Consolidation/partition is done in TS (findings.ts) rather than via
|
|
22
|
+
* the upstream python helpers, but routes triage-skip drafts and
|
|
23
|
+
* non-executed PoCs into `findings-theoretical/` the same way.
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
import { existsSync, mkdirSync, readdirSync, rmSync, writeFileSync } from "node:fs";
|
|
27
|
+
import { join } from "node:path";
|
|
28
|
+
import type { AgentRuntimeModel } from "../agent-runner.ts";
|
|
29
|
+
import { type AgentDefinition, loadAgents } from "../agents.ts";
|
|
30
|
+
import {
|
|
31
|
+
type AuditRunState,
|
|
32
|
+
applyPhaseStatus,
|
|
33
|
+
initAudit,
|
|
34
|
+
latestAudit,
|
|
35
|
+
markAuditStatus,
|
|
36
|
+
readAuditState,
|
|
37
|
+
} from "../audit-state.ts";
|
|
38
|
+
import { runCandidateScanAsync } from "../candidate-scan.ts";
|
|
39
|
+
import {
|
|
40
|
+
consolidateDrafts,
|
|
41
|
+
findingsDraftDir,
|
|
42
|
+
listAllFindingDirs,
|
|
43
|
+
listFindingDirs,
|
|
44
|
+
partitionFindings,
|
|
45
|
+
} from "../findings.ts";
|
|
46
|
+
import { runReconAsync } from "../recon.ts";
|
|
47
|
+
import { Scheduler } from "../scheduler.ts";
|
|
48
|
+
import { type PhaseUiHooks, runAgentPhase } from "./phase-runner.ts";
|
|
49
|
+
|
|
50
|
+
export type BalancedUiHooks = PhaseUiHooks;
|
|
51
|
+
|
|
52
|
+
export interface RunBalancedOptions {
|
|
53
|
+
cwd: string;
|
|
54
|
+
signal?: AbortSignal;
|
|
55
|
+
ui?: BalancedUiHooks;
|
|
56
|
+
forceFresh?: boolean;
|
|
57
|
+
agentRuntime?: AgentRuntimeModel;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export interface RunBalancedResult {
|
|
61
|
+
auditId: string;
|
|
62
|
+
status: "complete" | "failed";
|
|
63
|
+
phases: Record<string, "complete" | "failed" | "skipped">;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export const BALANCED_ATTACK_SURFACE_DIR = "piolium/attack-surface";
|
|
67
|
+
export const BALANCED_KB_REPORT = `${BALANCED_ATTACK_SURFACE_DIR}/knowledge-base-report.md`;
|
|
68
|
+
export const BALANCED_ADVISORY_SUMMARY = `${BALANCED_ATTACK_SURFACE_DIR}/advisory-summary.md`;
|
|
69
|
+
export const BALANCED_SAST_REPORT = `${BALANCED_ATTACK_SURFACE_DIR}/source-sink-flows-all-severities.md`;
|
|
70
|
+
export const BALANCED_ATTACK_SURFACE_INVENTORY = `${BALANCED_ATTACK_SURFACE_DIR}/manual-attack-surface-inventory.md`;
|
|
71
|
+
export const BALANCED_PROBE_SUMMARY = `${BALANCED_ATTACK_SURFACE_DIR}/balanced-probe-summary.md`;
|
|
72
|
+
export const BALANCED_CHAMBER_SUMMARY = `${BALANCED_ATTACK_SURFACE_DIR}/balanced-chamber-summary.md`;
|
|
73
|
+
export const BALANCED_INTENT_RECONCILIATION = `${BALANCED_ATTACK_SURFACE_DIR}/intent-reconciliation.md`;
|
|
74
|
+
export const BALANCED_VERIFICATION_SUMMARY = `${BALANCED_ATTACK_SURFACE_DIR}/balanced-verification-summary.md`;
|
|
75
|
+
export const BALANCED_CLEANUP_SUMMARY = `${BALANCED_ATTACK_SURFACE_DIR}/balanced-cleanup-summary.json`;
|
|
76
|
+
export const BALANCED_CONSOLIDATION_MANIFEST = `${BALANCED_ATTACK_SURFACE_DIR}/balanced-consolidation-manifest.json`;
|
|
77
|
+
const BALANCED_TRANSIENT_PATHS = [
|
|
78
|
+
"piolium/tmp",
|
|
79
|
+
"piolium/probe-workspace",
|
|
80
|
+
"piolium/chamber-workspace",
|
|
81
|
+
"piolium/adversarial-reviews",
|
|
82
|
+
"piolium/bypass-analysis",
|
|
83
|
+
"piolium/codeql-artifacts",
|
|
84
|
+
"piolium/codeql-queries",
|
|
85
|
+
"piolium/semgrep-rules",
|
|
86
|
+
"piolium/agentic-actions-res",
|
|
87
|
+
"piolium/confirm-workspace",
|
|
88
|
+
"piolium/codeql-res",
|
|
89
|
+
"piolium/semgrep-res",
|
|
90
|
+
"piolium/real-env-evidence",
|
|
91
|
+
"piolium/raw",
|
|
92
|
+
"piolium/file-records",
|
|
93
|
+
"piolium/findings-draft",
|
|
94
|
+
"piolium/attack-surface/raw",
|
|
95
|
+
"piolium/attack-pattern-registry.json",
|
|
96
|
+
"piolium/authz-coverage-gaps.md",
|
|
97
|
+
"piolium/merged-results.sarif",
|
|
98
|
+
];
|
|
99
|
+
const FINAL_REPORT = "piolium/final-audit-report.md";
|
|
100
|
+
|
|
101
|
+
function exists(cwd: string, rel: string): boolean {
|
|
102
|
+
return existsSync(join(cwd, rel));
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function gateB1(cwd: string) {
|
|
106
|
+
return () => exists(cwd, BALANCED_ADVISORY_SUMMARY);
|
|
107
|
+
}
|
|
108
|
+
function gateB2(cwd: string) {
|
|
109
|
+
return () => exists(cwd, BALANCED_KB_REPORT);
|
|
110
|
+
}
|
|
111
|
+
function gateB3(cwd: string) {
|
|
112
|
+
return () =>
|
|
113
|
+
exists(cwd, BALANCED_SAST_REPORT) || hasDraftPrefix(cwd, "b3-") || hasDraftPrefix(cwd, "p4-");
|
|
114
|
+
}
|
|
115
|
+
function gateB4(cwd: string) {
|
|
116
|
+
return () => exists(cwd, BALANCED_ATTACK_SURFACE_INVENTORY) && exists(cwd, BALANCED_PROBE_SUMMARY);
|
|
117
|
+
}
|
|
118
|
+
function gateB5(cwd: string) {
|
|
119
|
+
return () =>
|
|
120
|
+
exists(cwd, BALANCED_CHAMBER_SUMMARY) || hasDraftPrefix(cwd, "b5-") || hasDraftPrefix(cwd, "p8-");
|
|
121
|
+
}
|
|
122
|
+
function gateB6(cwd: string) {
|
|
123
|
+
// Intent Reconciliation is best-effort: the artifact is nice-to-have but
|
|
124
|
+
// the phase never blocks the pipeline.
|
|
125
|
+
return () => exists(cwd, BALANCED_INTENT_RECONCILIATION);
|
|
126
|
+
}
|
|
127
|
+
function gateB9(cwd: string) {
|
|
128
|
+
return () => exists(cwd, FINAL_REPORT);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function hasDraftPrefix(cwd: string, prefix: string): boolean {
|
|
132
|
+
const dir = findingsDraftDir(cwd);
|
|
133
|
+
if (!existsSync(dir)) return false;
|
|
134
|
+
return readdirSync(dir).some((f) => f.startsWith(prefix) && f.endsWith(".md"));
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function pickResume(cwd: string, force: boolean): AuditRunState | undefined {
|
|
138
|
+
if (force) return undefined;
|
|
139
|
+
const state = readAuditState(cwd).state;
|
|
140
|
+
const audit = state ? latestAudit(state) : undefined;
|
|
141
|
+
if (!audit) return undefined;
|
|
142
|
+
if (audit.mode !== "balanced") return undefined;
|
|
143
|
+
if (audit.status === "complete") return undefined;
|
|
144
|
+
return audit;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
function buildB1Task(): string {
|
|
148
|
+
return [
|
|
149
|
+
"You are running Phase B1 (Intel Sweep) of /piolium-balanced.",
|
|
150
|
+
"",
|
|
151
|
+
"Goal: gather published security advisories (CVE/GHSA/OSV) and high-level dependency intelligence relevant to this repository.",
|
|
152
|
+
"",
|
|
153
|
+
`Required artifact: write \`${BALANCED_ADVISORY_SUMMARY}\` with sections:`,
|
|
154
|
+
" ## Repository Identity",
|
|
155
|
+
" ## Recent Advisories (last 24 months)",
|
|
156
|
+
" ## Dependency Intelligence",
|
|
157
|
+
" ## Architecture Hints",
|
|
158
|
+
" ## Coverage Gaps",
|
|
159
|
+
"",
|
|
160
|
+
"Scope: cve-scout only. Do NOT run commit archaeology or patch-bypass (those are deep-only).",
|
|
161
|
+
"Stop after writing the file. Do not promote drafts or move to B2.",
|
|
162
|
+
].join("\n");
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
function buildB2Task(): string {
|
|
166
|
+
return [
|
|
167
|
+
"You are running Phase B2 (Threat Model) of /piolium-balanced.",
|
|
168
|
+
"",
|
|
169
|
+
`Required artifact: \`${BALANCED_KB_REPORT}\`. Sections:`,
|
|
170
|
+
" ## Project Type & Components",
|
|
171
|
+
" ## Trust Boundaries",
|
|
172
|
+
" ## Data-Flow Slices (DFD)",
|
|
173
|
+
" ## Control-Flow Slices (CFD)",
|
|
174
|
+
" ## Framework Contracts and Hidden Control Channels",
|
|
175
|
+
" ## Domain Attack Modes (apply security-threat-model and other relevant skills)",
|
|
176
|
+
" ## Coverage Gaps",
|
|
177
|
+
"",
|
|
178
|
+
"BALANCED MODE: skip Domain Attack Research Modes B and C; skip Spec Gap Candidates and CodeQL Extraction Targets sections.",
|
|
179
|
+
"Framework-contract coverage must inventory middleware/proxy/runtime/header assumptions affecting auth, routing, tenant selection, debug/admin/preview behavior, method/path override, or cache keys.",
|
|
180
|
+
"",
|
|
181
|
+
`Read \`${BALANCED_ADVISORY_SUMMARY}\` if present. If \`piolium/INFO.md\` exists, treat it as authoritative for the sections it covers.`,
|
|
182
|
+
"Stop after writing the report. Do not start B3.",
|
|
183
|
+
].join("\n");
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
function buildB3Task(): string {
|
|
187
|
+
return [
|
|
188
|
+
"You are running Phase B3 (Code Scan) of /piolium-balanced.",
|
|
189
|
+
"",
|
|
190
|
+
"Goal: run the cheapest available static analysis pass. Required artifacts:",
|
|
191
|
+
` - \`${BALANCED_SAST_REPORT}\` summary`,
|
|
192
|
+
" - draft findings under `piolium/findings-draft/b3-NNN-<slug>.md`",
|
|
193
|
+
"",
|
|
194
|
+
"Read `piolium/attack-surface/candidates-summary.md` and `piolium/attack-surface/candidates.jsonl` first. Prioritize precise/high-score candidate files, then validate normally.",
|
|
195
|
+
"BALANCED MODE: run built-in CodeQL security suites + Semgrep Pro only. No custom queries/rules, no structural extraction, no SpotBugs/agentic-actions.",
|
|
196
|
+
"Then perform an inline enrichment pass: classify each SAST finding as likely-security / likely-correctness / likely-environment-only and drop the latter two.",
|
|
197
|
+
"If neither CodeQL nor Semgrep is available, fall back to grep+read for command injection, path traversal, SSRF, broken authn/z, hardcoded secrets, weak crypto.",
|
|
198
|
+
"Cap drafts at 20. Each draft frontmatter MUST include: id (b3-NNN), phase (B3), slug, severity (critical|high|medium|low|info).",
|
|
199
|
+
].join("\n");
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
function buildB4Task(): string {
|
|
203
|
+
return [
|
|
204
|
+
"You are running Phase B4 (Targeted Probe) of /piolium-balanced.",
|
|
205
|
+
"",
|
|
206
|
+
"This is a SINGLE-TEAM, SINGLE-PASS probe — not the deep multi-team probe. You act as Probe Strategist, Backward Reasoner and Evidence Harvester inline.",
|
|
207
|
+
"",
|
|
208
|
+
"Steps you must complete in order, all in one session:",
|
|
209
|
+
` 1. Read \`${BALANCED_KB_REPORT}\`. If absent, fail with a clear note.`,
|
|
210
|
+
" 2. Read `piolium/attack-surface/candidates-summary.md` and pick one or two highest-impact attack-surface slices.",
|
|
211
|
+
` 3. Write \`${BALANCED_ATTACK_SURFACE_INVENTORY}\` with entry points, public routes/URLs, attacker sources, sinks, hidden control channels, middleware/proxy assumptions, and key files.`,
|
|
212
|
+
" 4. Generate hypotheses (Pre-Mortem + Abductive reasoning).",
|
|
213
|
+
" 5. Verify hypotheses with `read`/`grep`/`bash` — file:line evidence required; apply causal challenge before any INVALIDATED verdict.",
|
|
214
|
+
" 6. Write verified hypotheses as drafts to `piolium/findings-draft/b4-NNN-<slug>.md`.",
|
|
215
|
+
` 7. Write \`${BALANCED_PROBE_SUMMARY}\` summarising probe execution and coverage.`,
|
|
216
|
+
"Transient scratch, if needed: `piolium/tmp/piolium/balanced-probe/`.",
|
|
217
|
+
"",
|
|
218
|
+
"Cap drafts at 10. Each draft frontmatter MUST include id (b4-NNN), phase (B4), slug, severity.",
|
|
219
|
+
].join("\n");
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
function buildB5Task(): string {
|
|
223
|
+
return [
|
|
224
|
+
"You are running Phase B5 (Review Panel + FP Check) of /piolium-balanced.",
|
|
225
|
+
"",
|
|
226
|
+
"There is no separate Ideator / Tracer / Advocate run in balanced mode — you do all of those roles inline, then run the FP/triage tail.",
|
|
227
|
+
"",
|
|
228
|
+
"Inputs:",
|
|
229
|
+
` - \`${BALANCED_KB_REPORT}\``,
|
|
230
|
+
` - \`${BALANCED_ATTACK_SURFACE_INVENTORY}\``,
|
|
231
|
+
` - \`${BALANCED_SAST_REPORT}\``,
|
|
232
|
+
" - `piolium/attack-surface/candidates-summary.md`",
|
|
233
|
+
" - `piolium/findings-draft/b3-*.md` (SAST drafts)",
|
|
234
|
+
" - `piolium/findings-draft/b4-*.md` (Probe drafts)",
|
|
235
|
+
"",
|
|
236
|
+
"Steps:",
|
|
237
|
+
" 1. Read all draft findings.",
|
|
238
|
+
" 2. For each draft: act as Ideator (challenge it), Devil's-Advocate (try to reject it), and Synthesizer (final verdict). Chain/extend pre-seeded probe hypotheses rather than regenerating.",
|
|
239
|
+
" 3. Do not delete weak drafts. Mark rejected drafts with frontmatter `status: rejected-fp` and `rejection_reason: <short reason>`.",
|
|
240
|
+
" 4. fp-check tail: for surviving CRITICAL drafts, do a cold re-verification pass; HIGH/MEDIUM rely on the Advocate challenge.",
|
|
241
|
+
" 5. Triage sweep: write `Triage-Priority` (P0/P1/P2/skip), `Triage-Exploitability`, `Triage-Impact`, `Triage-Reasoning` into each surviving draft's frontmatter. `skip` is reversible (routed to the theoretical bucket later).",
|
|
242
|
+
" 6. For surviving drafts, copy them to `piolium/findings-draft/b5-NNN-<slug>.md` with frontmatter `status: valid`, severity normalised to critical|high|medium|low|info, and the Triage-* fields preserved.",
|
|
243
|
+
` 7. Write the durable chamber summary to \`${BALANCED_CHAMBER_SUMMARY}\`.`,
|
|
244
|
+
"Transient scratch, if needed: `piolium/tmp/piolium/balanced-chamber/`.",
|
|
245
|
+
"",
|
|
246
|
+
"Cap surviving drafts at 12.",
|
|
247
|
+
].join("\n");
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
function buildB6Task(): string {
|
|
251
|
+
return [
|
|
252
|
+
"You are running Phase B6 (Intent Reconciliation) of /piolium-balanced — AUDIT CONTRACT.",
|
|
253
|
+
"",
|
|
254
|
+
"Runs after the B5 FP/triage tail (every VALID draft already carries a `Triage-Priority`) and BEFORE any PoC effort. Reconcile each surviving finding against what the project documents as intentional design, an exposed feature, or an explicitly in-scope risk.",
|
|
255
|
+
"",
|
|
256
|
+
"Inputs:",
|
|
257
|
+
" - Findings drafts: `piolium/findings-draft/b5-*.md` (evaluate every draft with `status: valid`).",
|
|
258
|
+
` - KB: \`${BALANCED_KB_REPORT}\` (read Architecture Model, Domain Attack Research, Known False-Positive Sources).`,
|
|
259
|
+
" - `piolium/INFO.md` `## Known False-Positive Sources` if present.",
|
|
260
|
+
"",
|
|
261
|
+
"For each VALID draft: do a bounded read of ONLY the `file:line` it cites, reconcile against documented intent, and write `Intent-Verdict` / `Intent-Source` / `Intent-Quote` into the draft frontmatter. For `intentional-design` or `documented-feature` whose decisive basis is `confidence: strong` (or operator INFO.md), overwrite `Triage-Priority: skip` with a `Triage-Reasoning: context-reviewer: …` note. Do NOT touch `status` or `severity`.",
|
|
262
|
+
"",
|
|
263
|
+
`Write the corpus to \`piolium/attack-surface/intent-corpus.json\`, per-finding verdicts to \`piolium/attack-surface/intent-verdicts.json\`, and the human-readable report to \`${BALANCED_INTENT_RECONCILIATION}\`.`,
|
|
264
|
+
"",
|
|
265
|
+
"This phase is best-effort: if you cannot complete, still write a minimal intent-reconciliation.md noting the gap. It must never suppress a finding outright.",
|
|
266
|
+
].join("\n");
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
function buildB7Task(findingDir: string, slug: string): string {
|
|
270
|
+
return [
|
|
271
|
+
"You are running Phase B7 (PoC Authoring) for a single finding in /piolium-balanced.",
|
|
272
|
+
"",
|
|
273
|
+
`Finding directory: ${findingDir}`,
|
|
274
|
+
`Slug: ${slug}`,
|
|
275
|
+
"",
|
|
276
|
+
"Steps:",
|
|
277
|
+
" 1. Read `draft.md` in the finding directory.",
|
|
278
|
+
" 2. Build a minimal proof-of-concept demonstrating exploitability.",
|
|
279
|
+
" 3. Write the PoC to `<finding-dir>/poc.{py|sh|js|rb|go}` (pick the most natural language).",
|
|
280
|
+
" 4. Write evidence (commands run, observed output) to `<finding-dir>/evidence/`.",
|
|
281
|
+
" 5. Write `PoC-Status: executed` (or `not-executed` with a reason) back into `draft.md` frontmatter.",
|
|
282
|
+
" 6. If exploitation requires an unavailable runtime, write a 'theoretical PoC' note explaining the chain and set `PoC-Status: not-executed`.",
|
|
283
|
+
"",
|
|
284
|
+
"You are NOT responsible for report.md — that is Phase B8. Stop after the PoC + evidence + PoC-Status are written.",
|
|
285
|
+
].join("\n");
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
function buildB8Task(findingDir: string): string {
|
|
289
|
+
return [
|
|
290
|
+
"You are running Phase B8 (Finding Finalize / vuln-report) for a single finding.",
|
|
291
|
+
"",
|
|
292
|
+
`Finding directory: ${findingDir}`,
|
|
293
|
+
"",
|
|
294
|
+
"Steps:",
|
|
295
|
+
" 1. Read `draft.md`, `poc.*` (if any), and `evidence/` contents.",
|
|
296
|
+
" 2. Use the `vuln-report` skill if available; otherwise produce a GitHub-advisory-style report.",
|
|
297
|
+
" 3. Write the final report to `<finding-dir>/report.md` — must be > 500 bytes, with Summary, Details, Root Cause, Proof of concept & Evidence, Impact, Remediation sections.",
|
|
298
|
+
" 4. If this finding is in the theoretical bucket (no executed PoC), the 'Proof of concept & Evidence' section must state the no-PoC reason.",
|
|
299
|
+
"",
|
|
300
|
+
"Do not modify draft.md.",
|
|
301
|
+
].join("\n");
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
function buildB9Task(): string {
|
|
305
|
+
return [
|
|
306
|
+
"You are running Phase B9 (Report Compose) of /piolium-balanced.",
|
|
307
|
+
"",
|
|
308
|
+
"Steps:",
|
|
309
|
+
" 1. List every directory under `piolium/findings/` AND `piolium/findings-theoretical/`. Each MUST have a `report.md` of >500 bytes — if any are missing, fail with a clear error and DO NOT write the final report.",
|
|
310
|
+
" 2. Compose `piolium/final-audit-report.md` with:",
|
|
311
|
+
" - Executive Summary (note: balanced mode — skipped vs deep: commit archaeology, patch-bypass, dedicated authz/cross-service phases, multi-round probing, variant analysis. Cold verification + intent reconciliation still run.)",
|
|
312
|
+
" - Findings by Severity — confirmed (PoC-executed) findings only, with links to per-finding report.md",
|
|
313
|
+
" - Theoretical / Unconfirmed Findings — `piolium/findings-theoretical/` entries, kept OUT of the Summary-of-Findings table",
|
|
314
|
+
` - Intent Reconciliation summary (surface \`${BALANCED_INTENT_RECONCILIATION}\` if present)`,
|
|
315
|
+
` - Attack Surface Summary linking \`${BALANCED_ATTACK_SURFACE_DIR}/\` artifacts`,
|
|
316
|
+
" - Coverage Gaps, Methodology Notes",
|
|
317
|
+
" 3. Reference findings by their <id>-<slug> directory name.",
|
|
318
|
+
"",
|
|
319
|
+
"Stop after writing the final report.",
|
|
320
|
+
].join("\n");
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
async function runB3PlusB4Parallel(
|
|
324
|
+
cwd: string,
|
|
325
|
+
audit: AuditRunState,
|
|
326
|
+
codeScanner: AgentDefinition | undefined,
|
|
327
|
+
probeLead: AgentDefinition | undefined,
|
|
328
|
+
signal: AbortSignal | undefined,
|
|
329
|
+
ui: BalancedUiHooks | undefined,
|
|
330
|
+
agentRuntime?: AgentRuntimeModel,
|
|
331
|
+
): Promise<{ failed: boolean }> {
|
|
332
|
+
const scheduler = new Scheduler({ maxConcurrent: 3, ...(signal ? { signal } : {}) });
|
|
333
|
+
const settled = await Promise.allSettled([
|
|
334
|
+
scheduler.enqueue({
|
|
335
|
+
id: "B3",
|
|
336
|
+
run: (sig) =>
|
|
337
|
+
runAgentPhase({
|
|
338
|
+
cwd,
|
|
339
|
+
audit,
|
|
340
|
+
phaseName: "B3",
|
|
341
|
+
statusKey: "piolium-balanced",
|
|
342
|
+
statusLabel: "● B3 code scan",
|
|
343
|
+
agent: codeScanner,
|
|
344
|
+
missingAgentMessage: "code-scanner agent missing",
|
|
345
|
+
task: buildB3Task(),
|
|
346
|
+
gate: gateB3(cwd),
|
|
347
|
+
mode: "balanced",
|
|
348
|
+
ui,
|
|
349
|
+
agentRuntime,
|
|
350
|
+
...(sig ? { signal: sig } : {}),
|
|
351
|
+
}),
|
|
352
|
+
}),
|
|
353
|
+
scheduler.enqueue({
|
|
354
|
+
id: "B4",
|
|
355
|
+
run: (sig) =>
|
|
356
|
+
runAgentPhase({
|
|
357
|
+
cwd,
|
|
358
|
+
audit,
|
|
359
|
+
phaseName: "B4",
|
|
360
|
+
statusKey: "piolium-balanced",
|
|
361
|
+
statusLabel: "● B4 targeted probe",
|
|
362
|
+
agent: probeLead,
|
|
363
|
+
missingAgentMessage: "probe-lead agent missing",
|
|
364
|
+
task: buildB4Task(),
|
|
365
|
+
gate: gateB4(cwd),
|
|
366
|
+
mode: "balanced",
|
|
367
|
+
ui,
|
|
368
|
+
agentRuntime,
|
|
369
|
+
...(sig ? { signal: sig } : {}),
|
|
370
|
+
}),
|
|
371
|
+
}),
|
|
372
|
+
]);
|
|
373
|
+
scheduler.dispose();
|
|
374
|
+
return { failed: settled.some((s) => s.status === "rejected") };
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
async function runPerFindingPhase(
|
|
378
|
+
cwd: string,
|
|
379
|
+
audit: AuditRunState,
|
|
380
|
+
phaseName: "B7" | "B8",
|
|
381
|
+
agent: AgentDefinition | undefined,
|
|
382
|
+
taskBuilder: (dir: string, slug: string) => string,
|
|
383
|
+
dirs: { id: string; slug: string; path: string }[],
|
|
384
|
+
ui: BalancedUiHooks | undefined,
|
|
385
|
+
signal: AbortSignal | undefined,
|
|
386
|
+
agentRuntime?: AgentRuntimeModel,
|
|
387
|
+
): Promise<{ failed: boolean }> {
|
|
388
|
+
if (!agent) {
|
|
389
|
+
await applyPhaseStatus(cwd, audit, phaseName, {
|
|
390
|
+
status: "failed",
|
|
391
|
+
error: `agent missing for ${phaseName}`,
|
|
392
|
+
});
|
|
393
|
+
throw new Error(`agent missing for ${phaseName}`);
|
|
394
|
+
}
|
|
395
|
+
await applyPhaseStatus(cwd, audit, phaseName, { status: "in_progress" });
|
|
396
|
+
if (dirs.length === 0) {
|
|
397
|
+
await applyPhaseStatus(cwd, audit, phaseName, { status: "skipped" });
|
|
398
|
+
return { failed: false };
|
|
399
|
+
}
|
|
400
|
+
const scheduler = new Scheduler({ maxConcurrent: 3, ...(signal ? { signal } : {}) });
|
|
401
|
+
const results = await Promise.allSettled(
|
|
402
|
+
dirs.map((d) =>
|
|
403
|
+
scheduler.enqueue({
|
|
404
|
+
id: `${phaseName}:${d.id}`,
|
|
405
|
+
run: (sig) =>
|
|
406
|
+
runAgentPhase({
|
|
407
|
+
cwd,
|
|
408
|
+
audit,
|
|
409
|
+
phaseName: `${phaseName}:${d.id}`,
|
|
410
|
+
statusKey: "piolium-balanced",
|
|
411
|
+
statusLabel: `● ${phaseName} ${d.id}`,
|
|
412
|
+
agent,
|
|
413
|
+
missingAgentMessage: `agent missing for ${phaseName}`,
|
|
414
|
+
task: taskBuilder(d.path, d.slug),
|
|
415
|
+
gate: () =>
|
|
416
|
+
phaseName === "B7"
|
|
417
|
+
? readdirSync(d.path).some((f) => f.startsWith("poc.")) ||
|
|
418
|
+
existsSync(join(d.path, "poc.theoretical.md"))
|
|
419
|
+
: existsSync(join(d.path, "report.md")),
|
|
420
|
+
mode: "balanced",
|
|
421
|
+
ui,
|
|
422
|
+
agentRuntime,
|
|
423
|
+
...(sig ? { signal: sig } : {}),
|
|
424
|
+
}),
|
|
425
|
+
}),
|
|
426
|
+
),
|
|
427
|
+
);
|
|
428
|
+
scheduler.dispose();
|
|
429
|
+
const failed = results.some((r) => r.status === "rejected");
|
|
430
|
+
await applyPhaseStatus(cwd, audit, phaseName, {
|
|
431
|
+
status: failed ? "failed" : "complete",
|
|
432
|
+
...(failed ? { error: `Some per-finding ${phaseName} runs failed.` } : {}),
|
|
433
|
+
});
|
|
434
|
+
return { failed };
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
export interface BalancedCleanupResult {
|
|
438
|
+
summaryPath: string;
|
|
439
|
+
removed: string[];
|
|
440
|
+
missing: string[];
|
|
441
|
+
retained: string[];
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
export function cleanupBalancedTransientArtifacts(cwd: string): BalancedCleanupResult {
|
|
445
|
+
mkdirSync(join(cwd, BALANCED_ATTACK_SURFACE_DIR), { recursive: true });
|
|
446
|
+
const removed: string[] = [];
|
|
447
|
+
const missing: string[] = [];
|
|
448
|
+
for (const rel of BALANCED_TRANSIENT_PATHS) {
|
|
449
|
+
const abs = join(cwd, rel);
|
|
450
|
+
if (!existsSync(abs)) {
|
|
451
|
+
missing.push(rel);
|
|
452
|
+
continue;
|
|
453
|
+
}
|
|
454
|
+
rmSync(abs, { recursive: true, force: true });
|
|
455
|
+
removed.push(rel);
|
|
456
|
+
}
|
|
457
|
+
const result: BalancedCleanupResult = {
|
|
458
|
+
summaryPath: BALANCED_CLEANUP_SUMMARY,
|
|
459
|
+
removed,
|
|
460
|
+
missing,
|
|
461
|
+
retained: [
|
|
462
|
+
"piolium/attack-surface/",
|
|
463
|
+
"piolium/findings/",
|
|
464
|
+
"piolium/findings-theoretical/",
|
|
465
|
+
"piolium/final-audit-report.md",
|
|
466
|
+
"piolium/audit-state.json",
|
|
467
|
+
],
|
|
468
|
+
};
|
|
469
|
+
writeFileSync(join(cwd, BALANCED_CLEANUP_SUMMARY), `${JSON.stringify(result, null, "\t")}\n`);
|
|
470
|
+
return result;
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
/**
|
|
474
|
+
* Post-B9 finalize: file-state stamp note + transient cleanup, folded inline
|
|
475
|
+
* (upstream balanced has no separate verify/cleanup tail phase).
|
|
476
|
+
*/
|
|
477
|
+
function finalizeBalanced(cwd: string): void {
|
|
478
|
+
mkdirSync(join(cwd, BALANCED_ATTACK_SURFACE_DIR), { recursive: true });
|
|
479
|
+
const confirmed = listFindingDirs(cwd);
|
|
480
|
+
const theoretical = listAllFindingDirs(cwd).filter(
|
|
481
|
+
(d) => !confirmed.some((c) => c.path === d.path),
|
|
482
|
+
);
|
|
483
|
+
const missingReports = listAllFindingDirs(cwd)
|
|
484
|
+
.filter((d) => !d.hasReport)
|
|
485
|
+
.map((d) => `${d.id}-${d.slug}`);
|
|
486
|
+
const cleanup = cleanupBalancedTransientArtifacts(cwd);
|
|
487
|
+
writeFileSync(
|
|
488
|
+
join(cwd, BALANCED_VERIFICATION_SUMMARY),
|
|
489
|
+
[
|
|
490
|
+
"# Balanced Verification & Cleanup",
|
|
491
|
+
"",
|
|
492
|
+
`Generated: ${new Date().toISOString()}`,
|
|
493
|
+
"",
|
|
494
|
+
"## Verification",
|
|
495
|
+
"",
|
|
496
|
+
"- Scope: package verification; live target confirmation remains `/piolium-confirm`.",
|
|
497
|
+
`- Confirmed findings: ${confirmed.length}`,
|
|
498
|
+
`- Theoretical findings: ${theoretical.length}`,
|
|
499
|
+
`- Missing report.md: ${missingReports.length > 0 ? missingReports.join(", ") : "none"}`,
|
|
500
|
+
"",
|
|
501
|
+
"## File-State Stamp",
|
|
502
|
+
"",
|
|
503
|
+
"- Per-file hashes for incremental scope are persisted to `piolium/file-state.json` by the scan layer; this run records the audit id that touched each file.",
|
|
504
|
+
"",
|
|
505
|
+
"## Cleanup",
|
|
506
|
+
"",
|
|
507
|
+
`- Removed: ${cleanup.removed.length > 0 ? cleanup.removed.map((p) => `\`${p}\``).join(", ") : "(none)"}`,
|
|
508
|
+
`- Cleanup summary: \`${cleanup.summaryPath}\``,
|
|
509
|
+
"",
|
|
510
|
+
].join("\n"),
|
|
511
|
+
);
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
export async function runBalancedAudit(opts: RunBalancedOptions): Promise<RunBalancedResult> {
|
|
515
|
+
const { cwd, signal, ui } = opts;
|
|
516
|
+
ui?.setStatus?.("piolium-balanced", "● preparing recon");
|
|
517
|
+
const recon = await runReconAsync(cwd, { signal });
|
|
518
|
+
mkdirSync(join(cwd, BALANCED_ATTACK_SURFACE_DIR), { recursive: true });
|
|
519
|
+
ui?.setStatus?.("piolium-balanced", "● scanning candidate files");
|
|
520
|
+
const candidateScan = await runCandidateScanAsync(cwd, { signal });
|
|
521
|
+
ui?.notify?.(
|
|
522
|
+
`Candidate scan: ${candidateScan.candidateCount} match(es) across ${candidateScan.candidateFiles} file(s).`,
|
|
523
|
+
"info",
|
|
524
|
+
);
|
|
525
|
+
let audit = pickResume(cwd, opts.forceFresh ?? false);
|
|
526
|
+
if (!audit) {
|
|
527
|
+
audit = await initAudit(cwd, {
|
|
528
|
+
mode: "balanced",
|
|
529
|
+
...(recon.commit ? { commit: recon.commit } : { commit: null }),
|
|
530
|
+
...(recon.branch ? { branch: recon.branch } : { branch: "nogit" }),
|
|
531
|
+
...(recon.repository ? { repository: recon.repository } : {}),
|
|
532
|
+
history_available: recon.historyAvailable,
|
|
533
|
+
agent_sdk: "pi",
|
|
534
|
+
});
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
const { agents } = loadAgents({ cwd });
|
|
538
|
+
const cveScout = agents.get("cve-scout");
|
|
539
|
+
const threatModeler = agents.get("threat-modeler");
|
|
540
|
+
const codeScanner = agents.get("code-scanner");
|
|
541
|
+
const probeLead = agents.get("probe-lead");
|
|
542
|
+
const reviewAdjudicator = agents.get("review-adjudicator");
|
|
543
|
+
const contextReviewer = agents.get("context-reviewer");
|
|
544
|
+
const pocAuthor = agents.get("poc-author");
|
|
545
|
+
const findingWriter = agents.get("finding-writer");
|
|
546
|
+
const reportComposer = agents.get("report-composer");
|
|
547
|
+
|
|
548
|
+
let failed = false;
|
|
549
|
+
|
|
550
|
+
try {
|
|
551
|
+
// B1 Intel Sweep
|
|
552
|
+
await runAgentPhase({
|
|
553
|
+
cwd,
|
|
554
|
+
audit,
|
|
555
|
+
phaseName: "B1",
|
|
556
|
+
statusKey: "piolium-balanced",
|
|
557
|
+
statusLabel: "● B1 intel sweep",
|
|
558
|
+
agent: cveScout,
|
|
559
|
+
missingAgentMessage: "cve-scout agent missing",
|
|
560
|
+
task: buildB1Task(),
|
|
561
|
+
gate: gateB1(cwd),
|
|
562
|
+
mode: "balanced",
|
|
563
|
+
ui,
|
|
564
|
+
agentRuntime: opts.agentRuntime,
|
|
565
|
+
...(signal ? { signal } : {}),
|
|
566
|
+
});
|
|
567
|
+
|
|
568
|
+
// B2 Threat Model
|
|
569
|
+
await runAgentPhase({
|
|
570
|
+
cwd,
|
|
571
|
+
audit,
|
|
572
|
+
phaseName: "B2",
|
|
573
|
+
statusKey: "piolium-balanced",
|
|
574
|
+
statusLabel: "● B2 threat model",
|
|
575
|
+
agent: threatModeler,
|
|
576
|
+
missingAgentMessage: "threat-modeler agent missing",
|
|
577
|
+
task: buildB2Task(),
|
|
578
|
+
gate: gateB2(cwd),
|
|
579
|
+
mode: "balanced",
|
|
580
|
+
ui,
|
|
581
|
+
agentRuntime: opts.agentRuntime,
|
|
582
|
+
...(signal ? { signal } : {}),
|
|
583
|
+
});
|
|
584
|
+
|
|
585
|
+
// B3 + B4 in parallel
|
|
586
|
+
const par = await runB3PlusB4Parallel(
|
|
587
|
+
cwd,
|
|
588
|
+
audit,
|
|
589
|
+
codeScanner,
|
|
590
|
+
probeLead,
|
|
591
|
+
signal,
|
|
592
|
+
ui,
|
|
593
|
+
opts.agentRuntime,
|
|
594
|
+
);
|
|
595
|
+
if (par.failed) failed = true;
|
|
596
|
+
|
|
597
|
+
// B5 Review Panel + FP Check
|
|
598
|
+
if (!failed) {
|
|
599
|
+
await runAgentPhase({
|
|
600
|
+
cwd,
|
|
601
|
+
audit,
|
|
602
|
+
phaseName: "B5",
|
|
603
|
+
statusKey: "piolium-balanced",
|
|
604
|
+
statusLabel: "● B5 review panel",
|
|
605
|
+
agent: reviewAdjudicator,
|
|
606
|
+
missingAgentMessage: "review-adjudicator agent missing",
|
|
607
|
+
task: buildB5Task(),
|
|
608
|
+
gate: gateB5(cwd),
|
|
609
|
+
mode: "balanced",
|
|
610
|
+
ui,
|
|
611
|
+
agentRuntime: opts.agentRuntime,
|
|
612
|
+
...(signal ? { signal } : {}),
|
|
613
|
+
});
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
// B6 Intent Reconciliation (best-effort; skip-and-continue).
|
|
617
|
+
if (!failed) {
|
|
618
|
+
try {
|
|
619
|
+
await runAgentPhase({
|
|
620
|
+
cwd,
|
|
621
|
+
audit,
|
|
622
|
+
phaseName: "B6",
|
|
623
|
+
statusKey: "piolium-balanced",
|
|
624
|
+
statusLabel: "● B6 intent reconciliation",
|
|
625
|
+
agent: contextReviewer,
|
|
626
|
+
missingAgentMessage: "context-reviewer agent missing",
|
|
627
|
+
task: buildB6Task(),
|
|
628
|
+
gate: gateB6(cwd),
|
|
629
|
+
mode: "balanced",
|
|
630
|
+
ui,
|
|
631
|
+
agentRuntime: opts.agentRuntime,
|
|
632
|
+
...(signal ? { signal } : {}),
|
|
633
|
+
});
|
|
634
|
+
} catch {
|
|
635
|
+
// Intent Reconciliation never blocks the pipeline.
|
|
636
|
+
await applyPhaseStatus(cwd, audit, "B6", {
|
|
637
|
+
status: "failed",
|
|
638
|
+
error: "policy: skip-and-continue",
|
|
639
|
+
});
|
|
640
|
+
ui?.notify?.("B6 Intent Reconciliation skipped (skip-and-continue).", "warning");
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
// Consolidate chamber-survived drafts (b5-*) into severity-prefixed
|
|
644
|
+
// findings/<C|H|M><N>-<slug>/. The triager / B6 may have set
|
|
645
|
+
// Triage-Priority: skip on some drafts.
|
|
646
|
+
const consolidation = consolidateDrafts(cwd, ["b5-"]);
|
|
647
|
+
const manifest = {
|
|
648
|
+
generated_at: new Date().toISOString(),
|
|
649
|
+
source_prefixes: ["b5-"],
|
|
650
|
+
promoted: consolidation.promoted.map((entry) => ({
|
|
651
|
+
id: entry.id,
|
|
652
|
+
slug: entry.slug,
|
|
653
|
+
severity: entry.severity,
|
|
654
|
+
original_id: entry.originalId,
|
|
655
|
+
...(entry.phase ? { phase: entry.phase } : {}),
|
|
656
|
+
source_path: entry.sourcePath,
|
|
657
|
+
finding_dir: entry.findingDir,
|
|
658
|
+
})),
|
|
659
|
+
dropped: consolidation.dropped.map((entry) => ({
|
|
660
|
+
original_id: entry.originalId,
|
|
661
|
+
severity: entry.severity,
|
|
662
|
+
source_path: entry.sourcePath,
|
|
663
|
+
reason: entry.reason,
|
|
664
|
+
})),
|
|
665
|
+
};
|
|
666
|
+
writeFileSync(
|
|
667
|
+
join(cwd, BALANCED_CONSOLIDATION_MANIFEST),
|
|
668
|
+
`${JSON.stringify(manifest, null, "\t")}\n`,
|
|
669
|
+
);
|
|
670
|
+
// Route triage-skip / intent-skip drafts straight to the
|
|
671
|
+
// theoretical bucket before any PoC effort.
|
|
672
|
+
const part = partitionFindings(cwd);
|
|
673
|
+
ui?.notify?.(
|
|
674
|
+
`B5/B6 promoted ${consolidation.promoted.length} finding(s); ${part.theoretical.length} routed to theoretical; dropped ${consolidation.dropped.length}.`,
|
|
675
|
+
"info",
|
|
676
|
+
);
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
// B7 — per-finding PoC (confirmed bucket only).
|
|
680
|
+
if (!failed) {
|
|
681
|
+
const r = await runPerFindingPhase(
|
|
682
|
+
cwd,
|
|
683
|
+
audit,
|
|
684
|
+
"B7",
|
|
685
|
+
pocAuthor,
|
|
686
|
+
buildB7Task,
|
|
687
|
+
listFindingDirs(cwd),
|
|
688
|
+
ui,
|
|
689
|
+
signal,
|
|
690
|
+
opts.agentRuntime,
|
|
691
|
+
);
|
|
692
|
+
if (r.failed) failed = true;
|
|
693
|
+
// Demote any finding whose PoC did not execute into theoretical.
|
|
694
|
+
if (!failed) partitionFindings(cwd);
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
// B8 — per-finding finalisation over BOTH buckets.
|
|
698
|
+
if (!failed) {
|
|
699
|
+
const r = await runPerFindingPhase(
|
|
700
|
+
cwd,
|
|
701
|
+
audit,
|
|
702
|
+
"B8",
|
|
703
|
+
findingWriter,
|
|
704
|
+
(dir) => buildB8Task(dir),
|
|
705
|
+
listAllFindingDirs(cwd),
|
|
706
|
+
ui,
|
|
707
|
+
signal,
|
|
708
|
+
opts.agentRuntime,
|
|
709
|
+
);
|
|
710
|
+
if (r.failed) failed = true;
|
|
711
|
+
}
|
|
712
|
+
|
|
713
|
+
// B9 — final report assembly + inline cleanup/file-state stamp.
|
|
714
|
+
if (!failed) {
|
|
715
|
+
await runAgentPhase({
|
|
716
|
+
cwd,
|
|
717
|
+
audit,
|
|
718
|
+
phaseName: "B9",
|
|
719
|
+
statusKey: "piolium-balanced",
|
|
720
|
+
statusLabel: "● B9 report compose",
|
|
721
|
+
agent: reportComposer,
|
|
722
|
+
missingAgentMessage: "report-composer agent missing",
|
|
723
|
+
task: buildB9Task(),
|
|
724
|
+
gate: gateB9(cwd),
|
|
725
|
+
mode: "balanced",
|
|
726
|
+
ui,
|
|
727
|
+
agentRuntime: opts.agentRuntime,
|
|
728
|
+
...(signal ? { signal } : {}),
|
|
729
|
+
});
|
|
730
|
+
if (!failed) finalizeBalanced(cwd);
|
|
731
|
+
}
|
|
732
|
+
} catch {
|
|
733
|
+
failed = true;
|
|
734
|
+
}
|
|
735
|
+
|
|
736
|
+
await markAuditStatus(cwd, audit.audit_id, failed ? "failed" : "complete");
|
|
737
|
+
const fresh =
|
|
738
|
+
readAuditState(cwd).state?.audits.find((a) => a.audit_id === audit.audit_id) ?? audit;
|
|
739
|
+
const phases: Record<string, "complete" | "failed" | "skipped"> = {};
|
|
740
|
+
for (const [name, p] of Object.entries(fresh.phases)) {
|
|
741
|
+
if (p.status === "complete" || p.status === "failed" || p.status === "skipped") {
|
|
742
|
+
phases[name] = p.status;
|
|
743
|
+
}
|
|
744
|
+
}
|
|
745
|
+
ui?.notify?.(
|
|
746
|
+
failed ? "Balanced audit failed." : "Balanced audit complete.",
|
|
747
|
+
failed ? "error" : "info",
|
|
748
|
+
);
|
|
749
|
+
return { auditId: audit.audit_id, status: failed ? "failed" : "complete", phases };
|
|
750
|
+
}
|