frontend-security
Skill3 starsChanged 10 days agoMITView on GitHub
Use this skill when writing or reviewing client-side code that renders user data, stores tokens, calls an API, or embeds third-party scripts - React/Next/Vue/Svelte or plain DOM. Covers XSS sinks, token storage, environment-variable leakage, CSP and security headers, iframe and postMessage trust, and the checks that catch a public key that is actually a private one.
5 other projects use a version of this.
Adopted by
Most recently taken up by
- reddygopalkrishna/developer-agent-skillstheir own version, first seen Nov 8, 2026
- MiladJoodi/skillstheir own version, first seen Oct 11, 2026
- maleus-ai/claude-code-setuptheir own version, first seen Oct 8, 2026
- ZeroTokenClaw/agents-skillstheir own version, first seen Sep 21, 2026
- Rootx202/appsec-skillstheir own version, first seen Jul 13, 2026
Other versions5 projects use a version of this.
Word for word · 0
None found.
Edited · 0
None found.
Wrote their own version · 5
- MiladJoodi/skills★ 24same name
- reddygopalkrishna/developer-agent-skills★ 8same name
- Rootx202/appsec-skills★ 2same name
- maleus-ai/claude-code-setup★ 1same name
- ZeroTokenClaw/agents-skills★ 1same name
How matches are found. Word for word: the exact same text. Edited: shares passages with this file (“99% same” means 99% of its five-word phrases also appear here; 10% or more counts). Their own version: similar meaning, or, weakest, the same name. We can’t tell which project had it first. Projects are counted among the 200 most-starred candidates, and mentions are links to the repository we found, up to 50 per site, so both counts are a floor.
In this repository72 other files it gives its agents.
- workflow/AGENTS.mdAGENTS.md
- docs/llms.txtllms.txt
- llms.txtllms.txt
- ai-video-prompt-engineerSkill
- api-contract-designSkill
- api-hardeningSkill
- brainstormingSkill
- cavecrewSkill
- caveman-commitSkill
- caveman-compressSkill
- caveman-helpSkill
- caveman-reviewSkill
- cavemanSkill
- councilSkill
- database-migrationsSkill
- deep-researchSkill
- dependency-auditSkill
- deployment-patternsSkill
- e2e-testingSkill
- error-handlingSkill
- exa-searchSkill
- finishing-a-development-branchSkill
- github-opsSkill
- graphifySkill
- graphyloop-wavesSkill
- graphyloop-workflowSkill
- gsap-coreSkill
- gsap-frameworksSkill
- gsap-performanceSkill
- gsap-pluginsSkill
- gsap-reactSkill
- gsap-scrolltriggerSkill
- gsap-timelineSkill
- gsap-utilsSkill
- high-end-visual-designSkill
- image-to-codeSkill
- last30daysSkill
- minimalist-uiSkill
- postgres-patternsSkill
- prisma-patternsSkill
- receiving-code-reviewSkill
- redesign-existing-projectsSkill
- remotion-video-creationSkill
- requesting-code-reviewSkill
- search-firstSkill
- secrets-hygieneSkill
- security-reviewSkill
- security-scanSkill
- short-video-productionSkill
- story-engineeringSkill
- supabase-setupSkill
- swarm-memorySkill
- systematic-debuggingSkill
- tdd-workflowSkill
- terminal-opsSkill
- threejs-animationSkill
- threejs-fundamentalsSkill
- threejs-geometrySkill
- threejs-interactionSkill
- threejs-lightingSkill
- The first 60 shown.
The file
We split this skill into the rules it gives an agent
3 rules. 0 are common, 0 are distinctive, and 3 are very specific to this project.
0
Common
Most of the 5 edited versions we read keep these.
0
Distinctive
This author's own choices. Worth a look.
3
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Very specific: line 34
Never prefix: service-role keys, admin/API secrets, webhook signing secrets, SMTP or DB credentials, private OAuth client secrets.
- Very specific: line 70
Never put a token in a URL/query string (logs, referrers, history) or in a client-side cookie without secure.
- Very specific: line 100
Every external <script>: pin the version, prefer defer, add integrity + crossorigin when the CDN supports SRI, and add its origin to CSP …
“Common” is measured: kept by at least 6 in 10 of the 5 edited versions of this file we could read. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Frontend security
Server-side review misses the frontend because the frontend "has no secrets" — and
then a service-role key ships in a NEXT_PUBLIC_* var, a markdown renderer writes
raw HTML into the DOM, and an access token sits in localStorage where any
injected script can read it. This skill is the client-side pass: the sinks, the
storage, the headers, and what to verify before calling it done.
When to activate
- Rendering anything a user or an API supplied (HTML, markdown, SVG, URLs, filenames).
- Auth in the browser: login, logout, token refresh, "remember me", role-based UI.
- Adding an env var, an analytics/chat/payment script, an iframe, or a
postMessagechannel. - Reviewing a PR that touches any of the above, or a "make it work in the browser" fix.
The five checks, in order
- Secrets — no key that grants privilege may reach the bundle.
- Sinks — every place a string becomes markup, a URL, or code.
- Token storage — where credentials live between requests.
- Headers — CSP and friends, set where the app is actually served from.
- Third-party — scripts, iframes, and messages you did not write.
1. Secrets in the bundle
- A var is public the moment its name is prefixed (
NEXT_PUBLIC_,VITE_,PUBLIC_,REACT_APP_,EXPO_PUBLIC_) or referenced from a client component. Prefixing a server key does not make it safe — it publishes it. - Never prefix: service-role keys, admin/API secrets, webhook signing secrets,
SMTP or DB credentials, private OAuth client secrets. See
secrets-hygiene. - Grep before shipping:
NEXT_PUBLIC_.*(SERVICE|SECRET|PRIVATE|ADMIN|TOKEN|PASSWORD), and search the built output (.next/static,dist/assets) for the last 8 characters of any server key you hold. A hit in the build is a leak, not a warning. - Anything the browser can call, an attacker can call with the same key. If a key must stay secret, the call belongs in a route handler / server action / edge function, and the client calls that.
2. XSS sinks
| Sink | Rule |
dangerouslySetInnerHTML, v-html, {@html}, .innerHTML, outerHTML, insertAdjacentHTML, document.write | Only with output from a maintained sanitizer (DOMPurify or equivalent), allow-listed tags/attrs, sanitized where it is rendered — not where it was fetched |
href / src / action / formaction from data | Parse and allow-list the scheme; reject javascript:, data:, vbscript:. Relative or https: only |
<a target="_blank"> | rel="noopener noreferrer" |
style / CSS custom properties from data | Never interpolate raw user values into style strings |
eval, new Function, setTimeout("string"), dynamic import(userInput) | Not with user input. Ever |
| Inline SVG upload/preview | Sanitize as HTML — SVG carries <script> and event handlers |
JSON.parse(localStorage) then render | Treat as untrusted: another script may have written it |
Server-rendered JSON in a <script> tag | Escape <, >, &, \u2028/\u2029 or use application/json + textContent |
Framework escaping covers interpolation only. JSX escapes {value}; it does not
escape dangerouslySetInnerHTML, attribute-injected URLs, or SSR'd script bodies.
Markdown is HTML. marked/markdown-it output must be sanitized unless the
renderer is configured to disallow raw HTML.
3. Token and session storage
- Preferred:
httpOnly; secure; sameSite=lax|strictcookie set by the server. JS cannot read it, so an injected script cannot steal it. localStorage/sessionStorageare readable by every script on the origin, including a compromised dependency. Use only for non-credential UI state.- In-memory (module/context) plus a refresh call is acceptable for SPAs — it dies with the tab, which is the point.
- Never put a token in a URL/query string (logs, referrers, history) or in a
client-side cookie without
secure. - Log out means: server invalidates, client clears store and in-memory state. A "logout" that only navigates leaves the session alive.
- Client-side role checks are UX, not authorization. Hiding an admin button while
the API still answers
role=admincalls from anyone is an open door — the server must enforce it (seeapi-hardening).
4. Headers and CSP
Set headers where the app is served (next.config headers, middleware, hosting
config, reverse proxy) and verify with a real request, not by reading the config:
curl -sI https://host/ | grep -iE 'content-security-policy|x-frame|x-content-type|referrer|strict-transport|permissions-policy'- CSP is the one that actually stops XSS escalation. Aim for
default-src 'self', nounsafe-inline/unsafe-evalin scripts (nonce or hash for the framework's inline bootstrap), explicitconnect-src,img-src,frame-ancestors 'none'unless embedding is a feature. Ship inContent-Security-Policy-Report-Onlyfirst, read the reports, then enforce. X-Content-Type-Options: nosniff·Referrer-Policy: strict-origin-when-cross-origin·Strict-Transport-Securityon HTTPS ·Permissions-Policydenying camera / microphone / geolocation you do not use.- CORS is not a frontend fix. Widening
Access-Control-Allow-Originto*with credentials to "make fetch work" removes a control instead of fixing the bug.
5. Third-party and cross-origin
- Every external
<script>: pin the version, preferdefer, addintegrity+crossoriginwhen the CDN supports SRI, and add its origin to CSP explicitly. A tag-manager container is arbitrary code execution by design — treat it as such. postMessage: always send with an explicit target origin (never'*'), and on receive verifyevent.originagainst an allow-list before readingevent.data. Validate the payload shape too.- Iframes you embed:
sandboxwith the minimum tokens,allowlist for permissions,referrerpolicy. Untrusted embeds never getallow-scripts allow-same-origintogether — that combination lets the frame escape the sandbox. - File inputs: validate type and size client-side for UX, and never trust the
result. Preview images via
URL.createObjectURL, never by injecting markup.
Verification before reporting
- Build the app, then grep the output for server key fragments and for the prefixed-secret pattern above. Zero hits.
curl -sIthe running app and paste the security headers you actually got.- For each sink you touched: the sanitizer call site, or the reason it is safe.
- A rendering test with a payload, not a claim:
<img src=x onerror=alert(1)>,javascript:alert(1)in a link field,"><script>alert(1)</script>in a search box. Assert escaped text in the DOM.
Reporting
List findings as severity · file:line · what an attacker does · fix. Separate
"fixed here" from "needs a server change" (those belong to api-hardening /
chadi-backend). If a check could not run — no build, no running server — say
which one and why instead of implying it passed.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

