Skip to content

Security audit β€” 2026-06-24#73

Open
versila22 wants to merge 1 commit into
mainfrom
security-audit-2026-06-24
Open

Security audit β€” 2026-06-24#73
versila22 wants to merge 1 commit into
mainfrom
security-audit-2026-06-24

Conversation

@versila22

Copy link
Copy Markdown
Owner

Pentest mini-report β€” lima-app β€” 2026-06-24

Probed URL: https://limaimpro.duckdns.org/
Stack: React 18 + Vite 7 / npm / PWA=yes (vite-plugin-pwa, autoUpdate, SW workbox)
Backend: FastAPI (Python) on Railway β€” https://api-production-e15b.up.railway.app
Counts: Critical=1 High=3 Medium=4 Low=3 Info=2

Findings

Sev Cat Title Location
Critical SAST Hardcoded admin credentials in production source backend/app/main.py:54
High SAST Stale debug files tracked in git (.env.production, api.ts.orig, api_upload.patch) src/lib/api.ts.orig, .env.production
High SAST JWT token stored in sessionStorage (XSS-accessible) src/lib/api.ts:45,53
High SAST Unauthenticated /health/db leaks DB URL prefix and full table list backend/app/main.py:202-222
Medium SAST FRONTEND_URL default is stale third-party domain auto-injected into CORS_ORIGINS backend/app/config.py:37,85-87
Medium Infra TLS 1.2 enabled β€” TLS 1.3 preferred, 1.2 still accepted limaimpro.duckdns.org:443
Medium Deps vitest <3.2.6 β€” arbitrary file read/exec via UI server (GHSA-5xrq-8626-4rwp, CVSS 9.8) package.json devDependency
Medium Deps react-router-dom <6.30.4 β€” open redirect via // prefix paths (GHSA-2j2x-hqr9-3h42) package.json
Low SAST /health/migrations leaks full Alembic traceback on error (unauthenticated) backend/app/main.py:225-240
Low Deps serialize-javascript <=7.0.2 β€” RCE via RegExp/Date during build (GHSA-5c6j-r48x-rmvq, High) package.json devDependency
Low Infra robots.txt allows all crawlers on a members-only app public/robots.txt
Info DAST Security headers confirmed present in nginx.conf (CSP, HSTS, nosniff, X-Frame-Options) nginx.conf
Info PWA No VAPID private key in client code; SW uses NetworkFirst for API, no auth-route caching risk vite.config.ts workbox config

Top 3 fixes

  1. Hardcoded admin credentials β€” Remove _SEED_MEMBERS passwords from source; load seed credentials from environment variables or a secrets manager, and enforce a must-change-on-first-login flow.
  2. Unauthenticated /health/db and /health/migrations β€” Add Depends(require_admin) to both endpoints, or restrict to internal network only via Traefik middleware.
  3. Stale debug files in git β€” git rm src/lib/api.ts.orig src/lib/api_upload.patch; evaluate whether .env.production (contains Railway API URL only, no secrets) should remain tracked or be gitignored.

Evidence (Critical/High only)

Critical β€” Hardcoded admin credentials in source

  • Location: backend/app/main.py:54
  • Snippet: {"email": "admin@lima-impro.fr", ..., "password": "Admin1234!", "app_role": "admin", ...}
  • Impact: Anyone with read access to the repository knows the admin account password. If the seed ran against production DB and the password was never changed, the admin account is fully compromised.
  • Fix: Move seed credentials to environment variables (SEED_ADMIN_PASSWORD), add a must_change_password flag, or remove the seed entirely from production startup.

High β€” Stale debug files tracked in git

  • Location: src/lib/api.ts.orig, src/lib/api_upload.patch, .env.production, .env.development
  • Snippet: git ls-files returns all four files as tracked.
  • Impact: api.ts.orig contains the old localStorage-based token implementation β€” a weaker auth pattern. Both increase attack surface understanding for an adversary who clones the repo. .env.production contains the Railway API base URL (no secrets currently), but committing .env.* files establishes a dangerous precedent.
  • Fix: git rm src/lib/api.ts.orig src/lib/api_upload.patch; add *.orig and *.patch to .gitignore; confirm .env.production/.env.development are intentionally tracked.

High β€” JWT token in sessionStorage (XSS-accessible)

  • Location: src/lib/api.ts:42-53, src/contexts/AuthContext.tsx:74
  • Snippet: sessionStorage.setItem(_SESSION_KEY, token) β€” Safari ITP workaround.
  • Impact: Any XSS exploitable in a dependency can extract the access token from sessionStorage, bypassing httpOnly cookie protection.
  • Fix: Serve front and API from the same origin (same-site proxy) to eliminate the sessionStorage fallback; or document and accept the risk with strict CSP (already in place).

High β€” Unauthenticated /health/db info disclosure

  • Location: backend/app/main.py:202-222
  • Snippet: return {"async_url_prefix": settings.async_database_url[:60], "tables": tables}
  • Impact: Returns DB connection URL prefix and full schema table list to any unauthenticated caller.
  • Fix: Add _: Member = Depends(require_admin) to health_check_db and health_check_migrations.

Verified safe

  • CORS: explicit allowlist, not wildcard; allow_credentials=True is safe in this configuration
  • Login rate limiting: @limiter.limit("5/minute") on /auth/login and /auth/activate
  • httpOnly auth cookies: Secure + SameSite=None in production
  • CSP (nginx.conf): no unsafe-eval; script-src 'self'; frame-ancestors 'none'; object-src 'none'
  • HSTS: max-age=31536000; includeSubDomains confirmed in nginx.conf
  • X-Content-Type-Options: nosniff present; X-Frame-Options: DENY present
  • No VAPID private key in client code
  • No eval() or Function() calls in source
  • No postMessage usage in source
  • dangerouslySetInnerHTML in chart.tsx: CSS-only static config, not user input
  • ReactMarkdown in PlanPreview.tsx: no rehype-raw, HTML tags are escaped
  • TLS 1.0/1.1: disabled; TLS 1.3 negotiated (TLS_AES_256_GCM_SHA384)
  • API docs (/docs, /redoc): disabled in production
  • Password reset: always returns 200 (no email enumeration)
  • No exploitable open redirect in app code (all navigate() calls use hardcoded paths)

Needs server-side verification

  • HTTP response headers from live site (egress proxy blocked curl; inferred from nginx.conf)
  • CORS reflection test on Railway API with malicious Origin header
  • /health/db and /health/migrations reachability from public internet on Railway API
  • Seed admin password rotation β€” confirm admin@lima-impro.fr changed from Admin1234! in production DB
  • FRONTEND_URL Railway env var β€” confirm overridden from https://improv-cabaret-planner.lovable.app
  • Cookie flags in live traffic (Secure, SameSite)
  • Sensitive file exposure: .env, .git/config, backup.zip (network check blocked by egress policy)
  • Full TLS 1.2 cipher suite audit (nmap not available in sandbox)

Tools

ran=npm-audit, openssl-s_client, git-ls-files, grep-secret-scan; skipped=curl-http-headers (egress proxy policy denial), curl-cors-test (same), curl-sensitive-files (same), nmap (not installed)


Generated by Claude Code

…-06-03)

First automated pentest report for lima-app.
Findings: Critical=1 High=3 Medium=4 Low=3 Info=2

Critical: hardcoded admin credentials in main.py seed data.
High: stale debug files in git, JWT in sessionStorage, unauthenticated /health/db.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LNGHcHv84S3qAWEEBAN48L
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