Skip to content

fix: harden security across HTTP capabilities, SSRF, crypto, and SQL#245

Open
atani wants to merge 1 commit intoavihaymenahem:mainfrom
atani:fix/security-hardening
Open

fix: harden security across HTTP capabilities, SSRF, crypto, and SQL#245
atani wants to merge 1 commit intoavihaymenahem:mainfrom
atani:fix/security-hardening

Conversation

@atani
Copy link
Copy Markdown

@atani atani commented Apr 3, 2026

Hi there!

First off — Velo is an incredibly well-built email client. The architecture is clean, the Tauri integration is solid, and the codebase is remarkably consistent across 130+ test files. Really impressive work.

I ran a security audit while evaluating Velo for personal use and found a few areas where the already-strong security posture could be tightened further. This PR addresses the findings with minimal, focused changes.

Summary

7 targeted security hardening fixes across the frontend and Rust backend:

  • HTTP capability restriction — Plain HTTP now limited to localhost AI servers (Ollama/LM Studio). HTTPS remains unrestricted for unsubscribe URLs and external APIs
  • SSRF prevention on unsubscribe — New isSafeUrl() guard blocks requests to loopback, private IPv4/IPv6 ranges (including ::ffff: mapped addresses and cloud metadata 169.254.x.x), with redirect following disabled
  • SQL injection fix — Parameterized the accountId in compactQueue() (the only place in the codebase that used string interpolation — everything else was already properly parameterized)
  • Decryption hardeningdecryptField() now throws on failure instead of silently falling back to raw values, preventing potential credential tampering. getAllAccounts() uses Promise.allSettled so one corrupted account doesn't block the rest
  • OAuth token_url whitelist — Rust backend validates token exchange URLs against known providers, with prefix matching for Microsoft tenant GUIDs
  • Phishing detection improvementgetRegistrableDomain() now handles 38 multi-part ccTLDs (.co.uk, .co.jp, .com.au, etc.) to reduce false positives/negatives in brand impersonation detection

Test plan

  • TypeScript type check passes
  • Rust cargo check passes
  • All 1583 tests pass (134 test files)
  • 19 new tests for isSafeUrl() — IPv4/IPv6 private ranges, loopback, schemes, mapped addresses
  • 4 new tests for ccTLD brand impersonation (.co.uk, .co.jp, .com.au)
  • Manual: one-click unsubscribe still works with real newsletters
  • Manual: OAuth login flows (Gmail, Outlook)
  • Manual: thread pop-out window actions (reply, archive, star)

- Restrict HTTP capability: block plain HTTP except localhost AI servers
  (Ollama:11434, LM Studio:1234), allow all HTTPS
- Prevent SSRF via List-Unsubscribe: add isSafeUrl() guard blocking
  loopback, private IPv4, IPv6 private ranges (fc/fd, fe80, ::ffff mapped),
  and disable redirect following
- Fix SQL injection in compactQueue: parameterize accountId
- Harden decryption: throw on failure instead of silent plaintext fallback,
  use Promise.allSettled so one corrupted account doesn't block all
- Add OAuth token_url whitelist in Rust backend with prefix matching
  for Microsoft tenant GUIDs
- Improve phishing detection: support ccTLDs (.co.uk, .co.jp, etc.)
  in domain comparison
- Add 19 tests for isSafeUrl and 4 tests for ccTLD phishing detection
@atani atani requested a review from avihaymenahem as a code owner April 3, 2026 09:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant