Skip to content

Security

Findings

F-1: Token in localStorage (MEDIUM)

localStorage["auth_token"] is XSS-readable. The token has no expiry server-side and is not revokable in a durable way (in-memory revocation set in Someli-admin-api). One successful XSS → long-lived account takeover.

Mitigations: - Migrate to httpOnly; Secure; SameSite=Strict cookie (BE + FE coordinated change) - Encode exp claim in the token; FE refresh on expiry - DB-backed revocation in Someli-admin-api

F-2: Hardcoded Apptype (LOW)

src/services/api.ts createAuthHeaders() hardcodes:

"Apptype": btoa("admin-console"),
even though ENV.APP_TYPE exists in src/config/env.ts. The hardcoded value works (BE doesn't currently branch on Apptype), but the per-env config is dead code.

Fix: change to btoa(ENV.APP_TYPE).

F-3: 3rd-party script in production HTML (MEDIUM)

index.html:

<!-- IMPORTANT: DO NOT REMOVE THIS SCRIPT TAG OR THIS VERY COMMENT! -->
<script src="https://cdn.gpteng.co/gptengineer.js" type="module"></script>

This is the Lovable.dev runtime tag (Lovable.dev is an AI-assisted no-code editor; this codebase was bootstrapped from it). The comment instructs not to remove it.

The script is loaded in production (since it's in index.html, not behind a mode === 'development' check). That means: 1. Every page load fetches a script from cdn.gpteng.co (third-party domain) 2. If that CDN is compromised, attackers can execute arbitrary JS in admin staff's browsers 3. No Subresource Integrity (integrity="sha384-...") protects against tampering

Fix: if Lovable.dev is no longer used to edit this codebase, remove the tag. If it is still used, add integrity + crossorigin="anonymous" attributes, or load it only in dev.

F-4: No CSP (MEDIUM)

Vite's default build does not set CSP headers. Whether CSP is set depends on the hosting layer (nginx?). At audit time, there is no in-repo CSP config (no vercel.json, no netlify.toml, no _headers file).

Recommendation: configure CSP at the nginx layer or via a meta tag. Minimum: default-src 'self'; img-src 'self' data: https:; script-src 'self' 'unsafe-inline' https://cdn.gpteng.co; style-src 'self' 'unsafe-inline'; connect-src 'self' <api-host>.

F-5: No SRI (LOW)

No external scripts use Subresource Integrity. Only one external script exists (cdn.gpteng.co) — see F-3.

F-6: HMR overlay disabled (LOW dev-only)

vite.config.ts:

hmr: { overlay: false }

This disables Vite's error overlay in dev. It can hide errors during development. Re-enabling is one-line. Not a production issue.

F-7: lovable-tagger dev plugin (LOW)

lovable-tagger is a dev-only Vite plugin (mode === 'development' && componentTagger() in vite.config.ts). It is not loaded in production builds. As long as that conditional remains correct, no production impact.

F-8: fs.deny is minimal (LOW dev-only)

fs: { strict: true, deny: ['.env', '.git'] }

The deny list is small. In dev mode, files outside the project root could potentially be served. Vite's fs.strict mode mitigates this, but the team should periodically verify what Vite serves in dev to staff laptops.

F-9: No DOMPurify / sanitisation for dangerouslySetInnerHTML (LOW — only if used)

grep -rE "dangerouslySetInnerHTML" src/ — if any uses exist (none observed in spot-checks), they should sanitise input. If user-generated content is rendered as HTML anywhere, add DOMPurify.

F-10: localStorage["user_email"] (LOW)

Storing the user's email in localStorage is a minor PII exposure (XSS-readable, same as the token). Low impact because the user knows their own email; but if accumulating PII in localStorage becomes a pattern, that's a concern.

F-11: No 401 → logout (MEDIUM)

api.ts's request() doesn't differentiate 401 from other errors. A revoked token causes every action to fail with a toast, but the user remains "logged in" client-side. Add a 401 handler.

Outcomes-based summary

  • OWASP A02 Cryptographic Failures: token in localStorage, no expiry → effectively long-lived credentials
  • OWASP A05 Security Misconfiguration: no CSP, no SRI, 3rd-party script without integrity, HMR-overlay-off, three lockfiles producing inconsistent installs
  • OWASP A06 Vulnerable & Outdated Components: dependency tree is modern (good)
  • OWASP A07 Identification & Authentication Failures: weak logout (client-side only); no MFA; no session expiry; no 401 handler

What's not a finding

  • CSRF: not exploitable (token in custom header, not cookie)
  • Clickjacking: depends on nginx X-Frame-Options / frame-ancestors — not in repo, verify at hosting layer
  • Mixed content: builds use relative paths; no http:// URLs observed
  • SQL injection: not applicable (FE)
  • Hardcoded secrets: none in the FE (all auth secrets are server-side)