Files
lexmind/SECURITY-AUDIT.md

7.5 KiB

LexMind Security Audit Report

Date: 2026-02-01 Auditor: Automated Security Audit Status: COMPLETED


Executive Summary

Comprehensive security audit of the LexMind application (Next.js 16 + Prisma + PostgreSQL + NextAuth + Stripe). Found and fixed 12 vulnerabilities across 7 categories. Zero npm vulnerabilities remain.


1. NPM Vulnerabilities

Status: FIXED

Before After
21 high severity 0 vulnerabilities
  • Root cause: fast-xml-parser 5.2.5 (via AWS SDK) had RangeError DoS bug
  • Fix: Added npm override for fast-xml-parser@5.3.4 in package.json

2. SQL Injection / Prisma

Status: CLEAN

  • No $queryRaw or $executeRaw usage found
  • All database access uses Prisma's parameterized queries
  • No raw SQL anywhere in the codebase

3. XSS (Cross-Site Scripting)

Status: CLEAN

  • No dangerouslySetInnerHTML or innerHTML usage found
  • React's default escaping protects against XSS
  • Added Content-Security-Policy header (see Section 9)

4. CSRF Protection

Status: VERIFIED

  • NextAuth CSRF tokens working correctly
  • Cookies use __Host- prefix with HttpOnly; Secure; SameSite=Lax
  • All mutating API routes require authenticated session

5. Authentication & Authorization

Status: FIXED (2 critical issues)

🔴 CRITICAL: Unauthenticated Checkout Route

  • File: /api/checkout/route.ts
  • Issue: No getServerSession check — anyone could create Stripe checkout sessions
  • Fix: Added authentication check, uses session email instead of user-provided email

🔴 CRITICAL: Unauthenticated DOCX Export Route

  • File: /api/export/docx/route.ts
  • Issue: No authentication — anyone could generate DOCX documents
  • Fix: Added getServerSession check

Auth Coverage (all routes verified):

Route Auth IDOR Protected
/api/admin/stats ADMIN check N/A
/api/analise-processo session.user.id userId filter
/api/analise-processo/[id] session.user.id userId filter
/api/auditoria session.user.id userId filter
/api/auditoria/[id] session.user.id userId filter
/api/chat session.user.id userId filter
/api/chat/[chatId] session.user.id userId filter
/api/checkout FIXED N/A
/api/documents session.user.id userId filter
/api/documents/[id] session.user.id userId filter
/api/documents/generate session.user.id N/A
/api/export/docx FIXED N/A
/api/jurisprudencia session.user.id N/A (public data)
/api/jurisprudencia/search session.user.id N/A (public data)
/api/keys session.user.id userId filter
/api/keys/[id] session.user.id userId filter
/api/prazos session.user.id userId filter
/api/prazos/[id] session.user.id userId filter
/api/register N/A (public) N/A
/api/stripe/checkout session.user
/api/stripe/portal session.user
/api/stripe/webhook N/A (Stripe sig) signature verified
/api/templates session.user.id userId filter
/api/uploads session.user.id userId filter
/api/uploads/[id] session.user.id userId filter

6. Rate Limiting

Status: VERIFIED

Nginx rate limiting active:

  • Auth routes: 5 req/min (zone=auth)
  • API routes: 20 req/sec (zone=api)
  • General: 30 req/sec (zone=general)
  • Connection limit: 20 per IP (conn_limit)
  • Scanner/bot blocking via User-Agent filter

7. Input Validation

Status: FIXED (5 improvements)

  • Created: src/lib/validate.ts with sanitization utilities
  • Register route: Added input length limits for all fields
  • Chat route: Added 10,000 char message limit
  • Auditoria route: Added title (500) and content (100,000) limits
  • Prazos route: Added title (500) and description (5,000) limits
  • Pagination: Bounded page/limit params in uploads and jurisprudencia routes
  • Uploads: Added server-side file extension validation (defense in depth)

8. Sensitive Data Exposure

Status: FIXED

  • Created .gitignore.env was not being excluded (no .gitignore existed!)
  • Stripe error messages: Stopped leaking error.message to client in checkout/portal routes
  • API responses: Verified no password hashes or internal IDs are exposed
  • API keys: Properly hashed (SHA-256), only shown once on creation, masked in listings
  • NEXT_PUBLIC vars: Only publishable Stripe key and app URL (safe)
  • Error handling: All routes return generic error messages, details logged server-side

9. Security Headers

Status: FIXED (2 new headers added)

Headers now active:

Header Value Status
X-Frame-Options SAMEORIGIN existing
X-Content-Type-Options nosniff existing
X-XSS-Protection 1; mode=block existing
Referrer-Policy strict-origin-when-cross-origin existing
Strict-Transport-Security max-age=31536000; includeSubDomains existing
Content-Security-Policy Full CSP policy ADDED
Permissions-Policy camera=(), microphone=(), geolocation=() ADDED
server_tokens off existing

10. Database Security

Status: VERIFIED

  • PostgreSQL listens only on localhost (default, listen_addresses = 'localhost')
  • pg_hba.conf uses scram-sha-256 for TCP connections
  • Local connections use peer authentication
  • No remote access configured

11. File Upload Security

Status: VERIFIED + IMPROVED

  • MIME type whitelist: PDF, DOC, DOCX, TXT only
  • Added: File extension validation (defense in depth)
  • Max size: 50MB (enforced both in app and nginx client_max_body_size)
  • Storage limits per plan (1GB-20GB)
  • File paths sanitized via buildKey() — strips all special chars
  • No path traversal possible (../ becomes ___)
  • Files stored as private ACL on DigitalOcean Spaces
  • Access via signed URLs (15 min expiry)

12. Session Security

Status: IMPROVED

  • Cookies: __Host- prefix, HttpOnly, Secure, SameSite=Lax
  • Reduced session maxAge from 30 days to 7 days (more appropriate for legal app)
  • JWT strategy with strong NEXTAUTH_SECRET (44 chars, base64)
  • CSRF token verified on all auth requests

13. Other Fixes

Duplicate Webhook Route Removed

  • Deleted: /api/webhook/stripe/route.ts (incomplete, only logged events, no DB updates)
  • Active: /api/stripe/webhook/route.ts (fully functional with DB updates)

Next.js Middleware Added

  • Created: src/middleware.ts — adds security headers at application level as backup

14. Remaining Notes (Low Risk)

Item Risk Notes
typescript: { ignoreBuildErrors: true } in next.config Low Could hide type errors; recommend fixing eventually
AWS SDK DoS vuln was in XML parsing Info Fixed via override, but only exploitable if attacker controls S3 responses
File upload MIME check trusts client header Low Mitigated by extension whitelist + private storage
No email verification on registration Medium Users can register with unverified emails

Deployment Status

  • All fixes applied
  • npm audit: 0 vulnerabilities
  • Build successful
  • PM2 restarted
  • Nginx reloaded with new headers
  • Application verified working