@soyrageagency/proxmox-mcp 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (115) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +792 -0
  3. package/dist/assets.d.ts +18 -0
  4. package/dist/assets.js +53 -0
  5. package/dist/assets.js.map +1 -0
  6. package/dist/branding.d.ts +28 -0
  7. package/dist/branding.js +58 -0
  8. package/dist/branding.js.map +1 -0
  9. package/dist/config.d.ts +70 -0
  10. package/dist/config.js +119 -0
  11. package/dist/config.js.map +1 -0
  12. package/dist/index.d.ts +14 -0
  13. package/dist/index.js +89 -0
  14. package/dist/index.js.map +1 -0
  15. package/dist/logger.d.ts +23 -0
  16. package/dist/logger.js +65 -0
  17. package/dist/logger.js.map +1 -0
  18. package/dist/plugins.d.ts +32 -0
  19. package/dist/plugins.js +149 -0
  20. package/dist/plugins.js.map +1 -0
  21. package/dist/proxmox/client.d.ts +153 -0
  22. package/dist/proxmox/client.js +620 -0
  23. package/dist/proxmox/client.js.map +1 -0
  24. package/dist/resilience/backup-verifier.d.ts +28 -0
  25. package/dist/resilience/backup-verifier.js +222 -0
  26. package/dist/resilience/backup-verifier.js.map +1 -0
  27. package/dist/resilience/dr-drill.d.ts +17 -0
  28. package/dist/resilience/dr-drill.js +138 -0
  29. package/dist/resilience/dr-drill.js.map +1 -0
  30. package/dist/resilience/engine.d.ts +68 -0
  31. package/dist/resilience/engine.js +151 -0
  32. package/dist/resilience/engine.js.map +1 -0
  33. package/dist/resilience/patch-orchestrator.d.ts +31 -0
  34. package/dist/resilience/patch-orchestrator.js +188 -0
  35. package/dist/resilience/patch-orchestrator.js.map +1 -0
  36. package/dist/resilience/report.d.ts +33 -0
  37. package/dist/resilience/report.js +264 -0
  38. package/dist/resilience/report.js.map +1 -0
  39. package/dist/resilience/runbook.d.ts +42 -0
  40. package/dist/resilience/runbook.js +165 -0
  41. package/dist/resilience/runbook.js.map +1 -0
  42. package/dist/resilience/signing.d.ts +43 -0
  43. package/dist/resilience/signing.js +107 -0
  44. package/dist/resilience/signing.js.map +1 -0
  45. package/dist/resilience/types.d.ts +150 -0
  46. package/dist/resilience/types.js +14 -0
  47. package/dist/resilience/types.js.map +1 -0
  48. package/dist/resilience/util.d.ts +16 -0
  49. package/dist/resilience/util.js +32 -0
  50. package/dist/resilience/util.js.map +1 -0
  51. package/dist/tools/about.d.ts +14 -0
  52. package/dist/tools/about.js +54 -0
  53. package/dist/tools/about.js.map +1 -0
  54. package/dist/tools/backups.d.ts +15 -0
  55. package/dist/tools/backups.js +91 -0
  56. package/dist/tools/backups.js.map +1 -0
  57. package/dist/tools/cluster.d.ts +12 -0
  58. package/dist/tools/cluster.js +48 -0
  59. package/dist/tools/cluster.js.map +1 -0
  60. package/dist/tools/context.d.ts +30 -0
  61. package/dist/tools/context.js +12 -0
  62. package/dist/tools/context.js.map +1 -0
  63. package/dist/tools/guests.d.ts +15 -0
  64. package/dist/tools/guests.js +96 -0
  65. package/dist/tools/guests.js.map +1 -0
  66. package/dist/tools/lifecycle.d.ts +16 -0
  67. package/dist/tools/lifecycle.js +93 -0
  68. package/dist/tools/lifecycle.js.map +1 -0
  69. package/dist/tools/management.d.ts +17 -0
  70. package/dist/tools/management.js +135 -0
  71. package/dist/tools/management.js.map +1 -0
  72. package/dist/tools/nodes.d.ts +12 -0
  73. package/dist/tools/nodes.js +44 -0
  74. package/dist/tools/nodes.js.map +1 -0
  75. package/dist/tools/provisioning.d.ts +16 -0
  76. package/dist/tools/provisioning.js +122 -0
  77. package/dist/tools/provisioning.js.map +1 -0
  78. package/dist/tools/resilience.d.ts +21 -0
  79. package/dist/tools/resilience.js +116 -0
  80. package/dist/tools/resilience.js.map +1 -0
  81. package/dist/tools/snapshots.d.ts +17 -0
  82. package/dist/tools/snapshots.js +89 -0
  83. package/dist/tools/snapshots.js.map +1 -0
  84. package/dist/tools/storage.d.ts +11 -0
  85. package/dist/tools/storage.js +39 -0
  86. package/dist/tools/storage.js.map +1 -0
  87. package/dist/tools/tasks.d.ts +11 -0
  88. package/dist/tools/tasks.js +43 -0
  89. package/dist/tools/tasks.js.map +1 -0
  90. package/dist/tui/ansi.d.ts +63 -0
  91. package/dist/tui/ansi.js +115 -0
  92. package/dist/tui/ansi.js.map +1 -0
  93. package/dist/tui/app.d.ts +109 -0
  94. package/dist/tui/app.js +904 -0
  95. package/dist/tui/app.js.map +1 -0
  96. package/dist/tui/box.d.ts +18 -0
  97. package/dist/tui/box.js +38 -0
  98. package/dist/tui/box.js.map +1 -0
  99. package/dist/tui/index.d.ts +20 -0
  100. package/dist/tui/index.js +65 -0
  101. package/dist/tui/index.js.map +1 -0
  102. package/dist/update/channel.d.ts +69 -0
  103. package/dist/update/channel.js +165 -0
  104. package/dist/update/channel.js.map +1 -0
  105. package/dist/update/notice.d.ts +11 -0
  106. package/dist/update/notice.js +27 -0
  107. package/dist/update/notice.js.map +1 -0
  108. package/dist/utils/format.d.ts +21 -0
  109. package/dist/utils/format.js +63 -0
  110. package/dist/utils/format.js.map +1 -0
  111. package/dist/utils/result.d.ts +25 -0
  112. package/dist/utils/result.js +26 -0
  113. package/dist/utils/result.js.map +1 -0
  114. package/package.json +87 -0
  115. package/scripts/install.mjs +301 -0
@@ -0,0 +1,264 @@
1
+ /**
2
+ * Resilience evidence reports — control mapping, rendering & persistence.
3
+ *
4
+ * Turns a finished run into audit-grade artefacts: a signed JSON record
5
+ * (machine-readable), a Markdown report (diff-able, lives in git) and a
6
+ * branded HTML report that prints straight to PDF for an auditor. Each report
7
+ * maps its result onto concrete ISO 27001 / NIS2 / DORA controls.
8
+ *
9
+ * Part of Proxmox MCP Server.
10
+ * Crafted by SoyRage Agency — https://soyrage.es/
11
+ * MIT licensed (see LICENSE).
12
+ */
13
+ import { mkdirSync, writeFileSync } from "node:fs";
14
+ import { join } from "node:path";
15
+ import { BRAND } from "../branding.js";
16
+ /** The compliance controls each capability produces evidence for. */
17
+ export const CONTROLS = {
18
+ "backup-verify": [
19
+ { framework: "ISO 27001", clause: "A.8.13", title: "Information backup" },
20
+ { framework: "ISO 27001", clause: "A.5.29", title: "ICT readiness for business continuity" },
21
+ { framework: "NIS2", clause: "Art. 21(2)(c)", title: "Backup management & crisis management" },
22
+ { framework: "DORA", clause: "Art. 12", title: "Backup, restoration & recovery" },
23
+ ],
24
+ "patch-orchestrate": [
25
+ { framework: "ISO 27001", clause: "A.8.8", title: "Management of technical vulnerabilities" },
26
+ { framework: "ISO 27001", clause: "A.8.32", title: "Change management" },
27
+ { framework: "NIS2", clause: "Art. 21(2)(e)", title: "Security in acquisition, development & maintenance" },
28
+ { framework: "DORA", clause: "Art. 9", title: "Protection & prevention" },
29
+ ],
30
+ "dr-drill": [
31
+ { framework: "ISO 27001", clause: "A.5.30", title: "ICT readiness for business continuity" },
32
+ { framework: "NIS2", clause: "Art. 21(2)(c)", title: "Business continuity & disaster recovery" },
33
+ { framework: "DORA", clause: "Art. 11", title: "Response & recovery" },
34
+ { framework: "DORA", clause: "Art. 24-25", title: "Digital operational resilience testing" },
35
+ ],
36
+ };
37
+ const MARK = { pass: "PASS", fail: "FAIL", warn: "WARN", skip: "SKIP" };
38
+ const GLYPH = { pass: "✓", fail: "✗", warn: "!", skip: "·" };
39
+ const HTML_COLOR = { pass: "#2f9e57", fail: "#d64545", warn: "#c9922e", skip: "#8a95a8" };
40
+ /** Format seconds as "4m 12s" / "1h 03m" / "45s". */
41
+ export function dur(sec) {
42
+ if (!Number.isFinite(sec) || sec < 0)
43
+ return "—";
44
+ const h = Math.floor(sec / 3600);
45
+ const m = Math.floor((sec % 3600) / 60);
46
+ const s = Math.round(sec % 60);
47
+ if (h > 0)
48
+ return `${h}h ${String(m).padStart(2, "0")}m`;
49
+ if (m > 0)
50
+ return `${m}m ${String(s).padStart(2, "0")}s`;
51
+ return `${s}s`;
52
+ }
53
+ /** A one-line verdict banner for the console/MCP text result. */
54
+ export function verdict(r) {
55
+ const badge = MARK[r.outcome];
56
+ return `[${badge}] ${r.title} — ${r.summary}`;
57
+ }
58
+ /** Reduce a report to a compact dashboard summary. */
59
+ export function summarize(r) {
60
+ let metric = "";
61
+ if (r.capability === "backup-verify") {
62
+ const checks = r.items.reduce((a, it) => a + it.checks.length, 0);
63
+ const passed = r.items.reduce((a, it) => a + it.checks.filter((c) => c.outcome === "pass").length, 0);
64
+ metric = `${passed}/${checks} checks · RTO ${dur(Math.max(0, ...r.items.map((i) => i.restoreSec)))}`;
65
+ }
66
+ else if (r.capability === "patch-orchestrate") {
67
+ metric = `${r.patched} patched · ${r.rolledBack} rolled back`;
68
+ }
69
+ else {
70
+ metric = `RTO ${dur(r.rtoSec)} · RPO ${dur(r.rpoSec)}`;
71
+ }
72
+ return {
73
+ capability: r.capability,
74
+ title: r.title,
75
+ outcome: r.outcome,
76
+ finishedAt: r.finishedAt,
77
+ metric,
78
+ signedBy: r.signature ? r.signature.keyFingerprint.slice(0, 12) : "",
79
+ id: r.id,
80
+ };
81
+ }
82
+ function checkLine(c) {
83
+ const ms = c.ms !== undefined ? ` (${c.ms} ms)` : "";
84
+ return `- ${GLYPH[c.outcome]} **${c.label}** — ${c.detail}${ms}`;
85
+ }
86
+ function controlsTable(controls) {
87
+ const rows = controls.map((c) => `| ${c.framework} | ${c.clause} | ${c.title} |`).join("\n");
88
+ return `| Framework | Control | Title |\n| --- | --- | --- |\n${rows}`;
89
+ }
90
+ function signatureBlock(r) {
91
+ if (!r.signature)
92
+ return "_Unsigned._";
93
+ const s = r.signature;
94
+ return [
95
+ `- **Algorithm:** ${s.algorithm}`,
96
+ `- **Signed at:** ${s.signedAt}`,
97
+ `- **Payload SHA-256:** \`${s.digest}\``,
98
+ `- **Signature (base64):** \`${s.signature}\``,
99
+ `- **Key fingerprint:** \`${s.keyFingerprint}\``,
100
+ "",
101
+ "Verify offline with the bundled public key:",
102
+ "",
103
+ "```",
104
+ s.publicKeyPem,
105
+ "```",
106
+ ].join("\n");
107
+ }
108
+ /** Render a report as a Markdown evidence document. */
109
+ export function renderMarkdown(r) {
110
+ const head = [
111
+ `# ${r.title}`,
112
+ "",
113
+ `**Verdict:** ${MARK[r.outcome]} · **Report ID:** \`${r.id}\`${r.demo ? " · _demo data_" : ""}`,
114
+ "",
115
+ `- **Started:** ${r.startedAt}`,
116
+ `- **Finished:** ${r.finishedAt}`,
117
+ `- **Duration:** ${dur(r.durationSec)}`,
118
+ `- **Summary:** ${r.summary}`,
119
+ "",
120
+ ];
121
+ const body = [];
122
+ if (r.capability === "backup-verify") {
123
+ const rep = r;
124
+ body.push(`## Verified backups`, "", `Ephemeral guests were fenced onto the isolated bridge \`${rep.isolatedBridge}\` and destroyed after testing.`, "");
125
+ for (const it of rep.items) {
126
+ body.push(`### ${GLYPH[it.outcome]} ${it.name} (VMID ${it.vmid})`, "", `- Archive: \`${it.archive}\` (age ${dur(it.archiveAgeSec)})`, `- Restored to ephemeral VMID ${it.ephemeralVmid} in **${dur(it.restoreSec)}** (measured RTO)`, `- Cleanup: ${it.cleanedUp ? "ephemeral guest destroyed ✓" : "**left behind — investigate**"}`, "", ...it.checks.map(checkLine), "");
127
+ }
128
+ }
129
+ else if (r.capability === "patch-orchestrate") {
130
+ const rep = r;
131
+ body.push(`## Patch run`, "", `Maintenance window: ${rep.window || "anytime"}. Guests are patched in dependency order; any guest failing its post-patch health check is rolled back to its pre-patch snapshot automatically.`, "", `| Batch | Guest | Snapshot | Updates | Health | Result |`, `| --- | --- | --- | --- | --- | --- |`, ...rep.steps.map((s) => `| ${s.batch} | ${s.name} (${s.vmid}) | \`${s.snapshot}\` | ${s.updates} | ${GLYPH[s.health.outcome]} ${s.health.detail} | ${s.rolledBack ? "ROLLED BACK" : MARK[s.outcome]} |`), "", `**${rep.patched} patched, ${rep.rolledBack} automatically rolled back.**`, "");
132
+ }
133
+ else {
134
+ const rep = r;
135
+ body.push(`## Disaster-recovery drill`, "", `- Runbook: **${rep.runbook}**`, `- Environment: **${rep.environment}** (never production)`, `- Measured RTO: **${dur(rep.rtoSec)}** · RPO: **${dur(rep.rpoSec)}**`, "", `| # | Action | Target | Result | Detail |`, `| --- | --- | --- | --- | --- |`, ...rep.steps.map((s) => `| ${s.index} | ${s.action} | ${s.target} | ${GLYPH[s.outcome]} ${MARK[s.outcome]} | ${s.detail} |`), "");
136
+ }
137
+ const tail = [
138
+ `## Compliance mapping`,
139
+ "",
140
+ "This evidence supports the following controls:",
141
+ "",
142
+ controlsTable(r.controls),
143
+ "",
144
+ `## Signature`,
145
+ "",
146
+ signatureBlock(r),
147
+ "",
148
+ "---",
149
+ "",
150
+ `Generated by **${BRAND.product}** — ${BRAND.author} · ${BRAND.url}`,
151
+ `Support the project: ${BRAND.donate}`,
152
+ "",
153
+ ];
154
+ return [...head, ...body, ...tail].join("\n");
155
+ }
156
+ const esc = (s) => s.replace(/[&<>]/g, (c) => ({ "&": "&amp;", "<": "&lt;", ">": "&gt;" }[c]));
157
+ /** Render a branded, printable HTML evidence report. */
158
+ export function renderHtml(r) {
159
+ const accent = "#3b9ef0";
160
+ const pill = (o) => `<span class="pill" style="background:${HTML_COLOR[o]}">${MARK[o]}</span>`;
161
+ const detail = [];
162
+ if (r.capability === "backup-verify") {
163
+ const rep = r;
164
+ detail.push(`<p class="muted">Ephemeral guests fenced onto isolated bridge <code>${esc(rep.isolatedBridge)}</code> and destroyed after testing.</p>`);
165
+ for (const it of rep.items) {
166
+ detail.push(`<div class="card"><h3>${pill(it.outcome)} ${esc(it.name)} <span class="muted">VMID ${it.vmid}</span></h3>`, `<p class="muted">Archive <code>${esc(it.archive)}</code> · age ${dur(it.archiveAgeSec)} · restored to VMID ${it.ephemeralVmid} in <strong>${dur(it.restoreSec)}</strong> (RTO) · ${it.cleanedUp ? "cleaned up" : "<strong>not cleaned up</strong>"}</p>`, `<table class="checks">${it.checks
167
+ .map((c) => `<tr><td class="g" style="color:${HTML_COLOR[c.outcome]}">${GLYPH[c.outcome]}</td><td>${esc(c.label)}</td><td class="muted">${esc(c.detail)}</td><td class="muted">${c.ms !== undefined ? c.ms + " ms" : ""}</td></tr>`)
168
+ .join("")}</table></div>`);
169
+ }
170
+ }
171
+ else if (r.capability === "patch-orchestrate") {
172
+ const rep = r;
173
+ detail.push(`<p class="muted">Maintenance window: ${esc(rep.window || "anytime")}. Patched in dependency order; failed health checks trigger an automatic rollback.</p>`, `<table class="grid"><tr><th>Batch</th><th>Guest</th><th>Snapshot</th><th>Updates</th><th>Health</th><th>Result</th></tr>`, ...rep.steps.map((s) => `<tr><td>${s.batch}</td><td>${esc(s.name)} <span class="muted">${s.vmid}</span></td><td><code>${esc(s.snapshot)}</code></td><td>${s.updates}</td><td style="color:${HTML_COLOR[s.health.outcome]}">${GLYPH[s.health.outcome]} ${esc(s.health.detail)}</td><td>${s.rolledBack ? '<span class="pill" style="background:' + HTML_COLOR.warn + '">ROLLED BACK</span>' : pill(s.outcome)}</td></tr>`), `</table><p><strong>${rep.patched} patched, ${rep.rolledBack} automatically rolled back.</strong></p>`);
174
+ }
175
+ else {
176
+ const rep = r;
177
+ detail.push(`<p class="muted">Runbook <strong>${esc(rep.runbook)}</strong> · environment <strong>${esc(rep.environment)}</strong> · RTO <strong>${dur(rep.rtoSec)}</strong> · RPO <strong>${dur(rep.rpoSec)}</strong></p>`, `<table class="grid"><tr><th>#</th><th>Action</th><th>Target</th><th>Result</th><th>Detail</th></tr>`, ...rep.steps.map((s) => `<tr><td>${s.index}</td><td>${esc(s.action)}</td><td>${esc(s.target)}</td><td style="color:${HTML_COLOR[s.outcome]}">${GLYPH[s.outcome]} ${MARK[s.outcome]}</td><td class="muted">${esc(s.detail)}</td></tr>`), `</table>`);
178
+ }
179
+ const controls = r.controls
180
+ .map((c) => `<tr><td><strong>${esc(c.framework)}</strong></td><td>${esc(c.clause)}</td><td>${esc(c.title)}</td></tr>`)
181
+ .join("");
182
+ const sig = r.signature;
183
+ const sigHtml = sig
184
+ ? `<table class="kv">
185
+ <tr><td>Algorithm</td><td>${esc(sig.algorithm)}</td></tr>
186
+ <tr><td>Signed at</td><td>${esc(sig.signedAt)}</td></tr>
187
+ <tr><td>Payload SHA-256</td><td><code>${esc(sig.digest)}</code></td></tr>
188
+ <tr><td>Signature</td><td><code class="wrap">${esc(sig.signature)}</code></td></tr>
189
+ <tr><td>Key fingerprint</td><td><code>${esc(sig.keyFingerprint)}</code></td></tr>
190
+ </table>
191
+ <details><summary>Public key (verify offline)</summary><pre>${esc(sig.publicKeyPem)}</pre></details>`
192
+ : "<p class='muted'>Unsigned.</p>";
193
+ return `<!doctype html><html lang="en"><head><meta charset="utf-8">
194
+ <title>${esc(r.title)}</title>
195
+ <style>
196
+ :root{--accent:${accent};--ink:#111;--mute:#8b8b86;--line:#e7e3da;--bg:#f3f1ea;--grid:rgba(17,17,17,.035)}
197
+ *{box-sizing:border-box}
198
+ body{font:14px/1.55 Inter,-apple-system,Segoe UI,Roboto,Helvetica,Arial,sans-serif;color:var(--ink);margin:0;
199
+ background-color:var(--bg);background-image:linear-gradient(var(--grid) 1px,transparent 1px),linear-gradient(90deg,var(--grid) 1px,transparent 1px);background-size:46px 46px}
200
+ .page{max-width:900px;margin:28px auto;background:#fff;border:1px solid var(--line);border-radius:18px;overflow:hidden}
201
+ header{padding:30px 40px 24px;border-bottom:1px solid var(--line)}
202
+ header .brand{font-weight:800;letter-spacing:.12em;color:var(--accent);font-size:11.5px}
203
+ header h1{margin:.45em 0 .35em;font-size:26px;font-weight:800;letter-spacing:-.03em}
204
+ .pill{display:inline-block;color:#fff;border-radius:999px;padding:3px 12px;font-size:12px;font-weight:700;letter-spacing:.03em}
205
+ main{padding:26px 40px 40px}
206
+ h2{font-size:13px;text-transform:uppercase;letter-spacing:.07em;color:var(--mute);border-bottom:1px solid var(--line);padding-bottom:7px;margin-top:30px;font-weight:700}
207
+ h3{font-size:15px;margin:.4em 0}
208
+ .muted{color:var(--mute)}
209
+ code{background:#f2f0ea;border-radius:5px;padding:1px 5px;font-size:12px}
210
+ code.wrap{word-break:break-all}
211
+ .meta{display:grid;grid-template-columns:repeat(4,1fr);gap:12px;margin-top:18px}
212
+ .meta div{background:#faf9f5;border:1px solid var(--line);border-radius:12px;padding:12px 14px;color:var(--ink)}
213
+ .meta span{display:block;font-size:10px;text-transform:uppercase;letter-spacing:.08em;color:#a7a299;font-weight:700}
214
+ .meta strong{font-size:17px;font-weight:800;letter-spacing:-.02em}
215
+ table{width:100%;border-collapse:collapse;margin:10px 0}
216
+ table.grid th,table.grid td{border-bottom:1px solid #f0ede5;padding:7px 8px;text-align:left;font-size:13px}
217
+ table.grid th{color:var(--mute);font-size:10.5px;text-transform:uppercase;letter-spacing:.05em;font-weight:700}
218
+ table.checks td{padding:4px 8px;border-bottom:1px solid #f4f2ec}
219
+ table.checks td.g{width:20px;font-weight:700}
220
+ table.kv td{padding:5px 8px;border-bottom:1px solid #f4f2ec;vertical-align:top}
221
+ table.kv td:first-child{color:var(--mute);width:150px}
222
+ .card{border:1px solid var(--line);border-radius:14px;padding:14px 16px;margin:12px 0;background:#fbfaf6}
223
+ pre{background:#111;color:#c7d2e0;padding:12px;border-radius:8px;overflow:auto;font-size:11px}
224
+ footer{padding:20px 40px;border-top:1px solid var(--line);color:var(--mute);font-size:12px;display:flex;justify-content:space-between}
225
+ a{color:var(--accent)}
226
+ @media print{body{background:#fff}.page{border:none}}
227
+ </style></head><body><div class="page">
228
+ <header>
229
+ <div class="brand">SOYRAGE AGENCY · RESILIENCE EVIDENCE</div>
230
+ <h1>${esc(r.title)}</h1>
231
+ <div>${pill(r.outcome)} &nbsp;<span class="muted">Report ID ${esc(r.id)}${r.demo ? " · demo data" : ""}</span></div>
232
+ <div class="meta">
233
+ <div><span>Started</span><strong style="font-size:13px">${esc(r.startedAt.replace("T", " ").slice(0, 19))}</strong></div>
234
+ <div><span>Finished</span><strong style="font-size:13px">${esc(r.finishedAt.replace("T", " ").slice(0, 19))}</strong></div>
235
+ <div><span>Duration</span><strong>${dur(r.durationSec)}</strong></div>
236
+ <div><span>Verdict</span><strong style="color:${HTML_COLOR[r.outcome]}">${MARK[r.outcome]}</strong></div>
237
+ </div>
238
+ </header>
239
+ <main>
240
+ <p>${esc(r.summary)}</p>
241
+ <h2>Results</h2>
242
+ ${detail.join("\n")}
243
+ <h2>Compliance mapping</h2>
244
+ <table class="grid"><tr><th>Framework</th><th>Control</th><th>Title</th></tr>${controls}</table>
245
+ <h2>Signature</h2>
246
+ ${sigHtml}
247
+ </main>
248
+ <footer>
249
+ <span>Generated by ${esc(BRAND.product)} — ${esc(BRAND.author)}</span>
250
+ <span><a href="${BRAND.url}">${esc(BRAND.url)}</a> · <a href="${BRAND.donate}">Support</a></span>
251
+ </footer>
252
+ </div></body></html>`;
253
+ }
254
+ /** Persist a report as JSON + Markdown + HTML under `outDir`. Returns paths. */
255
+ export function writeReports(r, outDir) {
256
+ mkdirSync(outDir, { recursive: true });
257
+ const base = join(outDir, r.id);
258
+ const paths = { json: `${base}.json`, markdown: `${base}.md`, html: `${base}.html` };
259
+ writeFileSync(paths.json, JSON.stringify(r, null, 2));
260
+ writeFileSync(paths.markdown, renderMarkdown(r));
261
+ writeFileSync(paths.html, renderHtml(r));
262
+ return paths;
263
+ }
264
+ //# sourceMappingURL=report.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"report.js","sourceRoot":"","sources":["../../src/resilience/report.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAC;AAavC,qEAAqE;AACrE,MAAM,CAAC,MAAM,QAAQ,GAAqC;IACxD,eAAe,EAAE;QACf,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,oBAAoB,EAAE;QACzE,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,uCAAuC,EAAE;QAC5F,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,eAAe,EAAE,KAAK,EAAE,uCAAuC,EAAE;QAC9F,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,KAAK,EAAE,gCAAgC,EAAE;KAClF;IACD,mBAAmB,EAAE;QACnB,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,yCAAyC,EAAE;QAC7F,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,mBAAmB,EAAE;QACxE,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,eAAe,EAAE,KAAK,EAAE,oDAAoD,EAAE;QAC3G,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,yBAAyB,EAAE;KAC1E;IACD,UAAU,EAAE;QACV,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,uCAAuC,EAAE;QAC5F,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,eAAe,EAAE,KAAK,EAAE,yCAAyC,EAAE;QAChG,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,KAAK,EAAE,qBAAqB,EAAE;QACtE,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,YAAY,EAAE,KAAK,EAAE,wCAAwC,EAAE;KAC7F;CACF,CAAC;AAEF,MAAM,IAAI,GAA4B,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;AACjG,MAAM,KAAK,GAA4B,EAAE,IAAI,EAAE,GAAG,EAAE,IAAI,EAAE,GAAG,EAAE,IAAI,EAAE,GAAG,EAAE,IAAI,EAAE,GAAG,EAAE,CAAC;AACtF,MAAM,UAAU,GAA4B,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC;AAEnH,qDAAqD;AACrD,MAAM,UAAU,GAAG,CAAC,GAAW;IAC7B,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,GAAG,GAAG,CAAC;QAAE,OAAO,GAAG,CAAC;IACjD,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,GAAG,IAAI,CAAC,CAAC;IACjC,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC;IACxC,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,GAAG,EAAE,CAAC,CAAC;IAC/B,IAAI,CAAC,GAAG,CAAC;QAAE,OAAO,GAAG,CAAC,KAAK,MAAM,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,GAAG,CAAC;IACzD,IAAI,CAAC,GAAG,CAAC;QAAE,OAAO,GAAG,CAAC,KAAK,MAAM,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,GAAG,CAAC;IACzD,OAAO,GAAG,CAAC,GAAG,CAAC;AACjB,CAAC;AAED,iEAAiE;AACjE,MAAM,UAAU,OAAO,CAAC,CAAmB;IACzC,MAAM,KAAK,GAAG,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC;IAC9B,OAAO,IAAI,KAAK,KAAK,CAAC,CAAC,KAAK,MAAM,CAAC,CAAC,OAAO,EAAE,CAAC;AAChD,CAAC;AAED,sDAAsD;AACtD,MAAM,UAAU,SAAS,CAAC,CAAmB;IAC3C,IAAI,MAAM,GAAG,EAAE,CAAC;IAChB,IAAI,CAAC,CAAC,UAAU,KAAK,eAAe,EAAE,CAAC;QACrC,MAAM,MAAM,GAAG,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;QAClE,MAAM,MAAM,GAAG,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,KAAK,MAAM,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;QACtG,MAAM,GAAG,GAAG,MAAM,IAAI,MAAM,iBAAiB,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,CAAC;IACvG,CAAC;SAAM,IAAI,CAAC,CAAC,UAAU,KAAK,mBAAmB,EAAE,CAAC;QAChD,MAAM,GAAG,GAAG,CAAC,CAAC,OAAO,cAAc,CAAC,CAAC,UAAU,cAAc,CAAC;IAChE,CAAC;SAAM,CAAC;QACN,MAAM,GAAG,OAAO,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,UAAU,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC;IACzD,CAAC;IACD,OAAO;QACL,UAAU,EAAE,CAAC,CAAC,UAAU;QACxB,KAAK,EAAE,CAAC,CAAC,KAAK;QACd,OAAO,EAAE,CAAC,CAAC,OAAO;QAClB,UAAU,EAAE,CAAC,CAAC,UAAU;QACxB,MAAM;QACN,QAAQ,EAAE,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE;QACpE,EAAE,EAAE,CAAC,CAAC,EAAE;KACT,CAAC;AACJ,CAAC;AAED,SAAS,SAAS,CAAC,CAAc;IAC/B,MAAM,EAAE,GAAG,CAAC,CAAC,EAAE,KAAK,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC;IACrD,OAAO,KAAK,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,KAAK,QAAQ,CAAC,CAAC,MAAM,GAAG,EAAE,EAAE,CAAC;AACnE,CAAC;AAED,SAAS,aAAa,CAAC,QAAsB;IAC3C,MAAM,IAAI,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,CAAC,SAAS,MAAM,CAAC,CAAC,MAAM,MAAM,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC7F,OAAO,yDAAyD,IAAI,EAAE,CAAC;AACzE,CAAC;AAED,SAAS,cAAc,CAAC,CAAmB;IACzC,IAAI,CAAC,CAAC,CAAC,SAAS;QAAE,OAAO,aAAa,CAAC;IACvC,MAAM,CAAC,GAAG,CAAC,CAAC,SAAS,CAAC;IACtB,OAAO;QACL,oBAAoB,CAAC,CAAC,SAAS,EAAE;QACjC,oBAAoB,CAAC,CAAC,QAAQ,EAAE;QAChC,4BAA4B,CAAC,CAAC,MAAM,IAAI;QACxC,+BAA+B,CAAC,CAAC,SAAS,IAAI;QAC9C,4BAA4B,CAAC,CAAC,cAAc,IAAI;QAChD,EAAE;QACF,6CAA6C;QAC7C,EAAE;QACF,KAAK;QACL,CAAC,CAAC,YAAY;QACd,KAAK;KACN,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACf,CAAC;AAED,uDAAuD;AACvD,MAAM,UAAU,cAAc,CAAC,CAAmB;IAChD,MAAM,IAAI,GAAG;QACX,KAAK,CAAC,CAAC,KAAK,EAAE;QACd,EAAE;QACF,gBAAgB,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,yBAAyB,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,kBAAkB,CAAC,CAAC,CAAC,EAAE,EAAE;QACnG,EAAE;QACF,kBAAkB,CAAC,CAAC,SAAS,EAAE;QAC/B,mBAAmB,CAAC,CAAC,UAAU,EAAE;QACjC,mBAAmB,GAAG,CAAC,CAAC,CAAC,WAAW,CAAC,EAAE;QACvC,kBAAkB,CAAC,CAAC,OAAO,EAAE;QAC7B,EAAE;KACH,CAAC;IAEF,MAAM,IAAI,GAAa,EAAE,CAAC;IAC1B,IAAI,CAAC,CAAC,UAAU,KAAK,eAAe,EAAE,CAAC;QACrC,MAAM,GAAG,GAAG,CAAuB,CAAC;QACpC,IAAI,CAAC,IAAI,CAAC,qBAAqB,EAAE,EAAE,EAAE,2DAA2D,GAAG,CAAC,cAAc,iCAAiC,EAAE,EAAE,CAAC,CAAC;QACzJ,KAAK,MAAM,EAAE,IAAI,GAAG,CAAC,KAAK,EAAE,CAAC;YAC3B,IAAI,CAAC,IAAI,CACP,OAAO,KAAK,CAAC,EAAE,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,IAAI,UAAU,EAAE,CAAC,IAAI,GAAG,EACvD,EAAE,EACF,gBAAgB,EAAE,CAAC,OAAO,WAAW,GAAG,CAAC,EAAE,CAAC,aAAa,CAAC,GAAG,EAC7D,gCAAgC,EAAE,CAAC,aAAa,SAAS,GAAG,CAAC,EAAE,CAAC,UAAU,CAAC,mBAAmB,EAC9F,cAAc,EAAE,CAAC,SAAS,CAAC,CAAC,CAAC,6BAA6B,CAAC,CAAC,CAAC,+BAA+B,EAAE,EAC9F,EAAE,EACF,GAAG,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,SAAS,CAAC,EAC3B,EAAE,CACH,CAAC;QACJ,CAAC;IACH,CAAC;SAAM,IAAI,CAAC,CAAC,UAAU,KAAK,mBAAmB,EAAE,CAAC;QAChD,MAAM,GAAG,GAAG,CAAgB,CAAC;QAC7B,IAAI,CAAC,IAAI,CACP,cAAc,EACd,EAAE,EACF,uBAAuB,GAAG,CAAC,MAAM,IAAI,SAAS,iJAAiJ,EAC/L,EAAE,EACF,0DAA0D,EAC1D,uCAAuC,EACvC,GAAG,GAAG,CAAC,KAAK,CAAC,GAAG,CACd,CAAC,CAAC,EAAE,EAAE,CACJ,KAAK,CAAC,CAAC,KAAK,MAAM,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,IAAI,SAAS,CAAC,CAAC,QAAQ,QAAQ,CAAC,CAAC,OAAO,MAAM,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,MAAM,MAAM,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,CAClL,EACD,EAAE,EACF,KAAK,GAAG,CAAC,OAAO,aAAa,GAAG,CAAC,UAAU,+BAA+B,EAC1E,EAAE,CACH,CAAC;IACJ,CAAC;SAAM,CAAC;QACN,MAAM,GAAG,GAAG,CAAa,CAAC;QAC1B,IAAI,CAAC,IAAI,CACP,4BAA4B,EAC5B,EAAE,EACF,gBAAgB,GAAG,CAAC,OAAO,IAAI,EAC/B,oBAAoB,GAAG,CAAC,WAAW,uBAAuB,EAC1D,qBAAqB,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,eAAe,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,EACtE,EAAE,EACF,2CAA2C,EAC3C,iCAAiC,EACjC,GAAG,GAAG,CAAC,KAAK,CAAC,GAAG,CACd,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,CAAC,KAAK,MAAM,CAAC,CAAC,MAAM,MAAM,CAAC,CAAC,MAAM,MAAM,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,MAAM,IAAI,CAC3G,EACD,EAAE,CACH,CAAC;IACJ,CAAC;IAED,MAAM,IAAI,GAAG;QACX,uBAAuB;QACvB,EAAE;QACF,gDAAgD;QAChD,EAAE;QACF,aAAa,CAAC,CAAC,CAAC,QAAQ,CAAC;QACzB,EAAE;QACF,cAAc;QACd,EAAE;QACF,cAAc,CAAC,CAAC,CAAC;QACjB,EAAE;QACF,KAAK;QACL,EAAE;QACF,kBAAkB,KAAK,CAAC,OAAO,QAAQ,KAAK,CAAC,MAAM,MAAM,KAAK,CAAC,GAAG,EAAE;QACpE,wBAAwB,KAAK,CAAC,MAAM,EAAE;QACtC,EAAE;KACH,CAAC;IAEF,OAAO,CAAC,GAAG,IAAI,EAAE,GAAG,IAAI,EAAE,GAAG,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAChD,CAAC;AAED,MAAM,GAAG,GAAG,CAAC,CAAS,EAAU,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,CAAC,CAAC,CAAY,CAAA,CAAC,CAAC;AAEzH,wDAAwD;AACxD,MAAM,UAAU,UAAU,CAAC,CAAmB;IAC5C,MAAM,MAAM,GAAG,SAAS,CAAC;IACzB,MAAM,IAAI,GAAG,CAAC,CAAU,EAAE,EAAE,CAC1B,wCAAwC,UAAU,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC;IAE7E,MAAM,MAAM,GAAa,EAAE,CAAC;IAC5B,IAAI,CAAC,CAAC,UAAU,KAAK,eAAe,EAAE,CAAC;QACrC,MAAM,GAAG,GAAG,CAAuB,CAAC;QACpC,MAAM,CAAC,IAAI,CACT,uEAAuE,GAAG,CAAC,GAAG,CAAC,cAAc,CAAC,0CAA0C,CACzI,CAAC;QACF,KAAK,MAAM,EAAE,IAAI,GAAG,CAAC,KAAK,EAAE,CAAC;YAC3B,MAAM,CAAC,IAAI,CACT,yBAAyB,IAAI,CAAC,EAAE,CAAC,OAAO,CAAC,IAAI,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,6BAA6B,EAAE,CAAC,IAAI,cAAc,EAC3G,kCAAkC,GAAG,CAAC,EAAE,CAAC,OAAO,CAAC,iBAAiB,GAAG,CAAC,EAAE,CAAC,aAAa,CAAC,uBAAuB,EAAE,CAAC,aAAa,eAAe,GAAG,CAAC,EAAE,CAAC,UAAU,CAAC,qBAAqB,EAAE,CAAC,SAAS,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,iCAAiC,MAAM,EACzP,yBAAyB,EAAE,CAAC,MAAM;iBAC/B,GAAG,CACF,CAAC,CAAC,EAAE,EAAE,CACJ,kCAAkC,UAAU,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,YAAY,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,0BAA0B,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,0BAA0B,CAAC,CAAC,EAAE,KAAK,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,GAAG,KAAK,CAAC,CAAC,CAAC,EAAE,YAAY,CAC1N;iBACA,IAAI,CAAC,EAAE,CAAC,gBAAgB,CAC5B,CAAC;QACJ,CAAC;IACH,CAAC;SAAM,IAAI,CAAC,CAAC,UAAU,KAAK,mBAAmB,EAAE,CAAC;QAChD,MAAM,GAAG,GAAG,CAAgB,CAAC;QAC7B,MAAM,CAAC,IAAI,CACT,wCAAwC,GAAG,CAAC,GAAG,CAAC,MAAM,IAAI,SAAS,CAAC,wFAAwF,EAC5J,0HAA0H,EAC1H,GAAG,GAAG,CAAC,KAAK,CAAC,GAAG,CACd,CAAC,CAAC,EAAE,EAAE,CACJ,WAAW,CAAC,CAAC,KAAK,YAAY,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,wBAAwB,CAAC,CAAC,IAAI,yBAAyB,GAAG,CAAC,CAAC,CAAC,QAAQ,CAAC,mBAAmB,CAAC,CAAC,OAAO,yBAAyB,UAAU,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,KAAK,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,uCAAuC,GAAG,UAAU,CAAC,IAAI,GAAG,sBAAsB,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,YAAY,CAClY,EACD,sBAAsB,GAAG,CAAC,OAAO,aAAa,GAAG,CAAC,UAAU,0CAA0C,CACvG,CAAC;IACJ,CAAC;SAAM,CAAC;QACN,MAAM,GAAG,GAAG,CAAa,CAAC;QAC1B,MAAM,CAAC,IAAI,CACT,oCAAoC,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,mCAAmC,GAAG,CAAC,GAAG,CAAC,WAAW,CAAC,2BAA2B,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,2BAA2B,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,eAAe,EAC9M,qGAAqG,EACrG,GAAG,GAAG,CAAC,KAAK,CAAC,GAAG,CACd,CAAC,CAAC,EAAE,EAAE,CACJ,WAAW,CAAC,CAAC,KAAK,YAAY,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,YAAY,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,yBAAyB,UAAU,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,0BAA0B,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,YAAY,CAChN,EACD,UAAU,CACX,CAAC;IACJ,CAAC;IAED,MAAM,QAAQ,GAAG,CAAC,CAAC,QAAQ;SACxB,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,mBAAmB,GAAG,CAAC,CAAC,CAAC,SAAS,CAAC,qBAAqB,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,YAAY,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,YAAY,CAAC;SACrH,IAAI,CAAC,EAAE,CAAC,CAAC;IAEZ,MAAM,GAAG,GAAG,CAAC,CAAC,SAAS,CAAC;IACxB,MAAM,OAAO,GAAG,GAAG;QACjB,CAAC,CAAC;qCAC+B,GAAG,CAAC,GAAG,CAAC,SAAS,CAAC;qCAClB,GAAG,CAAC,GAAG,CAAC,QAAQ,CAAC;iDACL,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC;wDACR,GAAG,CAAC,GAAG,CAAC,SAAS,CAAC;iDACzB,GAAG,CAAC,GAAG,CAAC,cAAc,CAAC;;qEAEH,GAAG,CAAC,GAAG,CAAC,YAAY,CAAC,kBAAkB;QACxG,CAAC,CAAC,gCAAgC,CAAC;IAErC,OAAO;SACA,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC;;mBAEF,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;UAkCf,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC;WACX,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,wCAAwC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC,EAAE;;gEAE1C,GAAG,CAAC,CAAC,CAAC,SAAS,CAAC,OAAO,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;iEAC9C,GAAG,CAAC,CAAC,CAAC,UAAU,CAAC,OAAO,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;0CACvE,GAAG,CAAC,CAAC,CAAC,WAAW,CAAC;sDACN,UAAU,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC;;;;SAItF,GAAG,CAAC,CAAC,CAAC,OAAO,CAAC;;MAEjB,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC;;mFAE4D,QAAQ;;MAErF,OAAO;;;yBAGY,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC;qBAC7C,KAAK,CAAC,GAAG,KAAK,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,mBAAmB,KAAK,CAAC,MAAM;;qBAE3D,CAAC;AACtB,CAAC;AASD,gFAAgF;AAChF,MAAM,UAAU,YAAY,CAAC,CAAmB,EAAE,MAAc;IAC9D,SAAS,CAAC,MAAM,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACvC,MAAM,IAAI,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC;IAChC,MAAM,KAAK,GAAgB,EAAE,IAAI,EAAE,GAAG,IAAI,OAAO,EAAE,QAAQ,EAAE,GAAG,IAAI,KAAK,EAAE,IAAI,EAAE,GAAG,IAAI,OAAO,EAAE,CAAC;IAClG,aAAa,CAAC,KAAK,CAAC,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;IACtD,aAAa,CAAC,KAAK,CAAC,QAAQ,EAAE,cAAc,CAAC,CAAC,CAAC,CAAC,CAAC;IACjD,aAAa,CAAC,KAAK,CAAC,IAAI,EAAE,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC;IACzC,OAAO,KAAK,CAAC;AACf,CAAC"}
@@ -0,0 +1,42 @@
1
+ /**
2
+ * DR runbook parser (declarative YAML, dependency-free).
3
+ *
4
+ * Disaster-recovery drills are driven by a small, declarative runbook so the
5
+ * plan lives in version control next to the infrastructure — not in a Word
6
+ * document from 2019 that nobody has ever executed. This parses the safe subset
7
+ * of YAML the runbook schema needs (top-level scalars + a `steps:` sequence of
8
+ * mappings); a JSON runbook is accepted too.
9
+ *
10
+ * Part of Proxmox MCP Server.
11
+ * Crafted by SoyRage Agency — https://soyrage.es/
12
+ * MIT licensed (see LICENSE).
13
+ */
14
+ /** A single declarative recovery step. */
15
+ export interface RunbookStep {
16
+ /** restore | start | healthcheck | verify-file | failover | notify | teardown | wait */
17
+ action: string;
18
+ guest?: string;
19
+ /** Backup selector for `restore` (e.g. "latest"). */
20
+ from?: string;
21
+ /** Health check kind for `healthcheck` (service | db | http). */
22
+ check?: string;
23
+ /** File path for `verify-file`. */
24
+ path?: string;
25
+ /** Free-form note surfaced in the drill minutes. */
26
+ note?: string;
27
+ }
28
+ /** A parsed, validated recovery runbook. */
29
+ export interface Runbook {
30
+ name: string;
31
+ /** Target environment — the drill refuses to touch anything named "production". */
32
+ environment: string;
33
+ description: string;
34
+ /** Recovery Point Objective the drill asserts (hours). */
35
+ rpoHours: number;
36
+ steps: RunbookStep[];
37
+ }
38
+ /**
39
+ * Parse a declarative runbook from YAML (subset) or JSON text. Throws a clear
40
+ * error if the document is malformed or references an unknown action.
41
+ */
42
+ export declare function parseRunbook(text: string): Runbook;
@@ -0,0 +1,165 @@
1
+ /**
2
+ * DR runbook parser (declarative YAML, dependency-free).
3
+ *
4
+ * Disaster-recovery drills are driven by a small, declarative runbook so the
5
+ * plan lives in version control next to the infrastructure — not in a Word
6
+ * document from 2019 that nobody has ever executed. This parses the safe subset
7
+ * of YAML the runbook schema needs (top-level scalars + a `steps:` sequence of
8
+ * mappings); a JSON runbook is accepted too.
9
+ *
10
+ * Part of Proxmox MCP Server.
11
+ * Crafted by SoyRage Agency — https://soyrage.es/
12
+ * MIT licensed (see LICENSE).
13
+ */
14
+ const KNOWN_ACTIONS = new Set([
15
+ "restore",
16
+ "start",
17
+ "healthcheck",
18
+ "verify-file",
19
+ "failover",
20
+ "notify",
21
+ "teardown",
22
+ "wait",
23
+ ]);
24
+ /** Strip surrounding quotes and unescape a scalar. */
25
+ function scalar(raw) {
26
+ const v = raw.trim();
27
+ if ((v.startsWith('"') && v.endsWith('"')) || (v.startsWith("'") && v.endsWith("'"))) {
28
+ return v.slice(1, -1);
29
+ }
30
+ return v;
31
+ }
32
+ /** Indentation width (spaces) of a line. */
33
+ function indentOf(line) {
34
+ return line.length - line.trimStart().length;
35
+ }
36
+ /** Remove a trailing ` # comment`, respecting quoted strings. Keeps indentation. */
37
+ function stripInlineComment(line) {
38
+ let quote = null;
39
+ for (let i = 0; i < line.length; i++) {
40
+ const c = line[i];
41
+ if (quote) {
42
+ if (c === quote)
43
+ quote = null;
44
+ }
45
+ else if (c === '"' || c === "'") {
46
+ quote = c;
47
+ }
48
+ else if (c === "#" && (i === 0 || line[i - 1] === " " || line[i - 1] === "\t")) {
49
+ return line.slice(0, i).replace(/\s+$/, "");
50
+ }
51
+ }
52
+ return line;
53
+ }
54
+ /**
55
+ * Parse a declarative runbook from YAML (subset) or JSON text. Throws a clear
56
+ * error if the document is malformed or references an unknown action.
57
+ */
58
+ export function parseRunbook(text) {
59
+ const trimmed = text.trim();
60
+ const raw = trimmed.startsWith("{")
61
+ ? JSON.parse(trimmed)
62
+ : parseYaml(trimmed);
63
+ const name = String(raw.name ?? "").trim();
64
+ if (!name)
65
+ throw new Error("Runbook is missing a `name`.");
66
+ const environment = String(raw.environment ?? "staging").trim();
67
+ if (/prod/i.test(environment)) {
68
+ throw new Error(`Runbook environment "${environment}" looks like production. DR drills must ` +
69
+ "target an isolated test environment — refusing to run.");
70
+ }
71
+ const stepsRaw = Array.isArray(raw.steps) ? raw.steps : [];
72
+ if (stepsRaw.length === 0)
73
+ throw new Error("Runbook has no `steps`.");
74
+ const steps = stepsRaw.map((s, i) => {
75
+ const action = String(s.action ?? "").trim();
76
+ if (!KNOWN_ACTIONS.has(action)) {
77
+ throw new Error(`Step ${i + 1} has unknown action "${action}". Known actions: ${[...KNOWN_ACTIONS].join(", ")}.`);
78
+ }
79
+ return {
80
+ action,
81
+ guest: s.guest !== undefined ? String(s.guest) : undefined,
82
+ from: s.from !== undefined ? String(s.from) : undefined,
83
+ check: s.check !== undefined ? String(s.check) : undefined,
84
+ path: s.path !== undefined ? String(s.path) : undefined,
85
+ note: s.note !== undefined ? String(s.note) : undefined,
86
+ };
87
+ });
88
+ return {
89
+ name,
90
+ environment,
91
+ description: String(raw.description ?? "").trim(),
92
+ rpoHours: Number(raw.rpoHours ?? 24),
93
+ steps,
94
+ };
95
+ }
96
+ /**
97
+ * Minimal YAML reader for the runbook schema: top-level `key: value` scalars
98
+ * and a single `steps:` block that is a sequence of mappings. Deliberately not
99
+ * a general YAML parser — it covers exactly what a runbook needs.
100
+ */
101
+ function parseYaml(text) {
102
+ const lines = text
103
+ .split(/\r?\n/)
104
+ .map((l) => stripInlineComment(l.replace(/\t/g, " ")))
105
+ .filter((l) => l.trim() !== "" && !l.trim().startsWith("#"));
106
+ const root = {};
107
+ let i = 0;
108
+ while (i < lines.length) {
109
+ const line = lines[i];
110
+ if (indentOf(line) !== 0) {
111
+ throw new Error(`Unexpected indentation at: "${line.trim()}"`);
112
+ }
113
+ const colon = line.indexOf(":");
114
+ if (colon === -1)
115
+ throw new Error(`Expected "key: value" at: "${line.trim()}"`);
116
+ const key = line.slice(0, colon).trim();
117
+ const rest = line.slice(colon + 1).trim();
118
+ if (rest === "" && key === "steps") {
119
+ const [items, next] = parseSequence(lines, i + 1);
120
+ root[key] = items;
121
+ i = next;
122
+ continue;
123
+ }
124
+ root[key] = rest === "" ? "" : scalar(rest);
125
+ i += 1;
126
+ }
127
+ return root;
128
+ }
129
+ /** Parse a sequence of mappings starting at `start`; returns [items, nextIndex]. */
130
+ function parseSequence(lines, start) {
131
+ const items = [];
132
+ let i = start;
133
+ let itemIndent = -1;
134
+ let current = null;
135
+ while (i < lines.length) {
136
+ const line = lines[i];
137
+ const indent = indentOf(line);
138
+ if (indent === 0)
139
+ break; // back to top level
140
+ const trimmed = line.trim();
141
+ if (trimmed.startsWith("- ")) {
142
+ if (itemIndent === -1)
143
+ itemIndent = indent;
144
+ if (indent !== itemIndent)
145
+ break;
146
+ current = {};
147
+ items.push(current);
148
+ const inline = trimmed.slice(2).trim();
149
+ const colon = inline.indexOf(":");
150
+ if (colon !== -1)
151
+ current[inline.slice(0, colon).trim()] = scalar(inline.slice(colon + 1));
152
+ }
153
+ else if (current && indent > itemIndent) {
154
+ const colon = trimmed.indexOf(":");
155
+ if (colon !== -1)
156
+ current[trimmed.slice(0, colon).trim()] = scalar(trimmed.slice(colon + 1));
157
+ }
158
+ else {
159
+ break;
160
+ }
161
+ i += 1;
162
+ }
163
+ return [items, i];
164
+ }
165
+ //# sourceMappingURL=runbook.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runbook.js","sourceRoot":"","sources":["../../src/resilience/runbook.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Evidence signing (Ed25519, zero external dependencies).
3
+ *
4
+ * Compliance evidence is only useful if it can be trusted. Every resilience
5
+ * report is signed with an Ed25519 key so an auditor can verify — offline, with
6
+ * only the bundled public key — that the report was produced by this system on
7
+ * the stated date and has not been altered since.
8
+ *
9
+ * The private key lives on disk (generated on first use, or supplied via
10
+ * PROXMOX_MCP_SIGNING_KEY). Reports embed the public key + its fingerprint, so
11
+ * verification never needs the secret.
12
+ *
13
+ * Part of Proxmox MCP Server.
14
+ * Crafted by SoyRage Agency — https://soyrage.es/
15
+ * MIT licensed (see LICENSE).
16
+ */
17
+ import { type KeyObject } from "node:crypto";
18
+ import type { Signature } from "./types.js";
19
+ /** Deterministically serialise a value so the digest is stable across runs. */
20
+ export declare function canonical(value: unknown): string;
21
+ /** SHA-256 hex digest of a string. */
22
+ export declare function sha256(text: string): string;
23
+ /**
24
+ * Load the signing key from `keyPath`, generating and persisting a fresh
25
+ * Ed25519 keypair there on first use. Returns both key objects.
26
+ */
27
+ export declare function loadSigningKey(keyPath: string): {
28
+ priv: KeyObject;
29
+ pub: KeyObject;
30
+ };
31
+ /** Public-key fingerprint: SHA-256 of the DER SPKI, hex, colon-grouped short form. */
32
+ export declare function fingerprint(pub: KeyObject): string;
33
+ /**
34
+ * Sign a report payload. The signature covers the SHA-256 digest of the
35
+ * canonical (key-sorted) JSON of `payload` — i.e. the whole report minus the
36
+ * signature block itself.
37
+ */
38
+ export declare function signPayload(payload: unknown, keyPath: string, signedAt: string): Signature;
39
+ /**
40
+ * Verify a report's signature against its embedded public key. Returns true
41
+ * only if the digest matches the payload AND the signature matches the digest.
42
+ */
43
+ export declare function verifySignature(payload: unknown, sig: Signature): boolean;