// The testable core of the operator-run remediation. The mapping from existing // resource name to owning account is operator-authored, and validation fails // closed on any resource absent from it: ownership is a recorded fact, never a // name-prefix guess. Token stripping removes every account-wide credential from // a sub-account secrets file while preserving everything else verbatim. export interface MappingResult { ok: boolean; missing: string[]; } // Account-wide credentials stripped from a sub-account's cloudflare.env. // CLOUDFLARE_ACCOUNT_ID is intentionally kept: it is an identifier, not a // credential, and does not grant access on its own. // Account-wide-reach tokens that must never persist in a sub-account secrets // file: the master and every scoped DATA token that reaches the whole shared // account. CF_STORAGE_TOKEN (D1+R2) and CF_CALENDAR_D1_TOKEN (D1) are house-code // data scopes (Task 1670) minted account-scoped, so a copy in a sub-account file // would let that tenant reach every other tenant's D1/R2 — strip them too. The // account's own zone/pages hosting tokens (CF_DNS_TOKEN, etc.) are narrow and // stay. const STRIP_KEYS = [ "CLOUDFLARE_API_TOKEN", "CF_PAGES_D1_TOKEN", "CF_PAGES_TOKEN", "CF_STORAGE_TOKEN", "CF_CALENDAR_D1_TOKEN", ]; export function validateMapping( enumerated: string[], mapping: Record, ): MappingResult { const missing = enumerated.filter((name) => !(name in mapping)); return { ok: missing.length === 0, missing }; } export function stripAccountWideTokens(contents: string): string { const kept = contents.split("\n").filter((line) => { const key = line.match(/^\s*([A-Za-z_][A-Za-z0-9_]*)=/)?.[1]; return !(key && STRIP_KEYS.includes(key)); }); return kept.join("\n"); }