| name | frontend-review-security |
| description | Use when conducting a frontend security review — static analysis (risky HTML patterns, env var exposure), authentication/authorization audit (token storage, route guards, logout), and AI self-penetration testing. Runs `scripts/audit-security.sh`. For CVE triage and deprecated library detection, use `frontend-review-deps`. |
Frontend Review — Security
You are performing a frontend security review. The focus areas are:
- Static — risky HTML sinks, environment variable exposure in client bundles
- Auth / Authorization — token storage, route guards, session management
- AI self-pentest — desk-check of common vulnerability patterns
- Staging environment — HTTP headers, auth boundaries, cookie flags
Procedure
- Run
scripts/audit-security.sh --repo <client-repo>.
- Read
raw/security.json.
- For each
dangerouslySetInnerHTML / v-html / .innerHTML = hit, locate the file and judge whether the input is sanitized.
- Run the Authentication & Authorization review (see below).
- Run the Env / Config review (see below).
- For AI self-pentest, mentally walk through the attack scenarios below.
- For staging, draft the header checklist.
Authentication & Authorization Review
Token storage
Check where access tokens are stored and flag insecure patterns:
| Storage | Risk | Verdict |
|---|
httpOnly Cookie | JS-inaccessible, XSS-resistant | ✅ Recommended |
localStorage | Readable by any JS on the page — XSS steals it | ⚠ Flag + require XSS mitigations |
sessionStorage | Same XSS risk as localStorage | ⚠ Flag |
| In-memory (module variable) | Lost on reload; only viable in short-lived SPAs | Context-dependent |
- Check whether the refresh token is also stored in
httpOnly Cookie.
- Check whether the access token lifetime is short (recommended: 15 min – 1 hour).
Route guards
- Does
ProtectedRoute (or equivalent) have a loading state that prevents a flash of the protected page before auth status resolves?
if (!user) return <Navigate to="/login" />;
if (isLoading) return <LoadingSpinner />;
if (!user) return <Navigate to={`/login?redirect=${encodeURIComponent(location.pathname)}`} />;
- Does the app redirect back to the original page after login (
redirect / redirect_to param)?
- Server-side authorization exists for every protected API endpoint — frontend guards alone can be bypassed via DevTools or curl.
Token refresh
- Does the API client auto-retry on 401 by refreshing the token first?
- Is refresh deduplicated so that parallel 401s don't trigger multiple refresh calls?
let refreshPromise: Promise<string> | null = null;
async function refreshToken(): Promise<string> {
if (refreshPromise) return refreshPromise;
refreshPromise = doRefresh().finally(() => { refreshPromise = null; });
return refreshPromise;
}
Logout
- Does logout call the server endpoint AND clear all client-side state?
await api.post('/auth/logout');
queryClient.clear();
authStore.reset();
router.replace('/login');
- After logout, does a hard-reload show the previous user's data? (Check: TanStack Query devtools, React state, localStorage)
Env / Config Review
VITE_ / NEXT_PUBLIC_ prefixed variables must not contain secrets. Vite / Next.js embed these into the client bundle — anyone can read them in DevTools.
.env must not be committed. Run: git log --all --full-history -- '*.env'
- Is
src/config.ts (or equivalent) the single entry point for env var reads? Direct import.meta.env.VITE_FOO calls scattered in components are a review red flag.
- Does
config.ts throw at startup if a required env var is missing?
const requireEnv = (key: string): string => {
const value = import.meta.env[key];
if (!value) throw new Error(`Missing required env var: ${key}`);
return value;
};
- Is
ImportMetaEnv extended in vite-env.d.ts so that unknown VITE_* keys are caught by TypeScript?
AI Self-Pentest Scenarios
Walk through each scenario mentally and note: OK / finding / unable-to-determine.
- XSS via URL parameter — does a malicious
?q=<script>alert(1)</script> get rendered unsanitized?
- XSS via form input — is user-supplied HTML ever rendered with
dangerouslySetInnerHTML without sanitization?
- CSRF — do state-mutating API calls require a CSRF token or use
SameSite=Strict cookies?
- Auth boundary bypass — can an unauthenticated
fetch('/api/protected') return data?
- Sensitive data in storage — does
localStorage.getItem reveal tokens, PII, or session data?
- Client-side-only authorization — are there role checks in React code that are not mirrored server-side?
- Open redirect — does the
redirect / next login parameter allow arbitrary external URLs?
Staging Checklist
Draft these for the human to run against the deployed staging URL:
Output
Write <client-repo>/.frontend-review/report/latest/md/security-review.md with:
- Static findings (risky sinks, env var exposure)
- Auth / Authorization findings (token storage, route guard gaps, logout issues)
- AI pentest notes (each scenario: OK / finding / unable-to-determine)
- Staging checklist (to be executed by the human)
- Issues to file (
gh issue create commands with titles and bodies)
Do NOT execute the gh issue create commands yourself — print them for the human.
Boundaries
- Do NOT attempt actual exploitation. This is a desk review.
- Do NOT run scanners against production URLs.
- Do NOT touch the client source code.
- CVE triage and trend-watch are handled by
frontend-review-deps.
Reference