[object Object]

← back to Cncp Mcp

security: cncp-refresh keeps newest 5 backups, deletes the rest

ae1c6b77505f64b31e149f816321189fc4ca0638 · 2026-05-11 10:05:26 -0700 · Steve Abrams

Backups can contain real API keys (ELEVENLABS_API_KEY surfaced in security
audit 2026-05-11). Unbounded hourly rotation was a slow-growing liability.
Pre-emptively trimmed disk to last 5 in same pass.

Files touched

Diff

commit ae1c6b77505f64b31e149f816321189fc4ca0638
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Mon May 11 10:05:26 2026 -0700

    security: cncp-refresh keeps newest 5 backups, deletes the rest
    
    Backups can contain real API keys (ELEVENLABS_API_KEY surfaced in security
    audit 2026-05-11). Unbounded hourly rotation was a slow-growing liability.
    Pre-emptively trimmed disk to last 5 in same pass.
---
 scripts/cncp-refresh.mjs | 18 +++++++++++++++++-
 1 file changed, 17 insertions(+), 1 deletion(-)

diff --git a/scripts/cncp-refresh.mjs b/scripts/cncp-refresh.mjs
index 071427b..cfb3eae 100755
--- a/scripts/cncp-refresh.mjs
+++ b/scripts/cncp-refresh.mjs
@@ -28,7 +28,7 @@
 
 import { promises as fs } from "node:fs";
 import { homedir } from "node:os";
-import { join } from "node:path";
+import { join, dirname, basename } from "node:path";
 
 const CNCP_DIR = process.env.CNCP_DIR || join(homedir(), "cncp-starter");
 const CONFIG = join(CNCP_DIR, "cncp-config.json");
@@ -64,6 +64,22 @@ async function saveConfig(cfg) {
   const tmp = `${CONFIG}.tmp.${process.pid}.${Date.now()}`;
   await fs.writeFile(tmp, JSON.stringify(cfg, null, 2) + "\n", "utf-8");
   await fs.rename(tmp, CONFIG);
+  // Retention: keep newest 5 backups, delete rest. Backups can contain real
+  // API keys (e.g. ELEVENLABS_API_KEY in cncp-config.json), so unbounded
+  // rotation is a slow-growing security liability.
+  try {
+    const dir = dirname(CONFIG);
+    const base = basename(CONFIG);
+    const entries = await fs.readdir(dir);
+    const baks = entries
+      .filter(n => n.startsWith(`${base}.bak.`))
+      .map(n => ({ n, ts: Number(n.split(".bak.")[1]) || 0 }))
+      .sort((a, b) => b.ts - a.ts)
+      .slice(5);
+    for (const { n } of baks) {
+      try { await fs.unlink(join(dir, n)); } catch {}
+    }
+  } catch {}
 }
 
 // ─── --status-only mode ──────────────────────────────────────────────────

← c65b01b security: apply-pm2-caps now uses mktemp + trap-unlink for p  ·  back to Cncp Mcp  ·  feat: cncp-reconcile-projects.mjs — keep cncp-projects.json 45874a8 →