跳到正文
cccskills
免费在 GitHub 公开

web-frontend-security

Use when the diff renders user-controlled content as HTML, builds links or redirects, changes CSP or CORS, adds a cookie-authenticated state change or WebSocket, a postMessage handler, a markdown renderer, a service worker, browser storage of tokens or private data, a frameable sensitive action, or an Electron/Tauri window

中文简介正在准备中。当前显示原文说明。

查看安装方法

包含的文件(1)

  • SKILL.md10.0 KB

SKILL.md(原文)

安装前可以查看交给 AI 代理的指令内容。

Web Frontend Security

Overview

Modern template engines and UI frameworks escape by default. XSS comes back through the doors that switch escaping off, through the wrong escaper for the context, and through URLs. CSRF comes back whenever cookies authenticate a state change.

Core principle: find the escape hatch, then trace what flows into it. {value} in JSX is the control working — not a finding.

1. XSS Sinks by Framework (CWE-79)

Framework / engineEscape hatches to trace
React / Preact / SoliddangerouslySetInnerHTML, innerHTML props, refs writing el.innerHTML
Vuev-html, render functions setting innerHTML
AngularbypassSecurityTrustHtml/Url/ResourceUrl/Script/Style ([innerHTML] alone is sanitised)
Svelte{@html …}
DOM / jQueryinnerHTML, outerHTML, insertAdjacentHTML, document.write, .html(), $(userString), iframe.srcdoc
Jinja2 / Django`
Gotemplate.HTML(userValue), text/template used to render HTML
JVMThymeleaf th:utext, JSP <%= %> or escapeXml="false", FreeMarker ?no_esc
Node templatesHandlebars {{{ }}}, EJS <%- %>, Pug !=
.NET@Html.Raw, MarkupString
// ❌ user-authored bio rendered as HTML
<div dangerouslySetInnerHTML={{ __html: user.bio }} />
// ✅ sanitised with an allowlist sanitiser, or rendered as text
<div dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(user.bio) }} />

Markdown renderers count as HTML sinks when raw HTML is enabled: marked without a sanitiser, markdown-it with html: true, react-markdown with rehype-raw and no rehype-sanitize.

URLs are sinks too. href, src, formaction, location.href = …, window.open(…) with user data accept javascript: (and data: for some sinks). Validate the scheme (https:, mailto:) rather than relying on the framework's version-dependent handling.

<!-- ❌ profile website link -->
<a :href="user.website">site</a>
<!-- ✅ -->
<a :href="safeHttpUrl(user.website)">site</a>   <!-- returns "#" unless new URL(v).protocol is http: or https: -->

Wrong escaper for the context: HTML-escaping a value placed inside a <script> block, an event-handler attribute or a CSS value does not make it safe. Data embedded into an inline script needs JSON serialisation that escapes < (</script> breaks out).

Stored XSS (one user's content shown to another) → HIGH. Reflected → MEDIUM to HIGH by reach. Self-XSS → not a finding.

2. CSRF (CWE-352)

A finding when all hold: the endpoint changes state, the browser authenticates it automatically (cookies, HTTP auth), and nothing ties the request to the app's own pages (CSRF token, SameSite=Lax/Strict on the session cookie with no state-changing GET, an Origin/Sec-Fetch-Site check, a custom-header requirement the CORS policy enforces).

  • APIs authenticated only by an Authorization: Bearer header are not CSRF-able.
  • A new state-changing GET route is CSRF-able even with SameSite=Lax.
  • Removing or bypassing the CSRF middleware for a route (csrf_exempt, ignoringRequestMatchers) is a removed guard.

3. CORS

// ❌ any site can read authenticated responses: the request's Origin is reflected with credentials
res.setHeader("Access-Control-Allow-Origin", req.headers.origin);
res.setHeader("Access-Control-Allow-Credentials", "true");
// ✅ exact allowlist
if (ALLOWED_ORIGINS.has(req.headers.origin)) res.setHeader("Access-Control-Allow-Origin", req.headers.origin);

Access-Control-Allow-Origin: * without credentials is fine for public data; browsers refuse * with credentials. Watch for null origin allowed and suffix-matched origins (ssrf-and-url-allowlists has the matching pitfalls).

4. postMessage (CWE-346)

// ❌ any window can send a "token" message
window.addEventListener("message", (e) => { localStorage.setItem("token", e.data.token); });
// ✅
window.addEventListener("message", (e) => { if (e.origin !== "https://auth.example.com") return; … });

Also target.postMessage(secret, "*") — any framing or opening page can receive it.

5. CSP

A missing CSP is not reported. Adding 'unsafe-inline', 'unsafe-eval', a wildcard source or a JSONP-capable CDN to an existing policy is a weakened control: a note, or part of the finding when the same diff adds an injection sink.

6. Open Redirect (CWE-601) — high confidence only

Report only with a concrete chain: a post-login ?next= that can point off-site in an OAuth or SSO flow (code or token leakage), or a redirect used to pass an SSRF allowlist. A bare redirect on a marketing page is dropped.

7. Desktop Shells (Electron, Tauri)

In a desktop shell, renderer XSS can become code execution. This section covers the window and renderer settings; the main-process IPC handlers, URL schemes and loopback servers are desktop-and-local-ipc-security.

  • Electron: nodeIntegration: true, contextIsolation: false, sandbox: false, webSecurity: false; a preload exposing ipcRenderer or require wholesale through contextBridge; shell.openExternal(url) with an unvalidated scheme; IPC handlers that run commands or read paths from renderer arguments without validation; remote content loaded into a privileged window.
  • Tauri: broad allowlist/capabilities (shell open, fs scopes of **), dangerousRemoteDomainIpcAccess.

8. Cross-Site WebSocket Hijacking (CWE-1385)

A WebSocket upgrade carries the browser's cookies and no CORS check. A handshake authenticated by cookie that does not verify Origin against an exact allowlist (or require a token the page must fetch first) lets any site open the socket as the victim and read or send messages.

// ❌ cookie session, any origin
wss.on("connection", (ws, req) => { const user = sessionFrom(req.headers.cookie); … });
// ✅
const ok = ALLOWED_ORIGINS.has(req.headers.origin ?? "");
if (!ok) { ws.close(1008); return; }

Not a finding when the socket authenticates with a token sent in the first message or a header the page cannot be made to send.

9. Service Workers

  • The script URL passed to navigator.serviceWorker.register built from input, or served from a path user content can occupy; a Service-Worker-Allowed header that widens the scope beyond what the worker needs.
  • A fetch handler that caches personalized or authenticated responses without the user in the cache name or key, and serves them after logout or an account switch — or as the offline fallback for another user.
  • importScripts of a third-party URL without integrity control.

10. Browser Storage and Account Switching

A token in localStorage is not a finding by itself (authn-sessions-and-tokens §3). It is when a realistic lower-authority reader exists, or when the data outlives the session: private responses, drafts or auth decisions left in localStorage, IndexedDB or Cache Storage after logout and shown to the next account on a shared machine; a storage event or BroadcastChannel message that carries a token or an auth command between tabs without binding it to the current session.

11. DOM Clobbering and Prototype-Pollution Gadgets

Both need two halves in the trace. DOM clobbering: markup the attacker controls that keeps an id or name attribute (a sanitizer that allows them) and code that reads the clobbered global as configuration (window.config || defaults, document.getElementById("cdn").href for a script URL). Prototype pollution: an attacker-chosen key reaching a recursive merge or path setter and a gadget that reads the polluted property in a security decision, a script URL or an HTML sink (node-typescript-security-pitfalls §5).

12. Clickjacking

A frameable page with a one-click state change — confirming a payment, granting an OAuth scope, deleting an account, changing an email — and no frame-ancestors or X-Frame-Options. Missing frame headers on read-only or multi-step pages are hardening, not reported.

Common Mistakes

  • Flagging {user.name} in JSX, {{ name }} in Vue/Jinja, or [innerHTML] in Angular.
  • Reporting CSRF on a bearer-token API.
  • Reporting a missing CSP, X-Frame-Options or HSTS as a vulnerability.
  • Reporting an open redirect with no chain.

Red Flags

  • An escape hatch whose input traces back to another user's content.
  • DOMPurify imported but the sink fed the unsanitised variable.
  • Access-Control-Allow-Credentials: true next to a reflected origin.
  • addEventListener("message" with no origin comparison.
  • An Electron BrowserWindow created with nodeIntegration: true or loading remote URLs.
  • A WebSocket server that reads the session cookie and never reads Origin.
  • A service worker that cache.puts responses from authenticated API routes.
  • A sanitizer configured to keep id/name next to code that reads window.<name>.

References (names and links only)

OWASP Top 10:2025 A05 Injection, A01 Broken Access Control · ASVS 5.0 V3 Web Frontend Security · CWE-79, 346, 352, 601, 942, 1021, 1321, 1385 · Electron security checklist

评价

还没有评价。欢迎分享你的使用感受。

评论

无需登录即可发表。欢迎分享使用感受、提问,或说明在哪些环境下无法运行。

还没有评论。来写第一条吧。

提交后会立即公开。请勿填写邮箱、电话等个人信息。URL 最多 2 个。

0 / 1000 字

此表单受 reCAPTCHA 保护,适用 Google 的隐私权政策和服务条款。

同一仓库的技能

Use when writing acceptance criteria for a task - express each as an observable Given/When/Then that QA can execute, including negative cases

中文简介正在准备中。当前显示原文说明。

makifbaysal/tasktrooper1172026年10月21日 更新

Use when the diff adds or changes an endpoint, resolver, RPC, job or query that takes an object id, a role check, a request binding or a tenant filter - BOLA/IDOR, function-level authorization, mass assignment and tenant scoping

中文简介正在准备中。当前显示原文说明。

makifbaysal/tasktrooper1172026年10月21日 更新

Use on every UI change - semantic HTML, labels for controls, keyboard-navigable dialogs/menus, visible focus, and never color as the only signal

中文简介正在准备中。当前显示原文说明。

makifbaysal/tasktrooper1172026年10月21日 更新

Use when a task changes any screen, form, dialog, menu or control - Lighthouse/axe scan of the changed screens, a keyboard walk, and the thresholds that fail a task

中文简介正在准备中。当前显示原文说明。

makifbaysal/tasktrooper1172026年10月21日 更新

How to work a task returned with review, QA or UAT findings. Use when a task is in need_revision or PR review comments are in your context.

中文简介正在准备中。当前显示原文说明。

makifbaysal/tasktrooper1172026年10月21日 更新

Use when the repository keeps its alerting in code, or a person asks for alerts outside TaskTrooper - PrometheusRule YAML, a Cloud Monitoring alert policy in Terraform, a Datadog monitor, each routed to TaskTrooper's alert ingest URL so it opens an incident here

中文简介正在准备中。当前显示原文说明。

makifbaysal/tasktrooper1172026年10月21日 更新

查看 makifbaysal 的全部技能

报告此技能的问题