Skip to content

Latest commit

 

History

History
12 lines (11 loc) · 3.08 KB

File metadata and controls

12 lines (11 loc) · 3.08 KB
title Operational notes
description Implementation notes for key hashing, disabled-key behavior, and worker mode.
  • API keys are stored as peppered HMAC-SHA-256 hashes. Keys are random 192-bit keys with the mantis_live_ prefix (matches GitHub's secret-scanning format, so a leaked key in a public repo will trigger their alerting). MANTIS_API_KEY_PEPPER is required for new mints; legacy pre-pepper SHA-256 rows are still accepted and are upgraded opportunistically on next use. Dashboard cookies carry opaque mantis_sess_... tokens; the database stores only a SHA-256 hash of each session token.
  • Disabled or expired keys still return the configured response — they just don't record a hit or fire notifications. This prevents an attacker from probing for valid key IDs by looking for differential responses.
  • Notifications use a Postgres-backed queue. Public trigger requests enqueue notification rows after the response path; a worker claims pending rows with FOR UPDATE SKIP LOCKED, sends them, and retries failures with backoff. The worker starts automatically on non-Vercel deployments. On Vercel or other serverless hosts, use /api/cron/notifications with CRON_SECRET.
  • Operator secrets can be encrypted at rest. Set MANTIS_SECRET_KEY (AES-256-GCM, openssl rand -base64 32) to envelope-encrypt webhook HMAC signing secrets and the Apple Wallet auth secret + cert passphrase in the database, so a DB-only leak yields no usable secrets. Unset = today's plaintext-at-rest behavior; existing rows are read transparently and migrate to ciphertext on the next rotate/re-save. See Configuration.
  • API-key auth failures are rate-limited. Repeated failed bearer auth is throttled per client IP via a Postgres-backed fixed-window limiter (60/min) and returns 429 instead of 401 to blunt brute force; a successful auth never touches the limiter. The dashboard login action applies the same per-IP throttle to failed logins only (10/min), and only when a trusted client IP is available — so a flood of bad attempts can never lock out a valid key or the operator. The shared rate_limits table makes the cap hold across instances and serverless cold starts; the limiter fails open on a DB error.
  • Alert payloads escape attacker-controlled values. The hit IP, User-Agent, key memo, and the host_context parsed from X-Mantis-* headers are escaped before they're interpolated into Slack / Discord / Teams messages, so a canary tripper can't inject phishing links, channel mentions, or spoofed formatting into your alerts. The edge worker carries the same escapers and additionally drops the request query string from alert links.
  • Outbound webhooks are SSRF-guarded. Webhook / Slack / Discord / Teams deliveries enforce http(s)-only and refuse targets that resolve to RFC1918, loopback, link-local, or cloud-metadata (169.254.169.254) addresses, don't follow redirects, and re-validate the resolved address at connect time to close the DNS-rebinding window. Set ALLOW_PRIVATE_WEBHOOKS=1 only when your webhook target is intentionally on the same LAN.