Browser-side hardening: XSS and safe text binding, per-sink URL policy, DOM clobbering, nonce-based CSP, Trusted Types, subresource integrity, iframe capability minimization, postMessage validation, and where client state may live. Use when generating HTML, JSX, Vue, or Svelte templates, setting response headers in a web app, embedding third-party scripts or frames, or storing anything client-side.
Scanned 9/6/2026
Install to Claude Code
npx -y skills add ShieldNet-360/secure-vibe --skill frontend-security --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Frontend Security?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/shieldnet-360-frontend-security)More formats (shields.io, HTML) on the badges page.
---
name: frontend-security
description: "Browser-side hardening: XSS and safe text binding, per-sink URL policy, DOM clobbering, nonce-based CSP, Trusted Types, subresource integrity, iframe capability minimization, postMessage validation, and where client state may live. Use when generating HTML, JSX, Vue, or Svelte templates, setting response headers in a web app, embedding third-party scripts or frames, or storing anything client-side."
---
<!-- Native skill bundle for agent-skills (cross-tool convention). Generated by `secure-vibe dev regenerate`. -->
<!-- Do not edit by hand; the source of truth is skills/frontend-security/SKILL.md. -->
# Frontend Security
Browser-side hardening: XSS and safe text binding, per-sink URL policy, DOM clobbering, nonce-based CSP, Trusted Types, subresource integrity, iframe capability minimization, postMessage validation, and where client state may live. Use when generating HTML, JSX, Vue, or Svelte templates, setting response headers in a web app, embedding third-party scripts or frames, or storing anything client-side.
## ALWAYS
- Render untrusted data through the framework's ordinary text interpolation, which escapes for the output context. Do not reach for a raw-HTML API merely to display user data. Where rich HTML genuinely must be rendered, put it through a maintained sanitizer (DOMPurify) with an explicit allowlist first.
- Validate URL-bearing attributes **per sink**, not against one global scheme list. A navigation target (`href`, `action`, `formaction`, an `<iframe src>`) and an image source do not share a trust model: `javascript:` is never acceptable anywhere, while a narrowly constrained `data:image/…` or `blob:` may be exactly what an image sink requires. Decide the permitted schemes for each sink and check against that list.
- Read security-sensitive configuration and control-flow values from lexical variables or an object you own — never from a named `window` / `document` property (`window.config`, `document.redirectTo`, an implicit global). The browser exposes elements by `id` and `name` as named properties, so injected markup can shadow the value your code expected. An explicit `getElementById()` lookup is not the problem; trusting an ambient global is. Type-check what you read before using it in a privileged way.
- Build the `Content-Security-Policy` around a **nonce or hash as the trust root**: `script-src 'nonce-{value}' 'strict-dynamic'; object-src 'none'; base-uri 'none'`. Keeping `'self'` beside the nonce means every same-origin script is still trusted — that is a host-allowlist policy wearing a nonce, which may be the right trade-off but is not a strict CSP, so do not call it one. Generate the nonce from a CSPRNG afresh for **every HTML response**, and apply it only to scripts the server itself authorizes.
- Where you deploy Trusted Types, enforce `require-trusted-types-for 'script'` **and** restrict which policies may exist with the `trusted-types` directive. The first makes DOM sinks demand a typed value; without the second, any code can mint a pass-through policy (`createHTML: s => s`) and the guarantee is gone. Keep policies few, named, and centrally reviewed.
- Load third-party scripts and stylesheets from **version-pinned, immutable** URLs with `integrity="sha384-…"` and `crossorigin="anonymous"`. A hash pinned against a mutable URL breaks the page the first time the provider ships a legitimate update, which is how integrity checks come to be quietly deleted. If a provider cannot offer stable bytes with CORS, self-host rather than drop SRI.
- Sandbox every `<iframe>`, starting from **no** capability tokens and adding only what the embedded content needs. Do not combine `allow-scripts` with `allow-same-origin`: together they let the framed document reach the parent and remove its own `sandbox` attribute, so the sandbox stops meaning anything. If the content genuinely needs both, isolate it on a separate origin instead.
- On `postMessage`, name an explicit `targetOrigin` when sending — never `*` — and on receipt verify `event.origin` against an allowlist, validate the message's shape before reading any field, and where the channel expects one particular frame, check `event.source` as well. Origin, schema and sender are three separate checks.
- Set `X-Content-Type-Options: nosniff`, `Referrer-Policy: strict-origin-when-cross-origin` or stricter, `Permissions-Policy` dropping unused features, and `Cross-Origin-Opener-Policy: same-origin` to sever the opener relationship at the document level. Remove framework version banners here too (`X-Powered-By`, a detailed `Server`). Note that `no-referrer-when-downgrade` is **weaker** than the current browser default — configuring it is a step backwards.
- Send HSTS on production HTTPS. Add `includeSubDomains` only after confirming every subdomain serves HTTPS, and `preload` only as a deliberate, hard-to-reverse commitment for the registrable domain: the token makes a domain *eligible* for the preload list, it does not enrol it, and reversing it is slow.
- Keep authentication secrets out of JavaScript-readable storage — no access tokens, refresh tokens, JWTs, or sensitive personal or business data in `localStorage` or `sessionStorage`, where a single XSS reads all of it — and clear authenticated client-side state on logout. Server-issued session cookies are the alternative: `Secure`, `HttpOnly`, an application-appropriate `SameSite`, and the `__Host-` prefix for host-only cookies at `Path=/`. `auth-security` owns issuing them; this rule exists so a frontend review recognises an auth value that has escaped into JavaScript's reach.
## NEVER
- Use `dangerouslySetInnerHTML`, `v-html`, `{@html …}`, `innerHTML =`, or `document.write` with untrusted input.
- Use `eval`, `new Function`, `setTimeout(string)`, or `setInterval(string)`.
- Read or write `document.cookie` from JavaScript for an auth cookie — it should be `HttpOnly`, which means JavaScript cannot see it and code that does is reaching for a cookie that was never hardened.
- Treat **tightening the HTML sanitizer** as the fix when the exploit rides on content the sanitizer allows **by design** — a valid link, an `<img src>`, a permitted attribute. The sanitizer is working. The vulnerable behaviour is downstream, where some sink turns that allowed value into a navigation, a command, or a native capability. Fix the sink and reduce its authority.
## KNOWN FALSE POSITIVES
- Internal admin tools rendering Markdown or rich text from trusted authors may use a raw-HTML API after a sanitizer pass; document the sanitizer call inline.
- A deliberately **sandboxed** browser-extension page may use a more permissive CSP for code that needs eval-like behaviour, provided it stays isolated from the extension APIs. That is not a licence to add `'unsafe-eval'` to MV3 `extension_pages` — Chrome rejects that policy outright, and WebAssembly has its own `'wasm-unsafe-eval'` token.
- A WebSocket to a non-same-origin endpoint where the server validates `Origin`.
- A native or WASM decoder that is merely registered or configured is not yet an attack surface. Before flagging one, establish a reachable runtime path from untrusted input to that decoder — including an implementation fetched lazily at runtime, which does not have to be in the shipped bundle to be reachable.
## Reference files
Read these only when the task calls for them.
- `references/browser-controls.md`
- `references/verifying-findings.md`
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!