frontend-security
Skill8 starsChanged 9 days agoMITView on GitHub
Hardens browser code against XSS, token theft, CSRF, clickjacking and supply-chain attacks (safe DOM sinks, sanitization, strict CSP, Trusted Types, token storage, SRI, postMessage checks). Use when writing or reviewing frontend code that renders user content, handles auth tokens or cookies, adds third-party scripts, sets security headers or CSP, or uses innerHTML, dangerouslySetInnerHTML, v-html or bypassSecurityTrust. Not for server-side injection or authz checks (use security-audit).
- Installs packages
5 other projects use a version of this.
Adopted by
Most recently taken up by
- chadixearth/graphylooptheir own version, first seen Oct 13, 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
- chadixearth/graphyloop★ 3same 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 repository60 other files it gives its agents.
- api-designSkill
- auth-implementationSkill
- background-jobsSkill
- building-agentsSkill
- caching-strategiesSkill
- ci-cd-pipelinesSkill
- cloud-cost-optimizationSkill
- codebase-onboardingSkill
- code-review-changesSkill
- concurrency-patternsSkill
- database-migrationsSkill
- database-schema-designSkill
- database-testingSkill
- data-pipelinesSkill
- data-privacy-retentionSkill
- dependency-upgradesSkill
- design-docsSkill
- design-systemsSkill
- dockerfilesSkill
- domain-driven-designSkill
- event-driven-architectureSkill
- file-storage-uploadsSkill
- git-workflowSkill
- graphql-apisSkill
- grpc-servicesSkill
- incident-responseSkill
- infrastructure-as-codeSkill
- internationalizationSkill
- kubernetes-deploymentsSkill
- large-scale-migrationsSkill
- legacy-refactoringSkill
- llm-evalsSkill
- llm-integrationSkill
- mcp-serversSkill
- micro-frontendsSkill
- ml-experimentsSkill
- monorepo-managementSkill
- nosql-designSkill
- observabilitySkill
- orm-patternsSkill
- performance-profilingSkill
- postgres-operationsSkill
- rag-systemsSkill
- react-patternsSkill
- realtime-apisSkill
- resilience-patternsSkill
- root-cause-debuggingSkill
- search-integrationSkill
- security-auditSkill
- seo-and-ssrSkill
- sql-optimizationSkill
- system-designSkill
- test-strategySkill
- threat-modelingSkill
- typescript-patternsSkill
- unit-testingSkill
- verify-before-doneSkill
- web-accessibilitySkill
- web-performanceSkill
- writing-skillsSkill
The file
Show its rules and text
We split this skill into the rules it gives an agent
13 rules. 0 are common, 7 are distinctive, and 6 are very specific to this project.
0
Common
Most of the 5 edited versions we read keep these.
7
Distinctive
This author's own choices. Worth a look.
6
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 19
Not for: server-side injection, authorization checks and secrets management (see the security-audit skill).
- Distinctive: line 23
Find the sinks: Search the codebase for the dangerous APIs in the table below.
- Very specific: line 25
Prefer safe rendering: Use text bindings ({value}, {{ value }}, textContent) so the framework escapes output.
- Very specific: line 27
Validate URLs: before putting them in href, src, action or location: parse with new URL() and allow only http:, https: (and mailto: if …
- Distinctive: line 31
Before changing production headers or CSP enforcement, show the user the exact header and get confirmation: a wrong policy can break the …
- Very specific: line 67
Never perform state changes on GET.
- Very specific: line 87
Only variables with the framework's public prefix reach the browser (NEXT_PUBLIC_, VITE_, PUBLIC_ in SvelteKit and Astro, REACT_APP_ in …
- Distinctive: line 88
Don't serve source maps publicly in production unless you have decided the source is public.
- Very specific: line 93
Commit the lockfile and install with npm ci (or pnpm install --frozen-lockfile, yarn install --immutable) in CI.
- Distinctive: line 94
Run the ecosystem's audit in CI and review new dependencies before adding them: maintainers, download history, install scripts, and name …
- Very specific: line 95
Consider --ignore-scripts for installs where packages don't need lifecycle scripts.
- Distinctive: line 109
Every dangerous sink found has either been replaced or has sanitization and a comment explaining why it is safe.
- Distinctive: line 113
No secrets in client bundles.
“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
Assume every string that reaches the DOM can contain an attacker's payload, and that anything shipped to the browser is public. Defend in layers: safe rendering first, sanitization where HTML is unavoidable, then a strict CSP to contain what slips through.
When to use
- Rendering user-generated or third-party content (comments, markdown, rich text, URLs).
- Deciding where to store access or refresh tokens in a single-page app.
- Adding a Content Security Policy or other security headers.
- Adding third-party scripts (analytics, chat widgets, tag managers).
- Using
postMessage, iframes, redirects after login, orwindow.open.
Not for: server-side injection, authorization checks and secrets management (see the security-audit skill); OAuth/OIDC flows (see auth-implementation).
Workflow
- Find the sinks. Search the codebase for the dangerous APIs in the table below. Each hit needs a reason it is safe or a fix.
- Trace the data reaching each sink. If any part comes from a user, URL, storage, API response or
postMessage, treat it as untrusted. - Prefer safe rendering. Use text bindings (
{value},{{ value }},textContent) so the framework escapes output. Only fall back to HTML insertion when the feature truly needs markup. - Sanitize unavoidable HTML with a vetted library (for example DOMPurify) right before insertion, on the client, with a restrictive allowlist. Never write your own regex sanitizer.
- Validate URLs before putting them in
href,src,actionorlocation: parse withnew URL()and allow onlyhttp:,https:(andmailto:if needed). This blocksjavascript:URLs. - Contain with CSP. Roll out a nonce- or hash-based policy in report-only mode first. Read references/csp-rollout.md when adding or tightening a CSP.
- Check tokens, cookies and CSRF using the tables below.
- Review third parties and the bundle (SRI, public env vars, source maps, dependencies).
- Before changing production headers or CSP enforcement, show the user the exact header and get confirmation: a wrong policy can break the whole site.
Dangerous sinks
| Framework | Sink | Safe alternative |
| DOM | innerHTML, outerHTML, insertAdjacentHTML, document.write | textContent, createElement + append |
| DOM | eval, new Function, setTimeout("string") | Pass functions, never strings |
| React | dangerouslySetInnerHTML; href={userUrl} | Render as children; validate URL scheme |
| Vue | v-html; :href="userUrl" | {{ }} interpolation; validate URL scheme |
| Angular | bypassSecurityTrustHtml / Url / ResourceUrl / Script / Style | Let Angular's built-in sanitizer handle [innerHTML]; don't bypass |
| Svelte | {@html ...} | Normal {value} bindings |
| jQuery | .html(), $(userString) | .text() |
Also check:
- Server-rendered JSON in
<script>tags. A value containing</script>breaks out. Serialize with a library that escapes<(as\u003c), not plain string interpolation. - Markdown renderers. Turn off raw HTML in the renderer or sanitize its output.
- User-uploaded SVG. It can carry script. Don't render it inline on your origin (see file-storage-uploads).
Where to keep tokens
| Option | XSS can steal it? | Notes |
localStorage / sessionStorage | Yes, any script on the page can read it | Avoid for access or refresh tokens |
| JS memory only | Harder (lost on reload) | OK for short-lived access tokens; refresh through an HttpOnly cookie |
HttpOnly; Secure; SameSite cookie | No (script can't read it) | Needs CSRF protection |
| Backend-for-frontend (BFF) | No: tokens stay on the server | Recommended for SPAs handling sensitive data |
An XSS bug can still use a cookie session by making requests from the page. HttpOnly limits theft, not abuse, so preventing XSS still comes first.
Cookies and CSRF
- Set session cookies as
HttpOnly; Secure; SameSite=Lax(orStrict) withPath=/. Prefer the__Host-name prefix, which requiresSecure,Path=/and noDomain. SameSitestops cross-site requests, but sibling subdomains count as the same site. If any subdomain can host untrusted content, also use CSRF tokens.- For state-changing requests with cookie auth, use one of: a synchronizer token, a double-submit token, or a required custom header (which forces a CORS preflight). Also check the
Originheader on the server. - Never perform state changes on
GET.
Other browser attack surfaces
| Risk | Control |
| Clickjacking | Content-Security-Policy: frame-ancestors 'none' (or 'self'); X-Frame-Options: DENY for old browsers |
postMessage | Receiver checks event.origin against an exact allowlist and validates the message shape; sender passes an explicit targetOrigin, never "*" with sensitive data |
Open redirect (?next=) | Allow only relative paths: resolve with new URL(next, location.origin) and require the same origin; reject //host and /\host |
window.open to untrusted URLs | Pass noopener (third argument) so the new page can't control yours |
| Third-party scripts | Each one runs with full page access. Load only what you need; use Subresource Integrity (integrity="sha384-..." crossorigin="anonymous") for fixed-version files on CDNs |
| Prototype pollution | Don't deep-merge untrusted JSON into objects; use Object.create(null) or Map for user-keyed data |
Trusted Types
Content-Security-Policy: require-trusted-types-for 'script' makes the browser reject raw strings at DOM XSS sinks. Code must pass objects created by a named policy (for example a policy that calls DOMPurify). It is Baseline since early 2026 (Chromium, Firefox and Safari); older browsers ignore it, so check your support matrix. Roll it out in report-only mode like the rest of the CSP.
The bundle is public
- Anything in client code is readable: API keys, feature flags, internal URLs, comments.
- Only variables with the framework's public prefix reach the browser (
NEXT_PUBLIC_,VITE_,PUBLIC_in SvelteKit and Astro,REACT_APP_in Create React App). Never put a secret behind one of those prefixes. - Don't serve source maps publicly in production unless you have decided the source is public. Upload them privately to your error tracker instead.
- Client-side checks (hidden buttons, route guards) are UX, not security. The server must enforce every permission.
Dependency supply chain
- Commit the lockfile and install with
npm ci(orpnpm install --frozen-lockfile,yarn install --immutable) in CI. - Run the ecosystem's audit in CI and review new dependencies before adding them: maintainers, download history, install scripts, and name typos.
- Consider
--ignore-scriptsfor installs where packages don't need lifecycle scripts. - See the dependency-upgrades skill for upgrade workflow.
Anti-patterns
- "We sanitize on the server, so innerHTML is fine." Server sanitization is tied to one output context. Sanitize at the sink, for that context.
- Regex or blocklist sanitizers. Browsers parse HTML in surprising ways. Use a maintained library.
- CSP with
'unsafe-inline'and'unsafe-eval'inscript-src. It gives almost no XSS protection. - Allowlisting whole CDNs in
script-src. Public CDNs host scripts that can be abused to bypass the policy. Use nonces with'strict-dynamic'instead. - Tokens in
localStorage"because cookies are hard". - Hiding admin features in the UI without server checks.
Done when
- Every dangerous sink found has either been replaced or has sanitization and a comment explaining why it is safe.
- URLs from untrusted sources are scheme-validated before use.
- Tokens are not in web storage; cookie sessions have CSRF protection.
- A CSP is at least running in report-only mode with reports being collected, and
frame-ancestorsis set. - No secrets in client bundles; source maps are not public unless intended.
References
- references/csp-rollout.md: read when adding, tightening or debugging a Content Security Policy.
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.

