Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 60 additions & 0 deletions security/pentest-2026-06-16.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
# Pentest mini-report — versila22/lima-app — 2026-06-16

**Probed URL:** https://limaimpro.duckdns.org/
**Stack:** React 18 + Vite 7 / npm / PWA=yes (vite-plugin-pwa, workbox autoUpdate)
**Backend:** FastAPI (Python) on Railway · nginx SPA proxy in Docker
**Counts:** Critical=0 High=2 Medium=1 Low=2 Info=1

## Findings

| Sev | Cat | Title | Location |
|---|---|---|---|
| High | DAST | CSP style-src unsafe-inline | nginx.conf:12 |
| High | SCA | vitest <3.2.6 — arbitrary file read/execute via UI server (GHSA-5xrq-8626-4rwp) | package.json / vitest@3.2.4 (devDep) |
| Medium | SCA | react-router 6.30.3 open redirect via `//`-prefixed path (GHSA-2j2x-hqr9-3h42) | package.json / react-router@6.30.3 |
| Low | SAST | JWT access token stored in sessionStorage (Safari cross-origin cookie workaround) | src/lib/api.ts:45 |
| Low | Info | Leftover dev artifacts committed to repo (api.ts.orig, api_upload.patch) | src/lib/api.ts.orig, src/lib/api_upload.patch |
| Info | Infra | Default FRONTEND_URL (`improv-cabaret-planner.lovable.app`) injected into CORS allowlist when env var is unset | backend/app/config.py:36 |

## Top 3 fixes
1. **CSP unsafe-inline style-src** — Replace `'unsafe-inline'` with CSS hashes/nonces or move to external stylesheets to prevent CSS injection.
2. **react-router open redirect** — Upgrade `react-router-dom` to ≥6.30.4 (already near the boundary — single patch bump).
3. **vitest devDep** — Upgrade `vitest` to ≥3.2.6; the Vitest UI server (`vitest --ui`) should never be exposed on a shared/CI host.

## Evidence (Critical/High only)

**CSP unsafe-inline style-src** · nginx.conf:12
```
style-src 'self' 'unsafe-inline';
```
Allows attacker-controlled inline `<style>` injection (if another sink permits HTML injection) to exfiltrate data via CSS attribute selectors. `script-src 'self'` (no unsafe-inline) limits XSS risk but CSS injection remains.
Fix: use hashes for known inline styles; eliminate `'unsafe-inline'`.

**vitest 3.2.4 (devDep)** · GHSA-5xrq-8626-4rwp — CVSS 9.8
```
"vitest": "^3.2.4" → installed 3.2.4 (fix: 3.2.6)
```
When `vitest --ui` is running, arbitrary files can be read and executed by any network-reachable client. Production build is unaffected (devDependency not bundled), but CI runners and developer machines are at risk if the UI is exposed.
Fix: `npm install -D vitest@^3.2.6`.

## Verified safe
- HSTS present: `max-age=31536000; includeSubDomains` (nginx.conf:14)
- X-Frame-Options: DENY (nginx.conf:16)
- X-Content-Type-Options: nosniff (nginx.conf:15)
- Referrer-Policy: strict-origin-when-cross-origin (nginx.conf:17)
- CORS: explicit allowlist (no wildcard), `allow_credentials=True` paired with enumerated origins only — not reflective
- No dangerouslySetInnerHTML on user input: chart.tsx:70 uses static config keys, not user data
- No secrets/API keys found in committed source
- TLS: 1.2 + 1.3 only (1.0/1.1 disabled) — valid cert (expires 2026-07-16)
- Sensitive files (.env, .git/config): 403 from CDN/WAF layer
- Service worker scope: `/` with NetworkFirst caching for API — no auth route pre-caching issue
- JWT_SECRET default rejected at startup in non-dev mode (model_validator guard in config.py)

## Needs server-side verification
- Backend CORS actual production value of `CORS_ORIGINS` env var on Railway (verify `improv-cabaret-planner.lovable.app` is excluded in prod)
- Railway API response headers — confirm SecurityHeadersMiddleware headers are forwarded through proxy
- PWA workbox cache on auth routes — verify API cache excludes session-sensitive endpoints

## Tools
ran=npm-audit, grep-secrets, curl-headers(blocked-403), openssl-tls, nginx.conf-review, source-sast
skipped=pnpm-audit(not used in this repo), nmap(not authorized)
Loading