@love-moon/conductor-cli 0.7.6 → 0.7.7

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.
@@ -23,6 +23,49 @@ export function maskHandoffUrlForLogs(value) {
23
23
  });
24
24
  }
25
25
 
26
+ // Secrets other than the handoff token can reach the same log/summary surface
27
+ // once we start capturing a crashing child's output: the daemon puts
28
+ // `CONDUCTOR_AGENT_TOKEN` into the fire's environment AND into tmux's argv
29
+ // (`-e CONDUCTOR_AGENT_TOKEN=…`), and the fire inherits provider keys such as
30
+ // ANTHROPIC_API_KEY / OPENAI_API_KEY. Backends routinely echo those back when
31
+ // they fail ("invalid API key sk-ant-…", usage dumps, env dumps).
32
+ //
33
+ // A single-pattern denylist is the wrong shape for that, so this is a
34
+ // redaction *pass*: exact known secret values first (the strongest signal —
35
+ // the daemon holds its own token at runtime), then structural patterns.
36
+ const SECRET_ASSIGNMENT_RE =
37
+ /\b([A-Z0-9_]*(?:TOKEN|SECRET|KEY|PASSWORD|PASSWD|CREDENTIAL)[A-Z0-9_]*)\s*[=:]\s*("[^"]*"|'[^']*'|\S+)/gi;
38
+ const BEARER_RE = /\b(Bearer|Basic)\s+([A-Za-z0-9._~+/=-]{8,})/gi;
39
+ // Provider key shapes (OpenAI/Anthropic `sk-…`, Google `AIza…`, GitHub `ghp_…`).
40
+ const PROVIDER_KEY_RE = /\b(sk-[A-Za-z0-9_-]{12,}|AIza[A-Za-z0-9_-]{20,}|gh[pousr]_[A-Za-z0-9]{16,})\b/g;
41
+
42
+ const escapeRegExp = (value) => value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
43
+
44
+ /**
45
+ * Redact secrets from text that is about to be logged or persisted into a task
46
+ * status summary.
47
+ *
48
+ * @param {string} value
49
+ * @param {string[]} [knownSecrets] exact literal values to strip (e.g. the
50
+ * daemon's own AGENT_TOKEN). Short values are ignored so a 1-2 char token
51
+ * cannot blank out the whole message.
52
+ */
53
+ export function redactSecretsForLogs(value, knownSecrets = []) {
54
+ if (typeof value !== "string" || !value) {
55
+ return value;
56
+ }
57
+ let out = maskHandoffUrlForLogs(value);
58
+ for (const secret of knownSecrets) {
59
+ if (typeof secret === "string" && secret.length >= 8) {
60
+ out = out.replace(new RegExp(escapeRegExp(secret), "g"), "<redacted>");
61
+ }
62
+ }
63
+ out = out.replace(SECRET_ASSIGNMENT_RE, (_, key) => `${key}=<redacted>`);
64
+ out = out.replace(BEARER_RE, (_, scheme) => `${scheme} <redacted>`);
65
+ out = out.replace(PROVIDER_KEY_RE, "<redacted>");
66
+ return out;
67
+ }
68
+
26
69
  // Scrub any handoff URL embedded in an error message before surfacing it via
27
70
  // logs or task status summaries. Belt-and-suspenders for the case where an
28
71
  // internal error stringifies the outbox payload.