client-securitylisted
Install: claude install-skill soumit-kaz/lazysitter
# Client-side security
Two questions cover most of it:
1. **What did we ship to the browser that we should not have?**
2. **What will we render that we do not control?**
## Everything in the bundle is public
There is no such thing as a secret in client code. Minification is not obfuscation, and source maps are often shipped.
**Build-time public prefixes are the trap**: `NEXT_PUBLIC_`, `VITE_`, `REACT_APP_`, `PUBLIC_`, `EXPO_PUBLIC_`, `GATSBY_`. A value behind one of these is **inlined into the JavaScript as a string literal** — it is not an environment variable at runtime, it is published.
```bash
lazysitter fe-index signals --rule SEC-PUBLIC-ENV-SECRET
```
Some keys are *designed* to be public — a Stripe publishable key, a Supabase anon key, a public analytics id. Say which is which, and why, rather than flagging everything or nothing.
## XSS: React escapes by default, and here is where it does not
React escaping text is why XSS is rare in React apps — and why the exceptions matter so much.
**`dangerouslySetInnerHTML`** — the main sink. Every use needs a real sanitizer (DOMPurify or equivalent) with a stated allow-list, and a reason the content cannot be rendered as text. Sanitize on **output**, not only on input — data can enter through a path you did not sanitize.
**`href` / `src` from a variable** — a `javascript:` or `data:text/html` URL is script execution:
```jsx
<a href={user.website}> // user.website = "javascript:fetch('/api/keys')..."
```
Validat