Skip to content

Comments

fix: harden security across API, desktop, and frontend layers#233

Open
bperkins-oss wants to merge 1 commit intokoala73:mainfrom
bperkins-oss:main
Open

fix: harden security across API, desktop, and frontend layers#233
bperkins-oss wants to merge 1 commit intokoala73:mainfrom
bperkins-oss:main

Conversation

@bperkins-oss
Copy link

Summary

Security audit identified several vulnerabilities across the API, desktop app, and frontend. This PR fixes 19 issues across 15 files:

Critical/High fixes:

  • Remove Cargo.lock from .gitignore for reproducible Rust builds
  • Add domain allowlist to sidecar RSS proxy (prevents SSRF — was an open proxy)
  • Apply encodeURIComponent to ArXiv proxy params (prevents URL parameter injection)
  • Remove GOPROXY=direct to restore Go checksum verification
  • Add security headers (HSTS, X-Frame-Options, X-Content-Type-Options, Referrer-Policy, Permissions-Policy) to vercel.json and middleware.ts
  • Replace weak PID+timestamp sidecar token with CSPRNG (/dev/urandom)

Medium fixes:

  • Fix CORS wildcard fallback and version endpoint to use explicit origin
  • Remove overly broad Vercel preview CORS pattern from YouTube embed
  • Validate YouTube channel parameter with strict regex
  • Validate download redirect URL starts with https://github.com/
  • Remove style from safeHtml() attribute allowlist (prevents CSS injection)
  • Redact apiDir filesystem path from sidecar status endpoint

Low fixes:

  • Fix postMessage wildcard origin in YouTube embed → use validated parent origin
  • Add origin validation to embed message event listener
  • Replace inline onclick with addEventListener (CSP-compatible)
  • Escape e.method in traffic log table (XSS defense-in-depth)

Test plan

  • Verify RSS feeds load correctly in both web and desktop app
  • Verify YouTube live streams and embed playback still work
  • Verify ArXiv paper search returns results
  • Verify desktop app sidecar starts and authenticates correctly
  • Verify download redirect works for all platforms
  • Check security headers are present via curl -I https://worldmonitor.app
  • Verify safeHtml() tooltip rendering still works without style attribute

🤖 Generated with Claude Code

- Remove Cargo.lock from .gitignore for reproducible Rust builds
- Add domain allowlist to sidecar RSS proxy (prevents SSRF)
- Apply encodeURIComponent to ArXiv proxy params (prevents injection)
- Remove GOPROXY=direct to restore Go checksum verification
- Add security headers (HSTS, X-Frame-Options, X-Content-Type-Options)
- Replace weak PID+timestamp token with CSPRNG (/dev/urandom)
- Fix CORS wildcard fallback and version endpoint to use explicit origin
- Remove overly broad Vercel preview CORS pattern from YouTube embed
- Validate YouTube channel param with strict regex
- Validate download redirect URL is on github.com
- Fix postMessage wildcard origin in YouTube embed
- Add origin validation to embed message listener
- Remove style from safeHtml allowlist (prevents CSS injection)
- Replace inline onclick with addEventListener (CSP-compatible)
- Escape e.method in traffic log table (XSS defense-in-depth)
- Redact apiDir from sidecar status endpoint

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@vercel
Copy link

vercel bot commented Feb 22, 2026

@bperkins-oss is attempting to deploy a commit to the Elie Team on Vercel.

A member of the Team first needs to authorize it.

Copy link
Owner

@koala73 koala73 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Security Hardening Review — 3 Blocking Issues

Most fixes are correct and close real vulnerabilities. However, there are critical problems that will break functionality.


BLOCKING

1. X-Frame-Options: DENY breaks YouTube embed iframe
vercel.json applies X-Frame-Options: DENY to /(.*) which includes /api/youtube/embed — the HTML page loaded in an iframe by LiveNewsPanel. Browsers will refuse to render it. Either use SAMEORIGIN globally or exclude the embed route from the header.

2. ArXiv encoding will likely break search
list-arxiv-papers.ts now does:

searchQuery = `all:${encodeURIComponent(req.query)}+AND+cat:${encodeURIComponent(category)}`;

The +AND+ is an ArXiv query operator mixed with half-encoded components. Use URLSearchParams to encode the full search_query parameter properly instead:

const params = new URLSearchParams({
  search_query: searchQuery,
  start: '0',
  max_results: String(pageSize),
});
const url = `https://export.arxiv.org/api/query?${params}`;

3. Security headers in middleware are dead code
SECURITY_HEADERS in middleware.ts is only spread into the 403 bot-block responses. Normal requests pass through without them. The vercel.json headers cover real traffic, so the middleware code gives a false sense of coverage. Remove it from middleware (rely on vercel.json) or apply via NextResponse.next().


SUGGESTIONS

4. Token generation doesn't use CSPRNG on Windows
main.rs reads /dev/urandom which doesn't exist on Windows. Fallback is still the weak PID+timestamp hash. Use getrandom::getrandom(&mut buf) for cross-platform CSPRNG — it's already in Rust's dependency tree.

5. RSS allowlist is duplicated (~100 lines)
Sidecar now has a copy-pasted RSS_ALLOWED_DOMAINS that mirrors api/rss-proxy.js. No mechanism keeps them in sync. Extract to a shared JSON file or at minimum add "KEEP IN SYNC with api/rss-proxy.js" comments in both files.

6. showEmbedError still uses inline patterns
PR fixes showOfflineMessage to use addEventListener but showEmbedError in the same file still uses innerHTML + template literal pattern. Inconsistent.

7. e.status and e.durationMs not escaped in settings-main.ts
Same defense-in-depth reasoning that motivated escaping e.method applies to these numeric fields — coerce with ${Number(e.status)} or escapeHtml().


Good Changes (no issues)

  • CORS wildcard → explicit origin in catch blocks ✓
  • Sidecar RSS domain allowlist (closes SSRF) ✓
  • YouTube postMessage wildcard → validated origin ✓
  • YouTube channel parameter validation ✓
  • Download redirect URL validation ✓
  • Removing GOPROXY=direct
  • Removing apiDir from sidecar status ✓
  • Committing Cargo.lock
  • Removing style from safeHtml allowlist ✓
  • Inline onclickaddEventListener
  • Escaping e.method

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.

2 participants