Files
clawhub/scripts/lib/workerRedaction.ts
Jesse Merhi 2deb1b75e9 Harden worker secret logging (#2784)
* fix: redact scan worker artifact errors

* fix: harden worker secret logging

* fix: keep worker claim failures failing

* fix: scan diagnostics as files

* fix: pin diagnostics scanner image

* fix: narrow worker redaction boundary

* fix: avoid custom worker secret patterns

* fix: centralize worker transport redaction

* fix: redact persisted worker failure secrets

* fix: fail security worker on claim outages

* fix: redact structured diagnostic secret fields

* fix: harden worker failure redaction boundaries

* fix: cover bracketed worker secret values

* test: enforce worker workflow secret references

* fix: redact quoted worker secret values

* fix: redact diagnostic artifact path labels

* test: cover claim failure redaction

* fix: simplify worker redaction boundary

* test: prove worker logger emits structured events
2026-06-25 11:47:39 +10:00

61 lines
2.0 KiB
TypeScript

import { redactWorkerPublicText as redactSharedWorkerPublicText } from "../../convex/lib/workerTextRedaction";
const DEFAULT_MAX_TEXT_CHARS = 20_000;
export function redactWorkerPublicText(value: string, maxChars = DEFAULT_MAX_TEXT_CHARS) {
const redacted = redactSharedWorkerPublicText(value);
if (redacted.length <= maxChars) return redacted;
return `${redacted.slice(0, maxChars)}\n...[truncated ${redacted.length - maxChars} chars]`;
}
export function redactWorkerPublicErrorMessage(value: string) {
return redactWorkerPublicText(value);
}
export function safeWorkerArtifactPathLabel(value: string) {
const normalized = value.replace(/[\r\n]+/g, " ").trim();
const parts = normalized.split("/");
const redacted = redactSharedWorkerPublicText(normalized);
const isSafe =
normalized.length > 0 &&
normalized.length <= 240 &&
redacted === normalized &&
!normalized.startsWith("/") &&
!parts.includes("..") &&
/^[A-Za-z0-9._/-]+$/.test(normalized);
return isSafe ? normalized : "[redacted-path]";
}
function escapeGitHubActionsCommandValue(value: string) {
return value.replace(/%/g, "%25").replace(/\r/g, "%0D").replace(/\n/g, "%0A");
}
export function maskGitHubActionsSecret(
value: string | null | undefined,
options?: { env?: NodeJS.ProcessEnv; write?: (line: string) => void },
) {
if (!value) return false;
const env = options?.env ?? process.env;
if (env.GITHUB_ACTIONS !== "true") return false;
const write = options?.write ?? ((line: string) => process.stdout.write(line));
write(`::add-mask::${escapeGitHubActionsCommandValue(value)}\n`);
return true;
}
export function maskKnownWorkerSecrets(
env: NodeJS.ProcessEnv = process.env,
write?: (line: string) => void,
) {
const secretKeys = [
"SECURITY_SCAN_WORKER_TOKEN",
"OPENAI_API_KEY",
"GH_TOKEN",
"GITHUB_TOKEN",
"CONVEX_DEPLOY_KEY",
"HOMEBREW_GITHUB_API_TOKEN",
];
for (const key of secretKeys) {
maskGitHubActionsSecret(env[key], { env, write });
}
}