Use when reviewing UI for accessibility — WCAG 2.2 AA, keyboard nav, focus, ARIA, contrast, screen-reader semantics — even on 'is this a11y-OK?' or 'mach das barrierefrei'.
日本語の概要は準備中です。原文の説明を表示しています。
Writing/reviewing client-side UI (React/Vue/vanilla) — insecure-render + client-trust gaps AI ships: XSS via innerHTML, client secrets, client-only auth, CORS wildcard, token in localStorage
インストールする前に、エージェントに与えられる指示の中身を確認できます。
AI optimizes for the shortest code that produces the requested visible behavior and omits the invisible defensive layer — the sanitizer, the origin check, the server-side gate, the security header. Large samples put XSS in a majority of AI frontend code and ~2.74× more XSS than human code; every agent in the Tenzai benchmark shipped client-side flaws. These are high-precision, grep-catchable patterns — stop them at authoring time.
postMessage..tsx / .jsx / .vue / .svelte / .html or client-side .js / .ts.Do NOT use when: the change is server-only, CLI, or non-UI — route to security / security-sensitive-stop.
NEVER RENDER NON-CONSTANT INPUT INTO AN HTML/JS SINK WITHOUT ENCODING OR SANITIZING.
THE CLIENT IS UNTRUSTED — EVERY CLIENT GATE NEEDS A SERVER-SIDE TWIN.
NO SECRET, PRIVILEGED KEY, OR SESSION TOKEN LIVES IN CLIENT CODE OR localStorage.
postMessage reads, secret/token usage.| Pattern | Why it's dangerous | Do instead |
|---|---|---|
dangerouslySetInnerHTML / v-html / .innerHTML = on non-constant value | Stored/DOM XSS (CWE-79) | render as text, or sanitize (DOMPurify) if HTML is required |
Secret / API key inline or in NEXT_PUBLIC_* / VITE_* | Bundler inlines it into shipped JS (CWE-798) | call the third party via a backend-for-frontend proxy; only truly-public values get a public prefix |
| Client-side-only auth / role / validation | Bypassable with dev tools or a proxy (CWE-602) | client gate is UX only; enforce the same check server-side |
location.* / query param → HTML or JS sink | DOM XSS (CWE-79) | textContent; never pass URL data into an HTML/exec sink |
CORS origin: '*' (esp. with credentials) | Any site reads authenticated responses (CWE-942) | explicit origin allow-list; never reflect Origin on credentialed routes |
Token / JWT in localStorage / sessionStorage | XSS-exfiltratable (CWE-522) | HttpOnly; Secure; SameSite cookie |
addEventListener('message', …) without event.origin check | Any embedder drives the handler (CWE-346) | strict-equality check event.origin against an allow-list first |
redirect / next param → location / router.push | Open redirect / phishing (CWE-601) | relative-path or allow-listed-host only; reject external / non-http schemes |
eval / new Function / string-setTimeout on input | Eval injection / RCE (CWE-95) | a parser or explicit allow-list |
target="_blank" without rel="noopener" | Reverse tabnabbing (CWE-1022) | add rel="noopener noreferrer" |
| Missing CSP; missing alt/label/contrast | No XSS second line of defense; a11y failures | strict CSP (no unsafe-inline); alt text, labels, AA contrast |
Untrusted SVG markup into an HTML sink — innerHTML / v-html / dangerouslySetInnerHTML, or inlined server-side | An inlined SVG is live DOM, not an image: on* event attributes, <script>, foreignObject, a javascript: href and external references all execute, and a DOCTYPE with entities reads local files (CWE-79, CWE-611) | sanitize with an SVG profile chosen for the delivery context — inline DOM runs script and handlers in the host origin; a standalone .svg opened directly runs them in its own; <img> and CSS background-image run no script and load no external reference — or deliver through <img>, with CSP as the second line. Uploads are threat-modeling's data/threats.csv:9, not this row |
A payload assembled server-side and serialised into the page for a client island is a data-exposure surface: whatever is put in it is readable by anyone who can load the page — no API call, no auth check, view-source. Several mainstream frameworks ship their own named channel for exactly this, so the pattern is a documented mechanism rather than one project's arrangement.
Inspect the existing payload before judging any field. Read what the channel already carries and check current call sites for what the client actually consumes — a field is only defensible against what the page needs, and a review that never opened the payload is a guess. The grep below finds it.
The per-field question, which is what this section is:
Would I return this field from a public, unauthenticated endpoint? If not, it does not belong in the payload.
Field classes that recur and are almost always wrong there: internal ids that enumerate other users' rows · permission and role structures the client only needs a boolean of · feature-flag payloads carrying unreleased product names · anything from an admin-scoped query that was convenient to reuse · a whole serialised model where the page needs three of its fields.
Nothing enforces this, and the split is deliberate.
| Half | What it does | What it cannot do |
|---|---|---|
| the grep below | locates a payload a field was added to | tell a privileged field from a public one |
| this checklist entry | carries the judgement | run |
The question is not decidable by any check: a grep over the framework-named
payload channels cannot read intent. So the deterministic half is discovery,
advisory and non-blocking, and its whole value is putting the question in front
of a reader who would otherwise never see it. enforced_by: none is the honest
field here — this section refuses nothing.
What may NOT be encoded. The particular channel any one repository uses. What is encoded is the question and the framework-named channels; a concrete in-house payload shape would be one repository's implementation shipped as guidance.
Run before committing frontend changes; each should return zero (or every hit is read and justified):
# Insecure render + eval sinks
rg -n 'dangerouslySetInnerHTML|v-html|\.innerHTML\s*=|document\.write\(|\beval\(|new Function\('
# Client secrets / token storage
rg -n 'NEXT_PUBLIC_.*(SECRET|KEY|TOKEN|PASSWORD)|VITE_.*(SECRET|KEY)|localStorage\.setItem\([^)]*[Tt]oken'
# Wildcard CORS + unchecked postMessage + open redirect
rg -n "origin:\s*['\"]\*['\"]|Access-Control-Allow-Origin.*\*"
rg -n "addEventListener\(\s*['\"]message['\"]" # then confirm each checks event.origin
# Unhardened external links
rg -n 'target=["'\'']_blank["'\'']' # then confirm rel="noopener" present
# SVG active content — markup that executes once the SVG is inlined.
# Scoped to *.svg, where an on* attribute is always a defect. In .jsx/.tsx an
# on* attribute is ordinary React and matching it there would bury the signal;
# an on* on INLINE svg inside a host document is DQ4's case, not this grep's.
# <script is listed because the row above names it and the pattern did not.
rg -n '<foreignObject|<script[\s>]|\son[a-z]+\s*=|(?:xlink:)?href\s*=\s*["'\'']?\s*javascript:' -g '*.svg'
# Server-composed bootstrap payload — LOCATES it; the per-field judgement is yours
rg -n '__NEXT_DATA__|__NUXT__|window\.__INITIAL_STATE__|@json\(|json_encode\([^)]*\)\s*\)?\s*</script>|dehydratedState'
clean / hit at file:line → fixed by <change>)._blank to noopener, but legacy/embedded webviews do not — keep the rel for portability.NEXT_PUBLIC_/VITE_ on a genuinely public value (a publishable analytics ID) is fine; the violation is a sensitive name behind that prefix. Read the name, don't blanket-block the prefix.dangerouslySetInnerHTML/v-html/innerHTML without a sanitizer.origin: '*' on a credentialed endpoint.ai-code-blindspots — the surface→controls checklist that routes here.senior-engineering-discipline — anchor rule.security, defense-in-depth, accessibility-auditor, secrets-management.まだレビューはありません。使ってみた感想をお寄せください。
概要と使いどころ
Use when reviewing UI for accessibility — WCAG 2.2 AA, keyboard nav, focus, ARIA, contrast, screen-reader semantics — even on 'is this a11y-OK?' or 'mach das barrierefrei'.
日本語の概要は準備中です。原文の説明を表示しています。
Use when defining or auditing the activation event — aha-moment selection, retention correlation, falsifiable definition. Triggers on 'what is our aha moment', 'redefine activation'.
日本語の概要は準備中です。原文の説明を表示しています。
Use when capturing an architectural decision — file naming, next ADR number, Status / Context / Decision / Consequences, index regen; fires even without saying 'ADR'.
日本語の概要は準備中です。原文の説明を表示しています。
Adversarial critique — devil's advocate, stress-test, honest teardown ('poke holes', 'be brutal', 'was hältst du davon'); explicit request only. Routine code or design review → code-review.
日本語の概要は準備中です。原文の説明を表示しています。
Use when reading, creating, or updating agent documentation, module docs, roadmaps, or AGENTS.md. Understands the full .augment/, agents/, and copilot-instructions structure.
日本語の概要は準備中です。原文の説明を表示しています。
Use for an adversarial red-team / blue-team / auditor review of an AI agent's CONFIG + behaviour (rules, skills, MCP, hooks, permissions) — attack-chain → defensive-gap list, not a code audit.
日本語の概要は準備中です。原文の説明を表示しています。