Pre-release audit fixes
Security and correctness pass ahead of making the repo public. - Address generation now uses crypto.getRandomValues with rejection sampling instead of Math.random. Users can mint addresses and observe the output, and V8's PRNG state is recoverable from observed values, so one user could predict another's addresses. - Write the U+E000/U+E001 link-placeholder sentinels and the invisible character class as \u escapes. As literal characters they were invisible in review and rendered as empty strings; anything stripping them would have silently turned the final restore pass into inserting "<" between every character of every message. - Assemble the DM body once and cap once. Notes were appended after the 2000-char check, so a long email with oversized attachments produced a 2038-char message that Discord rejected outright, losing the delivery. - Budget the whole multipart payload rather than just the mail's own attachments, and take attachments individually. Generated message.txt/ .html were not counted, and a single oversized file discarded every attachment alongside it. - Bound HTML at 256KB before conversion. Parsing is quadratic in input size and email bodies are attacker-controlled; a crafted ~1MB body cost 13s of CPU, now 0.9s worst case. - Rate limit /list, /extend and /torch, not just /new. - Log delivery failures (adapter type and error only, never content, addresses or owner ids) so a user with DMs closed is diagnosable instead of silently dropping mail. - Track revoked_at so revoked addresses are cleaned up a grace period after revocation rather than surviving until their original expiry, and prune rate_limit rows whose window closed long ago. Includes a migration for existing deployments. - Return 400 rather than throwing on a malformed interaction payload. - package.json declared ISC while LICENSE is MIT; also replaces the scaffolding defaults with real metadata and scripts.
9 files changed+173−56
Rendering diff…