@runsec/mcp 1.0.35 → 1.0.37
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/data/.rag-cache.json +1 -0
- package/dist/data/skills/_exploit_overrides.json +16 -0
- package/dist/data/skills/advanced-agent-cloud/index.md +94 -0
- package/dist/data/skills/advanced-agent-cloud/patterns.md +46 -0
- package/dist/data/skills/advanced-agent-cloud/skill.json +38 -0
- package/dist/data/skills/app-logic/index.md +69 -0
- package/dist/data/skills/app-logic/patterns.md +23 -0
- package/dist/data/skills/app-logic/skill.json +24 -0
- package/dist/data/skills/auth-keycloak/index.md +69 -0
- package/dist/data/skills/auth-keycloak/patterns.md +46 -0
- package/dist/data/skills/auth-keycloak/skill.json +51 -0
- package/dist/data/skills/browser-agent/index.md +58 -0
- package/dist/data/skills/browser-agent/patterns.md +15 -0
- package/dist/data/skills/browser-agent/skill.json +24 -0
- package/dist/data/skills/cloud-secrets/index.md +66 -0
- package/dist/data/skills/cloud-secrets/patterns.md +19 -0
- package/dist/data/skills/cloud-secrets/skill.json +28 -0
- package/dist/data/skills/csharp-dotnet/index.md +103 -0
- package/dist/data/skills/csharp-dotnet/patterns.md +270 -0
- package/dist/data/skills/csharp-dotnet/skill.json +27 -0
- package/dist/data/skills/desktop-vsto-suite/index.md +202 -0
- package/dist/data/skills/desktop-vsto-suite/patterns.md +154 -0
- package/dist/data/skills/desktop-vsto-suite/skill.json +26 -0
- package/dist/data/skills/devops-security/index.md +64 -0
- package/dist/data/skills/devops-security/patterns.md +23 -0
- package/dist/data/skills/devops-security/skill.json +42 -0
- package/dist/data/skills/domain-access-management/index.md +123 -0
- package/dist/data/skills/domain-access-management/patterns.md +58 -0
- package/dist/data/skills/domain-access-management/skill.json +36 -0
- package/dist/data/skills/domain-data-privacy/index.md +98 -0
- package/dist/data/skills/domain-data-privacy/patterns.md +48 -0
- package/dist/data/skills/domain-data-privacy/skill.json +36 -0
- package/dist/data/skills/domain-input-validation/index.md +210 -0
- package/dist/data/skills/domain-input-validation/patterns.md +158 -0
- package/dist/data/skills/domain-input-validation/skill.json +24 -0
- package/dist/data/skills/domain-platform-hardening/index.md +169 -0
- package/dist/data/skills/domain-platform-hardening/patterns.md +96 -0
- package/dist/data/skills/domain-platform-hardening/skill.json +27 -0
- package/dist/data/skills/ds-ml-security/patterns.md +137 -0
- package/dist/data/skills/fastapi-async/index.md +83 -0
- package/dist/data/skills/fastapi-async/patterns.md +329 -0
- package/dist/data/skills/fastapi-async/skill.json +32 -0
- package/dist/data/skills/frontend-react/index.md +26 -0
- package/dist/data/skills/frontend-react/patterns.md +226 -0
- package/dist/data/skills/frontend-react/skill.json +24 -0
- package/dist/data/skills/go-core/index.md +86 -0
- package/dist/data/skills/go-core/patterns.md +272 -0
- package/dist/data/skills/go-core/skill.json +22 -0
- package/dist/data/skills/hft-cpp-security/patterns.md +37 -0
- package/dist/data/skills/index.md +73 -0
- package/dist/data/skills/infra-k8s-helm/index.md +138 -0
- package/dist/data/skills/infra-k8s-helm/patterns.md +279 -0
- package/dist/data/skills/infra-k8s-helm/skill.json +41 -0
- package/dist/data/skills/integration-security/index.md +73 -0
- package/dist/data/skills/integration-security/patterns.md +132 -0
- package/dist/data/skills/integration-security/skill.json +30 -0
- package/dist/data/skills/java-enterprise/index.md +31 -0
- package/dist/data/skills/java-enterprise/patterns.md +816 -0
- package/dist/data/skills/java-enterprise/skill.json +26 -0
- package/dist/data/skills/java-spring/index.md +65 -0
- package/dist/data/skills/java-spring/patterns.md +22 -0
- package/dist/data/skills/java-spring/skill.json +23 -0
- package/dist/data/skills/license-compliance/index.md +58 -0
- package/dist/data/skills/license-compliance/patterns.md +12 -0
- package/dist/data/skills/license-compliance/skill.json +28 -0
- package/dist/data/skills/mobile-security/patterns.md +42 -0
- package/dist/data/skills/nodejs-nestjs/index.md +71 -0
- package/dist/data/skills/nodejs-nestjs/patterns.md +288 -0
- package/dist/data/skills/nodejs-nestjs/skill.json +24 -0
- package/dist/data/skills/observability/index.md +68 -0
- package/dist/data/skills/observability/patterns.md +22 -0
- package/dist/data/skills/observability/skill.json +26 -0
- package/dist/data/skills/php-security/patterns.md +202 -0
- package/dist/data/skills/ru-regulatory/index.md +72 -0
- package/dist/data/skills/ru-regulatory/patterns.md +28 -0
- package/dist/data/skills/ru-regulatory/skill.json +53 -0
- package/dist/data/skills/ruby-rails/index.md +65 -0
- package/dist/data/skills/ruby-rails/patterns.md +172 -0
- package/dist/data/skills/ruby-rails/skill.json +24 -0
- package/dist/data/skills/rust-security/patterns.md +152 -0
- package/dist/data/trufflehog-config.yaml +407 -0
- package/dist/index.js +3766 -372
- package/package.json +1 -1
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
{
|
|
2
|
+
"skill_id": "fastapi-async",
|
|
3
|
+
"name": "FastAPI / Async Security",
|
|
4
|
+
"activation_triggers": [
|
|
5
|
+
"fas-fastapi-route",
|
|
6
|
+
"fas-slowapi-limit",
|
|
7
|
+
"fas-encode-databases",
|
|
8
|
+
"fas-pydantic-schema"
|
|
9
|
+
],
|
|
10
|
+
"relevant_extensions": [
|
|
11
|
+
".py"
|
|
12
|
+
],
|
|
13
|
+
"tools": [
|
|
14
|
+
"semgrep",
|
|
15
|
+
"syft",
|
|
16
|
+
"trufflehog"
|
|
17
|
+
],
|
|
18
|
+
"rules_path": "core/skills/fastapi-async/patterns.md",
|
|
19
|
+
"few_shot_examples": "core/gold-standard-testbed/api_vulnerable.py",
|
|
20
|
+
"mitigation_logic": {
|
|
21
|
+
"RRC-007": {
|
|
22
|
+
"exception_rule": "Если в коде присутствует проверка debug-режима, а в k8s/configmap.yaml или Helm values явно зафиксировано DEBUG=false, finding по debug leaks должен считаться инфраструктурно закрытым.",
|
|
23
|
+
"status_override": "OK",
|
|
24
|
+
"evidence_requirements": [
|
|
25
|
+
"Наличие branch/guard по debug flag в runtime-коде.",
|
|
26
|
+
"Подтвержденное значение DEBUG=false в deployment-конфигурации (ConfigMap/Helm values)."
|
|
27
|
+
],
|
|
28
|
+
"rationale": "Риск утечки диагностической информации контролируется политикой окружения и отключением debug в production-контуре."
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
"security_priority": 5
|
|
32
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# Frontend React Security
|
|
2
|
+
|
|
3
|
+
## Stack overview
|
|
4
|
+
|
|
5
|
+
**TypeScript/React** security patterns for XSS/injection, client-side state leakage, component safety, hooks lifecycle races, and routing controls. Metrics are prefixed **`FR`**.
|
|
6
|
+
|
|
7
|
+
## Top threats
|
|
8
|
+
|
|
9
|
+
- XSS sinks (`dangerouslySetInnerHTML`, `innerHTML`, `srcDoc`, dynamic script injection).
|
|
10
|
+
- Token/PII leakage in Redux/Zustand/Context and insecure browser storage.
|
|
11
|
+
- Prototype pollution in deep merge utilities and unsafe object path writes.
|
|
12
|
+
- Open redirects via `useNavigate`/`window.location` and unsafe external links.
|
|
13
|
+
|
|
14
|
+
## Pattern catalog
|
|
15
|
+
|
|
16
|
+
Canonical detections and remediations are maintained in [`patterns.md`](patterns.md).
|
|
17
|
+
|
|
18
|
+
## Verification
|
|
19
|
+
|
|
20
|
+
Use `Vulnerable: <ID>` markers in:
|
|
21
|
+
|
|
22
|
+
- [`core/gold-standard-testbed/typescript/react_xss_danger.tsx`](../../gold-standard-testbed/typescript/react_xss_danger.tsx)
|
|
23
|
+
- [`core/gold-standard-testbed/typescript/state_secret_leak.ts`](../../gold-standard-testbed/typescript/state_secret_leak.ts)
|
|
24
|
+
- [`core/gold-standard-testbed/typescript/prototype_pollution_merge.ts`](../../gold-standard-testbed/typescript/prototype_pollution_merge.ts)
|
|
25
|
+
- [`core/gold-standard-testbed/typescript/client_redirect_vulnerable.tsx`](../../gold-standard-testbed/typescript/client_redirect_vulnerable.tsx)
|
|
26
|
+
- [`core/gold-standard-testbed/typescript/insecure_storage_wrapper.ts`](../../gold-standard-testbed/typescript/insecure_storage_wrapper.ts)
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
| ID | Название метрики | Anti-Pattern (Vulnerable Code/YAML) | Safe-Pattern (Remediation) | Stack | Источник fix_template | Exploit scenario |
|
|
2
|
+
|---|---|---|---|---|---|---|
|
|
3
|
+
| FR-001 | React XSS: untrusted `dangerouslySetInnerHTML` | `<div dangerouslySetInnerHTML={{ __html: userHtml }} />` | `const safe = DOMPurify.sanitize(userHtml);`<br>`<div dangerouslySetInnerHTML={{ __html: safe }} />` | TypeScript/React | `ASVS V5.1, CWE-79` | User-controlled HTML executes script in browser context. |
|
|
4
|
+
| FR-002 | React XSS: direct `innerHTML` assignment | `ref.current!.innerHTML = payload` | `ref.current!.textContent = payload`<br>`// or sanitize before HTML rendering` | TypeScript/React | `ASVS V5.1, CWE-79` | DOM sink executes injected markup/script payloads. |
|
|
5
|
+
| FR-003 | React XSS: `insertAdjacentHTML` with unsanitized input | `el.insertAdjacentHTML("beforeend", html)` | `el.insertAdjacentText("beforeend", html)`<br>`// or DOMPurify.sanitize(html)` | TypeScript/React | `ASVS V5.1, CWE-79` | HTML injection into trusted component region. |
|
|
6
|
+
| FR-004 | React XSS: writing unescaped value to `srcDoc` | `<iframe srcDoc={userProvided} />` | `<iframe srcDoc={DOMPurify.sanitize(userProvided)} sandbox="" />` | TypeScript/React | `ASVS V5.1, CWE-79` | Malicious script runs inside iframe content. |
|
|
7
|
+
| FR-005 | Insecure dynamic code execution via `eval()` | `const out = eval(expr)` | `const out = safeMathEvaluator(expr)`<br>`// strict parser/allowlist, no eval` | TypeScript/React | `ASVS V5.1, CWE-95` | Arbitrary code execution in browser runtime. |
|
|
8
|
+
| FR-006 | Insecure `new Function()` construction | `const fn = new Function("x", code)` | `const fn = compileAllowlistedRule(ast)` | TypeScript/React | `ASVS V5.1, CWE-95` | Runtime code compilation enables injection paths. |
|
|
9
|
+
| FR-007 | Insecure `setTimeout(string)` execution | `setTimeout("runUserAction()", 10)` | `setTimeout(() => runUserAction(), 10)` | TypeScript/React | `ASVS V5.1, CWE-95` | String-based timer evaluates attacker-influenced code. |
|
|
10
|
+
| FR-008 | Insecure `setInterval(string)` execution | `setInterval(userCode, 1000)` | `setInterval(() => poll(), 1000)` | TypeScript/React | `ASVS V5.1, CWE-95` | Repeated string evaluation expands injection impact. |
|
|
11
|
+
| FR-009 | Unescaped URL in `javascript:` href sink | `<a href={userLink}>go</a>` | `if (!isSafeHttpUrl(userLink)) throw new Error("bad url");`<br>`<a href={userLink}>go</a>` | TypeScript/React | `ASVS V5.1, CWE-79` | `javascript:` URI executes script when clicked. |
|
|
12
|
+
| FR-010 | CSS injection via untrusted style object | `<div style={{ backgroundImage: "url(" + userUrl + ")" }} />` | `const safeUrl = allowlistedImageUrl(userUrl);`<br>`<div style={{ backgroundImage: \`url(${safeUrl})\` }} />` | TypeScript/React | `ASVS V5.1, CWE-79` | Crafted style values bypass UI trust boundaries. |
|
|
13
|
+
| FR-011 | Ref-based sink writes HTML from query param | `contentRef.current!.innerHTML = searchParams.get("msg") || ""` | `contentRef.current!.textContent = searchParams.get("msg") || ""` | TypeScript/React | `ASVS V5.1, CWE-79` | URL parameter injects executable markup. |
|
|
14
|
+
| FR-012 | Third-party markdown render without sanitization | `<Markdown>{rawMd}</Markdown>` | `<Markdown rehypePlugins={[rehypeSanitize]}>{rawMd}</Markdown>` | TypeScript/React | `ASVS V5.1, CWE-79` | Embedded HTML/script in markdown reaches DOM. |
|
|
15
|
+
| FR-013 | State Leakage (Logic: strong): Redux stores access token in plain text | `state.auth.accessToken = token` | `state.auth.accessToken = undefined`<br>`// use HttpOnly secure cookie + BFF` | TypeScript/React/Redux | `ASVS V14.2, CWE-200` | Stolen runtime/state snapshot exposes bearer tokens. |
|
|
16
|
+
| FR-014 | State Leakage (Logic: strong): Redux stores full card PAN | `state.payment.pan = form.pan` | `state.payment.panLast4 = form.pan.slice(-4)` | TypeScript/React/Redux | `ASVS V14.2, CWE-200` | Sensitive payment data leaks via devtools/logs. |
|
|
17
|
+
| FR-015 | State Leakage (Logic: strong): Zustand stores API secret key | `set({ apiKey: "sk-live-..." })` | `set({ apiKey: undefined })`<br>`// secrets must never be in client state` | TypeScript/React/Zustand | `ASVS V14.2, CWE-200` | Secret leakage through memory dumps and extensions. |
|
|
18
|
+
| FR-016 | State Leakage (Logic: strong): Context provider exposes raw PII profile | `<AuthContext.Provider value={{ user }}>` | `<AuthContext.Provider value={{ user: publicUserView(user) }}>` | TypeScript/React/Context | `ASVS V14.2, CWE-200` | Child components can exfiltrate excessive user data. |
|
|
19
|
+
| FR-017 | State Leakage (Logic: strong): persisting auth state with redux-persist | `persistReducer({ key: "root", storage }, authReducer)` | `persistReducer({ key: "root", storage, blacklist: ["auth","payment"] }, rootReducer)` | TypeScript/React/Redux | `ASVS V14.2, CWE-200` | Tokens/PII remain on disk after logout/device theft. |
|
|
20
|
+
| FR-018 | State Leakage (Logic: strong): logging entire store to console | `console.log(store.getState())` | `console.log({ route: state.ui.route })`<br>`// never log auth/payment branches` | TypeScript/React | `ASVS V14.2, CWE-532` | Browser logs expose secrets to local observers. |
|
|
21
|
+
| FR-019 | State Leakage (Logic: strong): sending full state to telemetry | `capture("state_snapshot", store.getState())` | `capture("state_snapshot", redactState(store.getState()))` | TypeScript/React | `ASVS V14.2, CWE-200` | Telemetry backend receives secret-bearing snapshots. |
|
|
22
|
+
| FR-020 | Insecure `localStorage` for access token | `localStorage.setItem("access_token", token)` | `// store session in HttpOnly Secure SameSite cookie` | TypeScript/React | `ASVS V14.2, CWE-922` | XSS attacker extracts long-lived session token. |
|
|
23
|
+
| FR-021 | Insecure `sessionStorage` for refresh token | `sessionStorage.setItem("refresh_token", rt)` | `// use server-side refresh rotation + cookie` | TypeScript/React | `ASVS V14.2, CWE-922` | Refresh token theft enables session replay. |
|
|
24
|
+
| FR-022 | Insecure custom storage wrapper for secrets | `storage.save("jwt", token)` | `storage.save("jwt", "")`<br>`// disable secret persistence in Web Storage` | TypeScript/React | `ASVS V14.2, CWE-922` | Central wrapper spreads insecure secret storage pattern. |
|
|
25
|
+
| FR-023 | Prototype Pollution (Logic: strong): unsafe deep merge with user object | `deepMerge(config, userTheme)` | `deepMergeSafe(config, sanitizeKeys(userTheme, ["__proto__","constructor","prototype"]))` | TypeScript/React | `ASVS V14.3, CWE-1321` | Polluted prototype alters app-wide behavior and guards. |
|
|
26
|
+
| FR-024 | Prototype Pollution (Logic: strong): lodash `merge` on untrusted payload | `merge(target, payload)` | `merge(target, stripPrototypeKeys(payload))` | TypeScript/React | `ASVS V14.3, CWE-1321` | Crafted keys mutate object prototypes transitively. |
|
|
27
|
+
| FR-025 | Prototype Pollution (Logic: strong): recursive assign without key filtering | `for (const k in src) out[k] = merge(out[k], src[k])` | `for (const k of Object.keys(src)) { if (isDangerousKey(k)) continue; out[k] = mergeSafe(out[k], src[k]); }` | TypeScript/React | `ASVS V14.3, CWE-1321` | Recursive merge enables hidden key smuggling. |
|
|
28
|
+
| FR-026 | Prototype Pollution (Logic: strong): query-string parser writes to object path | `set(obj, reqPath, value)` | `if (containsDangerousPath(reqPath)) throw new Error("blocked");`<br>`set(obj, reqPath, value)` | TypeScript/React | `ASVS V14.3, CWE-1321` | Attacker controls dotted path into prototype chain. |
|
|
29
|
+
| FR-027 | Reverse tabnabbing: `target="_blank"` without rel | `<a target="_blank" href={url}>` | `<a target="_blank" rel="noopener noreferrer" href={url}>` | TypeScript/React | `ASVS V14.3, CWE-1022` | New tab can hijack `window.opener` of origin tab. |
|
|
30
|
+
| FR-028 | Reverse tabnabbing in dynamic link component | `<Link to={ext} target="_blank">` | `<Link to={ext} target="_blank" rel="noopener noreferrer">` | TypeScript/React | `ASVS V14.3, CWE-1022` | Dynamic external links leave opener channel exposed. |
|
|
31
|
+
| FR-029 | Missing prop validation in `FileUpload` size/type controls | `<FileUpload onUpload={onUpload} maxSize={props.maxSize as any} />` | `const safeMax = Number.isFinite(props.maxSize) ? Math.min(props.maxSize, 10_000_000) : 1_000_000;` | TypeScript/React | `ASVS V5.1, CWE-20` | Invalid props disable client-side upload guardrails. |
|
|
32
|
+
| FR-030 | Missing prop validation in `PaymentForm` amount/currency | `<PaymentForm amount={query.amount as any} currency={query.currency as any} />` | `const amount = clampAmount(parseAmount(query.amount));`<br>`const currency = allowlistedCurrency(query.currency);` | TypeScript/React | `ASVS V5.1, CWE-20` | Crafted props alter sensitive payment workflow logic. |
|
|
33
|
+
| FR-031 | Missing prop validation for callback URL prop | `<ConsentDialog returnUrl={params.returnUrl} />` | `<ConsentDialog returnUrl={safeInternalPath(params.returnUrl)} />` | TypeScript/React | `ASVS V5.1, CWE-20` | Unvalidated callback path leads to redirect abuse. |
|
|
34
|
+
| FR-032 | Missing prop schema in high-risk component | `function AdminPanel(props: any) { ... }` | `type AdminPanelProps = z.infer<typeof AdminPanelSchema>;`<br>`const safe = AdminPanelSchema.parse(props);` | TypeScript/React | `ASVS V5.1, CWE-20` | `any` props bypass type and runtime security checks. |
|
|
35
|
+
| FR-033 | `useEffect` infinite loop via unstable dependency object | `useEffect(() => { setState(calc()); }, [{ id }])` | `const depId = id; useEffect(() => { setState(calc()); }, [depId])` | TypeScript/React | `ASVS V14.3, CWE-400` | Render loop causes client-side resource exhaustion. |
|
|
36
|
+
| FR-034 | `useEffect` race updating stale async response | `useEffect(() => { fetchData(id).then(setData); }, [id])` | `useEffect(() => { let active = true; fetchData(id).then(r => active && setData(r)); return () => { active = false; }; }, [id])` | TypeScript/React | `ASVS V14.3, CWE-362` | Slow response overwrites state for newer context. |
|
|
37
|
+
| FR-035 | Missing abort controller in async effect | `useEffect(() => { api.get(url).then(setItems); }, [url])` | `useEffect(() => { const ac = new AbortController(); api.get(url, { signal: ac.signal }).then(setItems); return () => ac.abort(); }, [url])` | TypeScript/React | `ASVS V14.3, CWE-400` | Hanging requests accumulate and degrade availability. |
|
|
38
|
+
| FR-036 | State update after unmount in concurrent view | `promise.then(() => setLoading(false))` | `if (!mountedRef.current) return; setLoading(false);` | TypeScript/React | `ASVS V14.3, CWE-362` | Unmounted updates create inconsistent component state. |
|
|
39
|
+
| FR-037 | Client-side Open Redirect via `useNavigate` param | `navigate(searchParams.get("next") || "/")` | `navigate(safeInternalPath(searchParams.get("next")))` | TypeScript/React Router | `ASVS V14.2, CWE-601` | External redirect target can phish users post-login. |
|
|
40
|
+
| FR-038 | Open Redirect via `window.location` assignment | `window.location.href = returnUrl` | `window.location.assign(safeInternalPath(returnUrl))` | TypeScript/React | `ASVS V14.2, CWE-601` | Untrusted URL causes off-domain redirection. |
|
|
41
|
+
| FR-039 | Open Redirect in auth callback handler | `const to = qs.get("redirect_uri"); navigate(to!)` | `const to = safeOAuthRedirect(qs.get("redirect_uri")); navigate(to)` | TypeScript/React | `ASVS V14.2, CWE-601` | OAuth callback abused to redirect to attacker endpoint. |
|
|
42
|
+
| FR-040 | Insecure `postMessage` target origin wildcard | `window.opener?.postMessage(token, "*")` | `window.opener?.postMessage(tokenRef, "https://app.example.com")` | TypeScript/React | `ASVS V14.3, CWE-346` | Sensitive message leaks to untrusted origins. |
|
|
43
|
+
| FR-041 | Missing origin validation in message listener | `window.addEventListener("message", e => handle(e.data))` | `window.addEventListener("message", e => { if (e.origin !== TRUSTED_ORIGIN) return; handle(e.data); })` | TypeScript/React | `ASVS V14.3, CWE-346` | Malicious frame injects control messages. |
|
|
44
|
+
| FR-042 | Insecure dynamic script injection | `const s = document.createElement("script"); s.src = userUrl; document.body.appendChild(s)` | `const s = document.createElement("script"); s.src = allowlistedScriptUrl(userUrl); s.integrity = KNOWN_SRI; s.crossOrigin = "anonymous";` | TypeScript/React | `ASVS V5.1, CWE-829` | Untrusted script source leads to supply-chain injection. |
|
|
45
|
+
| FR-043 | Insecure iframe source from query parameter | `<iframe src={params.get("url") || ""} />` | `<iframe src={allowlistedFrameUrl(params.get("url"))} sandbox="allow-scripts allow-same-origin" />` | TypeScript/React | `ASVS V14.3, CWE-829` | Embedded attacker page pivots into app workflows. |
|
|
46
|
+
| FR-044 | Missing CSP nonce handling for inline scripts | `<script dangerouslySetInnerHTML={{ __html: inlineJs }} />` | `<script nonce={cspNonce}>{trustedInlineJs}</script>` | TypeScript/React | `ASVS V14.3, CWE-693` | Inline script execution bypasses expected CSP posture. |
|
|
47
|
+
| FR-045 | JWT decoded on client without signature trust model | `const claims = JSON.parse(atob(token.split(".")[1]))` | `const claims = parseJwtForDisplayOnly(token);`<br>`// authorization decisions must stay server-side` | TypeScript/React | `ASVS V14.2, CWE-345` | Client trusts forged claims for privileged UI paths. |
|
|
48
|
+
| FR-046 | Privileged UI gating only in frontend route guard | `if (user.role === "admin") return <Admin />` | `if (!serverEntitlements.includes("admin:panel:read")) return <Forbidden />` | TypeScript/React | `ASVS V14.2, CWE-285` | Client-only role checks are bypassable and misleading. |
|
|
49
|
+
| FR-047 | Sensitive token appended to URL query string | `navigate("/done?token=" + token)` | `navigate("/done");`<br>`sessionMemory.set("txn_ref", tokenRef)` | TypeScript/React | `ASVS V14.2, CWE-598` | URL leaks token to logs, history, and referrers. |
|
|
50
|
+
| FR-048 | Untrusted filename rendered without escaping in download UI | `<div>{dangerouslyRender(fileName)}</div>` | `<div>{fileName}</div>` | TypeScript/React | `ASVS V5.1, CWE-79` | File metadata can deliver stored XSS payload. |
|
|
51
|
+
| FR-049 | Weak client-side sanitizer wrapper with bypassable config | `sanitize(html, { ALLOWED_TAGS: false })` | `sanitizeStrict(html, { ALLOWED_TAGS: SAFE_TAGS, ALLOWED_ATTR: SAFE_ATTRS })` | TypeScript/React | `ASVS V5.1, CWE-79` | Misconfigured sanitizer allows dangerous payload through. |
|
|
52
|
+
| FR-050 | Insecure form parser allows prototype keys in payload (Logic: strong) | `const data = JSON.parse(raw); setForm({ ...form, ...data })` | `const data = sanitizeKeys(JSON.parse(raw), ["__proto__","constructor","prototype"]); setForm({ ...form, ...data })` | TypeScript/React | `ASVS V14.3, CWE-1321` | Prototype key injection mutates component behavior globally. |
|
|
53
|
+
| FR-051 | UI Redressing: frame-embeddable app without frame-busting policy | `<meta http-equiv="Content-Security-Policy" content="default-src 'self'">` | `<meta http-equiv="Content-Security-Policy" content="default-src 'self'; frame-ancestors 'none'">`<br>`// additionally set X-Frame-Options: DENY on server` | TypeScript/React | `ASVS V14.3, CWE-1021` | Untrusted parent iframe overlays/controls sensitive UI. |
|
|
54
|
+
| FR-052 | UI Redressing: sensitive dialog rendered without anti-clickjacking overlay guard | `<PaymentApproveModal open={open} />` | `<PaymentApproveModal open={open} requireTopWindow={window.top === window.self} />` | TypeScript/React | `ASVS V14.3, CWE-1021` | Embedded contexts trick users into approving hidden actions. |
|
|
55
|
+
| FR-053 | UI Redressing: iframe bridge accepts postMessage from any origin | `window.addEventListener("message", e => applyOverlayState(e.data))` | `window.addEventListener("message", e => { if (e.origin !== TRUSTED_ORIGIN) return; applyOverlayState(e.data); })` | TypeScript/React | `ASVS V14.3, CWE-1021` | Host page spoofs UI state and captures interactions. |
|
|
56
|
+
| FR-054 | UI Redressing: fullscreen-like overlay can be toggled by URL param | `const mask = search.get("mask") === "1"` | `const mask = false;`<br>`// overlay mode only from signed server feature flags` | TypeScript/React | `ASVS V14.3, CWE-1021` | Attacker links user to spoofed UI confirmation screen. |
|
|
57
|
+
| FR-055 | CSRF (Logic: strong): axios instance missing anti-CSRF header defaults | `axios.create({ withCredentials: true })` | `axios.create({ withCredentials: true, headers: { "X-Requested-With": "XMLHttpRequest", "X-CSRF-Token": getCsrfToken() } })` | TypeScript/React/Axios | `ASVS V14.2, CWE-352` | Browser auto-sends cookies on cross-site state-changing requests. |
|
|
58
|
+
| FR-056 | CSRF (Logic: strong): state-changing fetch without CSRF token header | `fetch("/api/transfer", { method: "POST", credentials: "include", body: payload })` | `fetch("/api/transfer", { method: "POST", credentials: "include", headers: { "X-CSRF-Token": csrfToken, "X-Requested-With": "XMLHttpRequest" }, body: payload })` | TypeScript/React/Fetch | `ASVS V14.2, CWE-352` | Forged cross-site POST succeeds via ambient cookies. |
|
|
59
|
+
| FR-057 | CSRF (Logic: strong): global `withCredentials=true` for all axios calls | `axios.defaults.withCredentials = true` | `const api = axios.create({ withCredentials: false });`<br>`// enable credentials only per trusted same-site endpoint` | TypeScript/React/Axios | `ASVS V14.2, CWE-352` | Broad credential forwarding expands CSRF attack surface. |
|
|
60
|
+
| FR-058 | CSRF (Logic: strong): missing `same-origin` mode in credentialed fetch wrapper | `fetch(url, { credentials: "include", method })` | `fetch(url, { credentials: "same-origin", method, headers: { "X-CSRF-Token": csrfToken } })` | TypeScript/React/Fetch | `ASVS V14.2, CWE-352` | Cross-origin endpoints receive authenticated cookie traffic. |
|
|
61
|
+
| FR-059 | CSRF (Logic: strong): GraphQL mutation client sends cookies without CSRF proof | `client.mutate({ mutation, context: { credentials: "include" } })` | `client.mutate({ mutation, context: { credentials: "same-origin", headers: { "X-CSRF-Token": csrfToken } } })` | TypeScript/React/GraphQL | `ASVS V14.2, CWE-352` | Mutation endpoint vulnerable to cross-site request forgery. |
|
|
62
|
+
| FR-060 | XML Safety (Logic: strong): string-concatenated XML payload builder | "``const xml = \"<user><name>\" + name + \"</name></user>\";``" | `const xml = buildXml({ user: { name: escapeXml(name) } });` | TypeScript/XML Client | `ASVS V5.1, CWE-91` | XML meta-characters break structure and inject attacker nodes. |
|
|
63
|
+
| FR-061 | XML Safety (Logic: strong): parsing XML with permissive external entity support | `const doc = parser.parse(xml, { processExternalEntities: true })` | `const doc = parser.parse(xml, { processExternalEntities: false, resolveEntities: false })` | TypeScript/XML Client | `ASVS V5.1, CWE-611` | XXE-style payload retrieves local/remote sensitive resources. |
|
|
64
|
+
| FR-062 | XML Safety (Logic: strong): SOAP envelope built via template string with raw user fields | "``const body = `<soap:Body><id>${id}</id></soap:Body>`;``" | `const body = soapBuilder.body({ id: escapeXml(id) });` | TypeScript/SOAP Client | `ASVS V5.1, CWE-91` | Injected XML tags alter requested operation semantics. |
|
|
65
|
+
| FR-063 | XML Safety (Logic: strong): DOMParser result used without validation against schema | `const doc = new DOMParser().parseFromString(xml, "text/xml")` | `const doc = safeXmlParse(xml);`<br>`schemaValidator.assertValid(doc);` | TypeScript/XML Client | `ASVS V5.1, CWE-611` | Malformed XML bypasses expected control flow and trust checks. |
|
|
66
|
+
| FR-064 | XML Safety (Logic: strong): merging user-provided XML fragments into signed payload | `payload = signedPrefix + userFragment + signedSuffix` | `payload = signedPrefix + sanitizeXmlFragment(userFragment) + signedSuffix` | TypeScript/XML Client | `ASVS V5.1, CWE-91` | Unsanitized fragment injection subverts integrity assumptions. |
|
|
67
|
+
| FR-065 | Overlay Attack: z-index derived from query param | `const z = Number(search.get("z") || 1)` | `const z = 10;`<br>`// use fixed design token values, never user input` | TypeScript/React | `ASVS V14.3, CWE-451` | User-controlled layering can spoof trusted UI controls. |
|
|
68
|
+
| FR-066 | Overlay Attack: opacity controlled by external message event | `setOpacity(Number(event.data.opacity))` | `setOpacity(clampTrustedOpacity(event.data.opacity, event.origin))` | TypeScript/React | `ASVS V14.3, CWE-451` | UI can be visually hidden/replaced to trick user actions. |
|
|
69
|
+
| FR-067 | Overlay Attack: pointer-events toggled from URL to capture clicks | `style={{ pointerEvents: search.get("pe") || "auto" }}` | `style={{ pointerEvents: "none" }}`<br>`// interactive overlays require signed trusted config` | TypeScript/React | `ASVS V14.3, CWE-451` | Click redirection enables spoofed consent/approval flows. |
|
|
70
|
+
| FR-068 | Credential Exposure: token forwarded via `useSearchParams` | `setSearchParams({ token })` | `setSearchParams({ ref: tokenRef })`<br>`memoryStore.set(tokenRef, token)` | TypeScript/React Router | `ASVS V14.2, CWE-522` | URL tokens leak to history, logs, and referrers. |
|
|
71
|
+
| FR-069 | Credential Exposure: `history.push` with bearer in query string | `history.push("/callback?access_token=" + token)` | `history.push("/callback?ref=" + tokenRef)` | TypeScript/React Router | `ASVS V14.2, CWE-312` | Credentials exposed in browser/session artifacts. |
|
|
72
|
+
| FR-070 | Credential Exposure: reset secret embedded in route path | `navigate("/reset/" + resetToken)` | `navigate("/reset?rid=" + resetRef)` | TypeScript/React Router | `ASVS V14.2, CWE-522` | Secret in URL path appears in telemetry and reverse proxies. |
|
|
73
|
+
| FR-071 | Error Leak: rendering raw API error object in production DOM | `<pre>{JSON.stringify(error)}</pre>` | `<ErrorBanner message="Request failed. Try again later." />` | TypeScript/React | `ASVS V14.3, CWE-209` | Internal stack/details disclosed to untrusted clients. |
|
|
74
|
+
| FR-072 | Error Leak: passing `err.message` from backend directly into HTML sink | `<div dangerouslySetInnerHTML={{ __html: err.message }} />` | `<div>{safeErrorCode(err)}</div>` | TypeScript/React | `ASVS V14.3, CWE-209` | Backend exception content may contain sensitive internals/XSS payload. |
|
|
75
|
+
| FR-073 | Error Leak: axios interceptor exposes full response body on failure | `toast.error(JSON.stringify(error.response?.data))` | `toast.error("Operation failed");`<br>`logSecure("api_failure", redactError(error))` | TypeScript/React/Axios | `ASVS V14.3, CWE-209` | Response diagnostics reveal internal service topology/data. |
|
|
76
|
+
| FR-074 | Error Leak: debug boundary prints stack traces in production builds | `return <code>{error.stack}</code>` | `return isProd() ? <ErrorFallback /> : <DebugErrorPanel error={error} />` | TypeScript/React | `ASVS V14.3, CWE-209` | Client-visible stack traces expose implementation details. |
|
|
77
|
+
| FR-075 | Confused Deputy: dynamic axios baseURL from query params | `const api = axios.create({ baseURL: searchParams.get("api") || "/" })` | `const api = axios.create({ baseURL: allowlistedApiBase(searchParams.get("api")) })` | TypeScript/React | `CWE-441` | Attacker-controlled base URL pivots trusted client to untrusted backend. |
|
|
78
|
+
| FR-076 | Confused Deputy: fetch URL from `location.href`-derived host | `fetch(new URL("/v1/pay", location.href).toString())` | `fetch(new URL("/v1/pay", TRUSTED_API_ORIGIN).toString())` | TypeScript/React | `CWE-441` | Browser context makes authenticated calls to attacker-influenced origin. |
|
|
79
|
+
| FR-077 | Confused Deputy: proxy helper forwards arbitrary query URL | `const u = search.get("url"); return fetch(u!)` | `const u = allowlistedBackendUrl(search.get("url")); return fetch(u)` | TypeScript/React | `CWE-441` | Frontend proxy utility becomes SSRF-like confused deputy. |
|
|
80
|
+
| FR-078 | Confused Deputy: axios interceptor rewrites host from route query | `cfg.baseURL = search.get("host") || cfg.baseURL` | `cfg.baseURL = enforceTrustedHost(cfg.baseURL)` | TypeScript/React | `CWE-441` | Request interceptor routes secrets/tokens to attacker endpoint. |
|
|
81
|
+
| FR-079 | Confused Deputy: data URL used as request destination | `fetch(search.get("endpoint") || "data:text/plain,ok")` | `const ep = requireHttpHttpsAllowlist(search.get("endpoint")); fetch(ep)` | TypeScript/React | `CWE-441` | Non-http scheme bypasses expected network trust boundary. |
|
|
82
|
+
| FR-080 | Confused Deputy: fallback to `window.location.origin` in embedded contexts | `axios.create({ baseURL: window.location.origin })` | `axios.create({ baseURL: STATIC_API_ORIGIN })` | TypeScript/React | `CWE-441` | Embedded/malicious host origin hijacks API destination. |
|
|
83
|
+
| FR-081 | Confused Deputy: tenant API host selected from untrusted localStorage | `const host = localStorage.getItem("api_host"); fetch(host + "/txn")` | `const host = resolveTenantApiHostFromSignedConfig(); fetch(host + "/txn")` | TypeScript/React | `CWE-441` | Poisoned local storage redirects privileged requests externally. |
|
|
84
|
+
| FR-082 | Confused Deputy: dynamic websocket/API endpoint from hash fragment | `const ep = new URLSearchParams(location.hash.slice(1)).get("api"); axios.create({ baseURL: ep! })` | `const ep = parseSignedRuntimeConfig().apiBase; axios.create({ baseURL: ep })` | TypeScript/React | `CWE-441` | Hash-controlled endpoint bypasses route guard assumptions. |
|
|
85
|
+
| FR-083 | Confused Deputy: fetch wrapper accepts absolute URL for internal action | `export const post = (u,b) => fetch(u,{method:"POST",body:b,credentials:"include"})` | `export const post = (u,b) => fetch(requireRelativePath(u),{method:"POST",body:b,credentials:"same-origin"})` | TypeScript/React | `CWE-441` | Internal helper can be abused for cross-origin authenticated calls. |
|
|
86
|
+
| FR-084 | Confused Deputy: router param controls file upload endpoint | `const api = "/upload/" + params.get("target"); await fetch(api,{method:"POST"})` | `const api = "/upload/" + allowlistedUploadTarget(params.get("target")); await fetch(api,{method:"POST"})` | TypeScript/React | `CWE-441` | Untrusted routing data drives sensitive backend action path. |
|
|
87
|
+
| FR-085 | Prototype Pollution: `Object.assign` into state from parsed payload | `return Object.assign({}, state, JSON.parse(action.payload))` | `return Object.assign({}, state, sanitizePrototypeKeys(JSON.parse(action.payload)))` | TypeScript/React/Redux | `CWE-1321` | Parsed payload injects prototype keys into app state tree. |
|
|
88
|
+
| FR-086 | Prototype Pollution: reducer spread merge in loop | `for (const p of updates) s = { ...s, ...p }` | `for (const p of updates) s = { ...s, ...sanitizePrototypeKeys(p) }` | TypeScript/React/Redux | `CWE-1321` | Repeated spread merges allow hidden key smuggling. |
|
|
89
|
+
| FR-087 | Prototype Pollution: nested merge helper without dangerous-key guard | `out[k] = typeof v==="object" ? merge(out[k], v) : v` | `if (isDangerousKey(k)) continue; out[k] = typeof v==="object" ? mergeSafe(out[k], v) : v` | TypeScript/React | `CWE-1321` | Recursive merge mutates inherited object behavior. |
|
|
90
|
+
| FR-088 | Prototype Pollution: unsafe `JSON.parse` into Zustand set | `set(JSON.parse(raw))` | `set(sanitizePrototypeKeys(JSON.parse(raw)))` | TypeScript/React/Zustand | `CWE-1321` | Runtime state setter receives attacker-crafted prototype fields. |
|
|
91
|
+
| FR-089 | Prototype Pollution: using `_.merge` directly on action payload | `return _.merge({}, state, action.payload)` | `return _.merge({}, state, sanitizePrototypeKeys(action.payload))` | TypeScript/React/Redux | `CWE-1321` | Utility merge imports prototype chain keys by default. |
|
|
92
|
+
| FR-090 | Prototype Pollution: object path setter with user path segments | `setByPath(obj, pathFromUI, value)` | `setByPath(obj, requireSafePath(pathFromUI), value)` | TypeScript/React | `CWE-1321` | User path can target constructor/prototype internals. |
|
|
93
|
+
| FR-091 | Prototype Pollution: deserialized theme merged into global config | `globalTheme = { ...globalTheme, ...JSON.parse(themeRaw) }` | `globalTheme = { ...globalTheme, ...sanitizePrototypeKeys(JSON.parse(themeRaw)) }` | TypeScript/React | `CWE-1321` | Polluted theme object alters renderer/security controls. |
|
|
94
|
+
| FR-092 | Prototype Pollution: form draft hydration from untrusted cache | `draft = { ...draft, ...JSON.parse(cache) }` | `draft = { ...draft, ...sanitizePrototypeKeys(JSON.parse(cache)) }` | TypeScript/React | `CWE-1321` | Cache poisoning modifies form/control object prototypes. |
|
|
95
|
+
| FR-093 | Prototype Pollution: copy utility iterates `for...in` over payload | `for (const k in payload) target[k] = payload[k]` | `for (const k of Object.keys(payload)) { if (isDangerousKey(k)) continue; target[k] = payload[k]; }` | TypeScript/React | `CWE-1321` | `for...in` includes inherited keys and prototype gadgets. |
|
|
96
|
+
| FR-094 | Prototype Pollution: API response merged into React context value | `setCtx(prev => ({ ...prev, ...resp.data }))` | `setCtx(prev => ({ ...prev, ...sanitizePrototypeKeys(resp.data) }))` | TypeScript/React/Context | `CWE-1321` | Untrusted response can poison context state globally. |
|
|
97
|
+
| FR-095 | CSS/Template Injection: styled-components interpolates raw user CSS | "``const Box = styled.div`${p => p.userCss}`;``" | `const Box = styled.div`${p => sanitizeCss(p.userCss)}`;` | TypeScript/React/styled-components | `CWE-94` | User-controlled style interpolation enables CSS exfiltration tricks. |
|
|
98
|
+
| FR-096 | CSS Injection: dynamic `url()` in styled template from input | "``background: url(${p => p.avatar});``" | "``background: url(${p => allowlistedAssetUrl(p.avatar)});``" | TypeScript/React/styled-components | `CWE-94` | CSS URL sink may leak tokens via external resource loads. |
|
|
99
|
+
| FR-097 | Template Injection: Emotion css helper with raw string | "``const cls = css`${userStyle}`;``" | "``const cls = css`${sanitizeCss(userStyle)}`;``" | TypeScript/React/Emotion | `CWE-94` | Arbitrary CSS template content reaches runtime style engine. |
|
|
100
|
+
| FR-098 | CSS Injection: inline style from query parameter object | `const s = JSON.parse(search.get("style")!); <div style={s} />` | `const s = sanitizeStyleObject(JSON.parse(search.get("style")!)); <div style={s} />` | TypeScript/React | `CWE-94` | Query-controlled style object can spoof/overlay trusted UI. |
|
|
101
|
+
| FR-099 | Template Injection: render helper compiles user template literal | `const html = compileTemplate(userTpl)(data)` | `const html = compileTemplate(allowlistedTemplate(userTpl))(data)` | TypeScript/React | `CWE-94` | User template execution reaches unsafe runtime compiler path. |
|
|
102
|
+
| FR-100 | CSS Exfiltration risk: pseudo-selector content from secrets | "``const Box = styled.div`${p => `content:'${p.secret}'`}`;``" | `const Box = styled.div`${() => "content:''"}`;` | TypeScript/React/styled-components | `CWE-94` | Secrets reflected into CSS can be extracted via side channels. |
|
|
103
|
+
| FR-101 | Template Injection: dynamic keyframes from user-controlled string | `const anim = keyframes`${userFrames}`` | `const anim = keyframes`${sanitizeKeyframes(userFrames)}`` | TypeScript/React | `CWE-94` | Malicious animation CSS manipulates layout and overlays actions. |
|
|
104
|
+
| FR-102 | CSS Injection: global style component fed by API error text | `<Global styles={css`${apiError}`} />` | `<Global styles={css`${sanitizeCss(apiError)}`} />` | TypeScript/React | `CWE-94` | Error payload controls global CSS and spoofing behavior. |
|
|
105
|
+
| FR-103 | Confused Deputy: API base selected from `document.referrer` | `const api = axios.create({ baseURL: new URL(document.referrer).origin })` | `const api = axios.create({ baseURL: TRUSTED_API_ORIGIN })` | TypeScript/React | `CWE-441` | Referrer-controlled origin reroutes trusted API traffic. |
|
|
106
|
+
| FR-104 | Confused Deputy: fetch destination from `window.name` | `fetch(window.name + "/api/profile", { credentials: "include" })` | `fetch(TRUSTED_API_ORIGIN + "/api/profile", { credentials: "same-origin" })` | TypeScript/React | `CWE-441` | Cross-window state contaminates privileged network endpoint. |
|
|
107
|
+
| FR-105 | Confused Deputy: API wrapper allows protocol-relative URL | `api.get("//" + host + "/v1/data")` | `api.get("https://" + allowlistedHost(host) + "/v1/data")` | TypeScript/React | `CWE-441` | Protocol-relative host bypasses strict origin assumptions. |
|
|
108
|
+
| FR-106 | Confused Deputy: upload SDK endpoint from drag-drop metadata | `client = axios.create({ baseURL: file.meta.endpoint })` | `client = axios.create({ baseURL: allowlistedUploadEndpoint(file.meta.endpoint) })` | TypeScript/React | `CWE-441` | File metadata steers authenticated upload channel externally. |
|
|
109
|
+
| FR-107 | Confused Deputy: dynamic GraphQL endpoint from route state | `new ApolloClient({ uri: location.state.apiUri })` | `new ApolloClient({ uri: TRUSTED_GRAPHQL_URI })` | TypeScript/React | `CWE-441` | Navigation state can inject alternate backend for sensitive ops. |
|
|
110
|
+
| FR-108 | Confused Deputy: service worker forwards messages to arbitrary API base | `self.addEventListener("message", e => fetch(e.data.url))` | `self.addEventListener("message", e => fetch(requireTrustedSwUrl(e.data.url)))` | TypeScript/React/PWA | `CWE-441` | SW becomes trusted deputy for attacker-provided endpoint. |
|
|
111
|
+
| FR-109 | Confused Deputy: auth refresh call built from current location host | `fetch(location.origin + "/oauth/refresh", { credentials: "include" })` | `fetch(TRUSTED_AUTH_ORIGIN + "/oauth/refresh", { credentials: "include" })` | TypeScript/React | `CWE-441` | Hostname confusion leaks auth refresh cookies/tokens. |
|
|
112
|
+
| FR-110 | Prototype Pollution: deep clone via JSON roundtrip merged unsafely | `state = { ...state, ...JSON.parse(JSON.stringify(payload)) }` | `state = { ...state, ...sanitizePrototypeKeys(payload) }` | TypeScript/React | `CWE-1321` | JSON roundtrip does not remove dangerous object keys. |
|
|
113
|
+
| FR-111 | Prototype Pollution: reducer trusts server patch ops on state root | `applyPatch(state, resp.patch)` | `applyPatch(state, sanitizePatchOps(resp.patch))` | TypeScript/React/Redux | `CWE-1321` | Patch paths can target prototype/constructor chain. |
|
|
114
|
+
| FR-112 | Prototype Pollution: object merge from query param map | `const q = Object.fromEntries(searchParams); setState({ ...state, ...q })` | `const q = sanitizePrototypeKeys(Object.fromEntries(searchParams)); setState({ ...state, ...q })` | TypeScript/React | `CWE-1321` | URL params merged into state without key safeguards. |
|
|
115
|
+
| FR-113 | Prototype Pollution: dynamic form field names used as object keys | `next[fieldName] = value` | `if (isDangerousKey(fieldName)) return; next[fieldName] = value` | TypeScript/React | `CWE-1321` | Crafted field names poison object prototypes in form state. |
|
|
116
|
+
| FR-114 | Prototype Pollution: unsafe `immer` producer assigns raw payload keys | `produce(state, d => { Object.assign(d, payload); })` | `produce(state, d => { Object.assign(d, sanitizePrototypeKeys(payload)); })` | TypeScript/React/Redux Toolkit | `CWE-1321` | Producer applies dangerous keys into proxied state tree. |
|
|
117
|
+
| FR-115 | CSS Injection: user theme token interpolated into `@import` | "``const G = createGlobalStyle`@import url(${p => p.fontUrl});`;``" | "``const G = createGlobalStyle`@import url(${p => allowlistedFontUrl(p.fontUrl)});`;``" | TypeScript/React/styled-components | `CWE-94` | CSS import sink can exfiltrate or spoof UI assets. |
|
|
118
|
+
| FR-116 | CSS Injection: style tag content built from raw user preference | `styleEl.textContent = userCss` | `styleEl.textContent = sanitizeCss(userCss)` | TypeScript/React | `CWE-94` | Direct style tag injection affects full page behavior. |
|
|
119
|
+
| FR-117 | Template Injection: Handlebars compile with untrusted template from API | `const tpl = Handlebars.compile(resp.template)` | `const tpl = Handlebars.compile(allowlistedTemplate(resp.templateId))` | TypeScript/React | `CWE-94` | Runtime compilation of untrusted templates executes attacker logic. |
|
|
120
|
+
| FR-118 | CSS Injection: dynamic className from API-driven raw CSS text | `const cls = styleSheet.insertRule(apiCss)` | `const cls = styleSheet.insertRule(sanitizeCssRule(apiCss))` | TypeScript/React | `CWE-94` | Arbitrary CSS rule injection enables UI spoofing/exfiltration. |
|
|
121
|
+
| FR-119 | Template Injection: JSX runtime parser consumes user template string | `const C = parseJsx(userTemplate); return <C />` | `const C = parseJsx(allowlistedTemplate(userTemplate)); return <C />` | TypeScript/React | `CWE-94` | User template string reaches executable component parser. |
|
|
122
|
+
| FR-120 | Confused Deputy: retry client rebuilds baseURL from failed request URL | `client = axios.create({ baseURL: new URL(error.config.url!).origin })` | `client = axios.create({ baseURL: TRUSTED_API_ORIGIN })` | TypeScript/React/Axios | `CWE-441` | Error path contaminates future trusted request routing. |
|
|
123
|
+
| FR-121 | Cookie Security: session cookie set without `Secure` attribute | `document.cookie = "session=" + token + "; Path=/; SameSite=Lax"` | `document.cookie = "session=" + token + "; Path=/; Secure; SameSite=Strict"` | TypeScript/React | `CWE-614` | Session cookie can be transmitted over insecure channel when attributes are weak. |
|
|
124
|
+
| FR-122 | Cookie Security: auth cookie set without `SameSite=Strict` | `document.cookie = "auth=" + token + "; Path=/; Secure"` | `document.cookie = "auth=" + token + "; Path=/; Secure; SameSite=Strict"` | TypeScript/React | `CWE-614` | Cross-site request context may replay cookie in sensitive flows. |
|
|
125
|
+
| FR-123 | Cookie Security: refresh token cookie missing hardening flags | `document.cookie = "refresh=" + refresh + "; Path=/api"` | `document.cookie = "refresh=" + refresh + "; Path=/api; Secure; SameSite=Strict"` | TypeScript/React | `CWE-614` | Missing cookie flags reduce transport/session integrity guarantees. |
|
|
126
|
+
| FR-124 | Cookie Security: cookie helper omits strict attributes by default | `setCookie(name, value, { path: "/" })` | `setCookie(name, value, { path: "/", secure: true, sameSite: "strict" })` | TypeScript/React | `CWE-614` | Central helper propagates insecure cookie defaults across application. |
|
|
127
|
+
| FR-125 | Module Federation remoteEntry loaded from query param | `const remoteUrl = search.get("remote"); await import(/* webpackIgnore: true */ remoteUrl!)` | `const remoteUrl = allowlistedRemote(search.get("remote")); await import(/* webpackIgnore: true */ remoteUrl)` | TypeScript/React | `CWE-1329` | Untrusted remote entry enables arbitrary microfrontend code execution. |
|
|
128
|
+
| FR-126 | Module Federation remotes from wildcard host map | `remotes: { shell: "shell@[window.remoteHost]/remoteEntry.js" }` | `remotes: { shell: "shell@https://mf.example.com/remoteEntry.js" }` | TypeScript/React/Webpack | `CWE-1329` | Runtime-resolved host allows attacker-controlled remote module source. |
|
|
129
|
+
| FR-127 | Dynamic `import()` from `window.location`-derived URL | `await import(new URL(path, location.href).toString())` | `await import(resolveTrustedChunk(path))` | TypeScript/React | `CWE-1329` | Location-influenced import path loads untrusted JavaScript bundles. |
|
|
130
|
+
| FR-128 | Microfrontend loader skips SRI/integrity verification | `script.src = remoteEntry; document.head.appendChild(script)` | `script.src = remoteEntry; script.integrity = knownHash(remoteEntry); script.crossOrigin = "anonymous"; document.head.appendChild(script)` | TypeScript/React | `CWE-1329` | Remote entry integrity not checked before execution. |
|
|
131
|
+
| FR-129 | Module federation shared deps allow eager unsafeguarded override | `shared: { react: { singleton: false, eager: true } }` | `shared: { react: { singleton: true, requiredVersion: "18.x", strictVersion: true } }` | TypeScript/React/Webpack | `CWE-1329` | Dependency override can inject incompatible or malicious runtime code. |
|
|
132
|
+
| FR-130 | Remote container initialized from untrusted origin list | `for (const o of origins) remotes[o.name] = o.url` | `for (const o of trustedOrigins) remotes[o.name] = o.url` | TypeScript/React | `CWE-1329` | Origin list poisoning results in unauthorized remote execution. |
|
|
133
|
+
| FR-131 | Dynamic import from CDN URL without signature pinning | `await import("https://cdn.example.com/" + mod)` | `await import(allowlistedPinnedModule(mod))` | TypeScript/React | `CWE-1329` | CDN path manipulation injects hostile module at runtime. |
|
|
134
|
+
| FR-132 | Webpack publicPath set from location at runtime | `__webpack_public_path__ = window.location.origin + "/assets/"` | `__webpack_public_path__ = "https://static.example.com/assets/"` | TypeScript/React/Webpack | `CWE-1329` | Chunk loading path can be redirected to attacker origin. |
|
|
135
|
+
| FR-133 | Remote manifest fetched without host allowlist | `const manifest = await fetch(search.get("manifest")!).then(r => r.json())` | `const manifest = await fetch(allowlistedManifest(search.get("manifest"))).then(r => r.json())` | TypeScript/React | `CWE-1329` | Untrusted manifest controls loaded runtime modules. |
|
|
136
|
+
| FR-134 | Federated plugin registry accepts unsigned plugin metadata | `plugins.forEach(p => loadRemote(p.url))` | `plugins.forEach(p => verifyAndLoadRemote(p.url, p.signature))` | TypeScript/React | `CWE-1329` | Missing signature checks enables plugin supply chain hijack. |
|
|
137
|
+
| FR-135 | Production debug prop toggles sensitive panel visibility | `<AdminPanel debug={search.get("debug") === "1"} />` | `<AdminPanel debug={false} />`<br>`// debug props stripped in production build` | TypeScript/React | `CWE-489` | User-controlled debug flag exposes internal privileged UI state. |
|
|
138
|
+
| FR-136 | Sensitive PII logged via `console.log` in production render path | `console.log("profile", profile)` | `if (!isProd()) console.debug("profileId", profile.id)` | TypeScript/React | `CWE-489` | Console output leaks PII to browser logs/extensions. |
|
|
139
|
+
| FR-137 | Global debug store exposed on `window` | `window.debugStore = store` | `if (!isProd()) window.debugStore = undefined` | TypeScript/React | `CWE-489` | Global state exposure enables runtime tampering and data leakage. |
|
|
140
|
+
| FR-138 | Error boundary renders verbose internals behind debug prop | `<ErrorPanel debug={true} details={error.stack} />` | `<ErrorPanel debug={false} details={undefined} />` | TypeScript/React | `CWE-489` | Production users receive internal diagnostics and stack traces. |
|
|
141
|
+
| FR-139 | Feature flag enables devtools in production bundle | `if (flags.devtools) attachReduxDevtools(store)` | `if (!isProd() && flags.devtools) attachReduxDevtools(store)` | TypeScript/React/Redux | `CWE-489` | Devtools exposure grants state inspection and manipulation surface. |
|
|
142
|
+
| FR-140 | API response dumped to `window.__DEBUG__` object | `window.__DEBUG__ = { lastResponse: resp }` | `if (!isProd()) window.__DEBUG__ = { requestId: resp.requestId }` | TypeScript/React | `CWE-489` | Global debug payload exposes sensitive runtime artifacts. |
|
|
143
|
+
| FR-141 | Source-map disclosure flag forced in production runtime | `window.enableSourceMaps = true` | `window.enableSourceMaps = false` | TypeScript/React | `CWE-489` | Debug artifacts increase reverse engineering and exploitability. |
|
|
144
|
+
| FR-142 | Debug query parameter activates bypass branch | `if (search.get("trace") === "1") return renderRawState()` | `if (!isProd() && search.get("trace") === "1") return renderSafeTrace()` | TypeScript/React | `CWE-489` | URL-triggered debug mode leaks protected internals. |
|
|
145
|
+
| FR-143 | React context exports internal auth state for debugging | `window.authCtx = useContext(AuthContext)` | `if (!isProd()) window.authCtx = { userId: auth.userId }` | TypeScript/React | `CWE-489` | Debug export leaks auth/session details globally. |
|
|
146
|
+
| FR-144 | Build-time debug env var not stripped from client bundle | `const DEBUG = process.env.DEBUG === "true"` | `const DEBUG = false`<br>`// enforce DefinePlugin replacement in prod` | TypeScript/React/Webpack | `CWE-489` | Residual debug logic remains reachable in production. |
|
|
147
|
+
| FR-145 | Deprecated crypto wrapper dependency used for token ops | `import insecureCrypto from "legacy-crypto-wrapper"` | `import { subtle } from "./trusted-crypto-adapter"` | TypeScript/React | `CWE-1104` | Unmaintained crypto package increases compromise risk. |
|
|
148
|
+
| FR-146 | Runtime import of deprecated package from plugin metadata | `const lib = await import(plugin.cryptoLib)` | `const lib = await import(allowlistedMaintainedLib(plugin.cryptoLib))` | TypeScript/React | `CWE-1104` | Plugin-selected outdated libs introduce vulnerable code paths. |
|
|
149
|
+
| FR-147 | Use of known abandoned UUID/math-random library for security tokens | `import insecureId from "uuid-vulnerable"; const id = insecureId()` | `import { randomUUID } from "crypto"; const id = randomUUID()` | TypeScript/React | `CWE-1104` | Predictable/abandoned token generator weakens auth workflows. |
|
|
150
|
+
| FR-148 | Dependency health checks disabled in CI gating logic | `if (process.env.SKIP_AUDIT === "1") return true` | `if (isProdBuild()) requireAuditPass();` | TypeScript/React/CI | `CWE-1104` | Vulnerable dependencies enter production without policy enforcement. |
|
|
151
|
+
| FR-149 | Ignoring npm `deprecated` warnings during install hook | `npm config set loglevel silent` | `npm ci && npm audit --production --audit-level=high` | TypeScript/React/CI | `CWE-1104` | Deprecated dependency signals suppressed from build pipeline. |
|
|
152
|
+
| FR-150 | Direct use of stale third-party auth SDK without version pin | `import authSdk from "old-auth-sdk"` | `import authSdk from "old-auth-sdk@^x.y.z"`<br>`// enforce maintained version via lock policy` | TypeScript/React | `CWE-1104` | Outdated auth SDK may contain known exploitable flaws. |
|
|
153
|
+
| FR-151 | Dynamic script loader trusts package URL from config service | `loadScript(cfg.packageUrl)` | `loadScript(allowlistedPinnedPackageUrl(cfg.packageUrl))` | TypeScript/React | `CWE-1104` | Unreviewed external package URL injects unsafe dependency runtime. |
|
|
154
|
+
| FR-152 | Lockfile integrity bypass using `--legacy-peer-deps` in production build | `npm install --legacy-peer-deps` | `npm ci --ignore-scripts=false` | TypeScript/React/CI | `CWE-1104` | Build may resolve insecure transitive versions unexpectedly. |
|
|
155
|
+
| FR-153 | Package override mechanism disabled for vulnerable transitive deps | `overrides: {}` | `overrides: { "vuln-lib": "safe-version" }` | TypeScript/React/NPM | `CWE-1104` | Vulnerable transitive dependency remains unresolved in build graph. |
|
|
156
|
+
| FR-154 | Using unmaintained sanitizer package with known bypasses | `import sanitize from "legacy-sanitize"` | `import DOMPurify from "dompurify"` | TypeScript/React | `CWE-1104` | Known bypass in abandoned sanitizer allows XSS payloads. |
|
|
157
|
+
| FR-155 | Module federation remote fallback to HTTP in production | `const remote = env.REMOTE || "http://remote.local/remoteEntry.js"` | `const remote = env.REMOTE || "https://remote.example.com/remoteEntry.js"` | TypeScript/React/Webpack | `CWE-1329` | Insecure fallback enables MITM and remote code injection. |
|
|
158
|
+
| FR-156 | Remote scope name built from user input | `const scope = search.get("scope"); window[scope!].get("./App")` | `const scope = allowlistedScope(search.get("scope")); window[scope].get("./App")` | TypeScript/React | `CWE-1329` | User-controlled scope accesses unexpected global remote containers. |
|
|
159
|
+
| FR-157 | Exposing webpack module registry on global window for debugging | `window.__MODULES__ = __webpack_modules__` | `if (!isProd()) window.__MODULES__ = undefined` | TypeScript/React/Webpack | `CWE-489` | Global module registry exposure aids runtime tampering. |
|
|
160
|
+
| FR-158 | Emitting full Redux action payloads in console middleware | `console.log("action", action)` | `if (!isProd()) console.debug("action.type", action.type)` | TypeScript/React/Redux | `CWE-489` | Action logs can include tokens, PII, and secrets. |
|
|
161
|
+
| FR-159 | Storing stack traces on `window.lastError` in production | `window.lastError = error` | `if (!isProd()) window.lastError = { code: safeCode(error) }` | TypeScript/React | `CWE-489` | Global error object reveals internals and runtime secrets. |
|
|
162
|
+
| FR-160 | Dev-only mock API toggle exposed to end users | `if (search.get("mock") === "1") useMockApi()` | `if (!isProd() && search.get("mock") === "1") useMockApi()` | TypeScript/React | `CWE-489` | User-accessible mock mode can bypass real security controls. |
|
|
163
|
+
| FR-161 | Loading package metadata from untrusted registry mirror | `fetch(search.get("registry") + "/pkg/meta")` | `fetch(TRUSTED_REGISTRY + "/pkg/meta")` | TypeScript/React | `CWE-1104` | Untrusted registry metadata drives risky dependency decisions. |
|
|
164
|
+
| FR-162 | Automatic update channel for frontend plugins without signature verify | `installPlugin(update.url)` | `installPluginVerified(update.url, update.signature)` | TypeScript/React | `CWE-1104` | Unsigned updates allow supply chain takeover of plugin runtime. |
|
|
165
|
+
| FR-163 | Dynamic `require()` from user-provided package name | `const mod = require(userPkg)` | `const mod = require(allowlistedPkg(userPkg))` | TypeScript/React | `CWE-1329` | Untrusted package name controls loaded runtime dependency. |
|
|
166
|
+
| FR-164 | Build script exposes env secrets through debug banner | `window.__BUILD_INFO__ = process.env` | `window.__BUILD_INFO__ = { version: APP_VERSION }` | TypeScript/React | `CWE-489` | Production banner leaks CI/env internals and sensitive values. |
|
|
167
|
+
| FR-165 | NPM `postinstall` script from untrusted dependency not restricted | `"postinstall": "node scripts/postinstall.js"` | `"postinstall": "node scripts/postinstall.js --verify-lock-integrity"` | TypeScript/React/NPM | `CWE-1104` | Untrusted lifecycle scripts execute during dependency installation. |
|
|
168
|
+
| FR-166 | Federated module loaded without strict version compatibility checks | `shared: { react: { requiredVersion: false } }` | `shared: { react: { requiredVersion: "^18.2.0", strictVersion: true, singleton: true } }` | TypeScript/React/Webpack | `CWE-1329` | Version mismatch permits unsafe runtime behavior and gadget injection. |
|
|
169
|
+
| FR-167 | Runtime environment inspector exposed through global debug API | `window.debugEnv = () => ({ token, profile, flags })` | `window.debugEnv = () => ({ build: APP_VERSION })` | TypeScript/React | `CWE-489` | Public debug API leaks sensitive runtime state to scripts/extensions. |
|
|
170
|
+
| FR-168 | Dependency alias maps obsolete package into production bundle | `resolve.alias = { "crypto-lib": "crypto-lib-legacy" }` | `resolve.alias = { "crypto-lib": "crypto-lib-maintained" }` | TypeScript/React/Webpack | `CWE-1104` | Alias forces known-vulnerable package into final artifact. |
|
|
171
|
+
| FR-169 | Plugin sandbox disabled for remote microfrontend scripts | `sandbox: false` | `sandbox: true`<br>`// isolate remote execution context` | TypeScript/React | `CWE-1329` | Remote script executes with full app privileges and shared globals. |
|
|
172
|
+
| FR-170 | Console telemetry plugin sends raw `window` state in prod | `telemetry.send({ state: window })` | `telemetry.send({ state: minimalSafeState() })` | TypeScript/React | `CWE-489` | Massive global state leak includes sensitive internals and tokens. |
|
|
173
|
+
| FR-171 | Package manager policy allows deprecated packages in critical path | `"allowDeprecated": true` | `"allowDeprecated": false`<br>`// block build on deprecated critical dependencies` | TypeScript/React/Policy | `CWE-1104` | Deprecated packages remain in auth/payment critical execution paths. |
|
|
174
|
+
| FR-172 | Remote module prefetch from untrusted hint endpoint | `const hint = await fetch(search.get("hint")!).then(r=>r.text()); import(hint)` | `const hint = await fetch(TRUSTED_HINT_ENDPOINT).then(r=>r.text()); import(allowlistedHint(hint))` | TypeScript/React | `CWE-1329` | Prefetch hint service controls executable module source. |
|
|
175
|
+
| FR-173 | Production route exposes internal debug console component | `{showDebug && <InternalDebugConsole />}` | `{!isProd() && showDebug && <InternalDebugConsole />}` | TypeScript/React | `CWE-489` | Internal debugging UI reachable in production surface. |
|
|
176
|
+
| FR-174 | Unpinned semver range for high-risk client crypto dependency | `"secure-client-crypto": "^1.0.0"` | `"secure-client-crypto": "1.0.7"`<br>`// pin vetted version and monitor advisories` | TypeScript/React/NPM | `CWE-1104` | Broad semver range may pull vulnerable transitive release. |
|
|
177
|
+
| FR-175 | postMessage uses wildcard target origin | `window.postMessage(payload, "*")` | `window.postMessage(payload, TRUSTED_ORIGIN)` | TypeScript/React | `CWE-346` | Message can be delivered to untrusted window contexts. |
|
|
178
|
+
| FR-176 | opener postMessage with `*` targetOrigin | `window.opener?.postMessage(tokenRef, "*")` | `window.opener?.postMessage(tokenRef, TRUSTED_ORIGIN)` | TypeScript/React | `CWE-346` | Sensitive data sent to unknown parent/origin. |
|
|
179
|
+
| FR-177 | Message listener missing `event.origin` validation | `window.addEventListener("message", e => handle(e.data))` | `window.addEventListener("message", e => { if (e.origin !== TRUSTED_ORIGIN) return; handle(e.data); })` | TypeScript/React | `CWE-346` | Any origin can inject control messages. |
|
|
180
|
+
| FR-178 | Message listener validates type but not source origin | `if (e.data?.type === "AUTH") setAuth(e.data.token)` | `if (e.origin !== TRUSTED_ORIGIN) return; if (e.data?.type === "AUTH") setAuth(e.data.token)` | TypeScript/React | `CWE-346` | Type check alone does not guarantee trusted sender. |
|
|
181
|
+
| FR-179 | postMessage bridge forwards raw payload from URL param | `target.postMessage(search.get("msg"), "*")` | `target.postMessage(sanitizeMessage(search.get("msg")), TRUSTED_ORIGIN)` | TypeScript/React | `CWE-346` | URL parameter controls cross-origin message channel. |
|
|
182
|
+
| FR-180 | BroadcastChannel fallback without channel allowlist | `new BroadcastChannel(search.get("ch") || "default")` | `new BroadcastChannel(allowlistedChannel(search.get("ch")))` | TypeScript/React | `CWE-346` | Untrusted channel naming can mix isolation boundaries. |
|
|
183
|
+
| FR-181 | iframe `contentWindow.postMessage` to arbitrary origin | `iframe.contentWindow?.postMessage(cmd, originFromUser)` | `iframe.contentWindow?.postMessage(cmd, TRUSTED_IFRAME_ORIGIN)` | TypeScript/React | `CWE-346` | Command/control data can leak to attacker iframe. |
|
|
184
|
+
| FR-182 | Message event handler trusts `event.source` only | `if (e.source === iframe.contentWindow) handle(e.data)` | `if (e.source !== iframe.contentWindow || e.origin !== TRUSTED_IFRAME_ORIGIN) return; handle(e.data)` | TypeScript/React | `CWE-346` | Source object check without origin is insufficient. |
|
|
185
|
+
| FR-183 | Service worker message handler misses client origin policy | `self.addEventListener("message", e => process(e.data))` | `self.addEventListener("message", e => { if (!isTrustedClient(e.source?.url)) return; process(e.data); })` | TypeScript/React/PWA | `CWE-346` | Untrusted clients can invoke privileged SW actions. |
|
|
186
|
+
| FR-184 | postMessage auth response sent before nonce validation | `popup.postMessage({ token }, TRUSTED_ORIGIN)` | `if (!validateNonce(state, nonce)) throw new Error("invalid nonce"); popup.postMessage({ tokenRef }, TRUSTED_ORIGIN)` | TypeScript/React | `CWE-346` | Missing nonce binding enables message replay/spoof. |
|
|
187
|
+
| FR-185 | Production WebSocket uses insecure `ws://` endpoint | `new WebSocket("ws://api.example.com/realtime")` | `new WebSocket("wss://api.example.com/realtime")` | TypeScript/React | `CWE-319` | Plaintext websocket allows interception and tampering. |
|
|
188
|
+
| FR-186 | WebSocket URL derived from `http://` API base in prod | `const ws = apiBase.replace("http://","ws://")` | `const ws = apiBase.replace("https://","wss://")` | TypeScript/React | `CWE-319` | Mixed transport conversion downgrades confidentiality. |
|
|
189
|
+
| FR-187 | Mixed content fetch to hardcoded HTTP API | `fetch("http://api.example.com/v1/profile")` | `fetch("https://api.example.com/v1/profile")` | TypeScript/React | `CWE-319` | Browser mixed-content request exposes sensitive data. |
|
|
190
|
+
| FR-188 | Axios instance configured with insecure HTTP baseURL | `axios.create({ baseURL: "http://api.internal" })` | `axios.create({ baseURL: "https://api.internal" })` | TypeScript/React/Axios | `CWE-319` | Request credentials and payload traverse plaintext transport. |
|
|
191
|
+
| FR-189 | SockJS/STOMP client initialized over insecure transport | `new SockJS("http://api.example.com/stomp")` | `new SockJS("https://api.example.com/stomp")` | TypeScript/React | `CWE-319` | Messaging session vulnerable to MitM and replay. |
|
|
192
|
+
| FR-190 | HTTP fallback endpoint enabled in production config | `const endpoint = env.API_URL || "http://localhost:8080"` | `const endpoint = env.API_URL || "https://api.example.com"` | TypeScript/React | `CWE-319` | Insecure fallback accidentally shipped to production runtime. |
|
|
193
|
+
| FR-191 | GraphQL client URI uses insecure protocol | `new ApolloClient({ uri: "http://api.example.com/graphql" })` | `new ApolloClient({ uri: "https://api.example.com/graphql" })` | TypeScript/React | `CWE-319` | GraphQL queries/mutations exposed over plaintext channel. |
|
|
194
|
+
| FR-192 | Runtime protocol downgrade from `https` to `http` in helper | `url.replace("https://", "http://")` | `url`<br>`// never downgrade transport in production helpers` | TypeScript/React | `CWE-319` | Helper forces insecure transport for sensitive requests. |
|
|
195
|
+
| FR-193 | Insecure EventSource stream over HTTP | `new EventSource("http://api.example.com/events")` | `new EventSource("https://api.example.com/events")` | TypeScript/React | `CWE-319` | SSE payload may leak in transit under network attacker. |
|
|
196
|
+
| FR-194 | Insecure gRPC-web endpoint without TLS | `createGrpcClient("http://api.example.com")` | `createGrpcClient("https://api.example.com")` | TypeScript/React | `CWE-319` | gRPC-web metadata/payload sent over unencrypted channel. |
|
|
197
|
+
| FR-195 | Weak hash: MD5 used for security decision | `const digest = md5(value)` | `const digest = await sha256(value, salt)` | TypeScript/React | `CWE-916` | Weak hash enables collision/forgery in integrity checks. |
|
|
198
|
+
| FR-196 | Weak hash: SHA1 used for token/signature verification | `const sig = sha1(payload)` | `const sig = await sha256(payload, salt)` | TypeScript/React | `CWE-916` | SHA1 no longer adequate for security-sensitive hashing. |
|
|
199
|
+
| FR-197 | Unsalted hash for local credential artifact | `const h = sha256(password)` | `const h = sha256(password + ":" + perUserSalt)` | TypeScript/React | `CWE-916` | Unsalted hashes vulnerable to rainbow/precomputation attacks. |
|
|
200
|
+
| FR-198 | Static hardcoded salt reused for all users | `const salt = "global-salt"` | `const salt = crypto.getRandomValues(new Uint8Array(16))` | TypeScript/React | `CWE-916` | Shared salt undermines hash hardening effectiveness. |
|
|
201
|
+
| FR-199 | CryptoJS MD5 used for session integrity marker | `CryptoJS.MD5(sessionJson).toString()` | `CryptoJS.SHA256(sessionJson + dynamicSalt).toString()` | TypeScript/React | `CWE-916` | MD5 integrity marker can be forged via collisions. |
|
|
202
|
+
| FR-200 | PBKDF2 iteration count too low in browser crypto | `pbkdf2(password, salt, 1000)` | `pbkdf2(password, salt, 310000)` | TypeScript/React | `CWE-916` | Low iteration KDF allows faster brute-force attacks. |
|
|
203
|
+
| FR-201 | Weak random seed mixed into client hash | `const digest = sha256(value + Math.random())` | `const digest = sha256(value + toHex(crypto.getRandomValues(new Uint8Array(16))))` | TypeScript/React | `CWE-916` | Predictable entropy reduces resistance to guessing. |
|
|
204
|
+
| FR-202 | Hash truncation to short length for security checks | `const sig = sha256(data).slice(0, 8)` | `const sig = sha256(data)` | TypeScript/React | `CWE-916` | Short digest truncation weakens collision resistance. |
|
|
205
|
+
| FR-203 | Client-side password verifier compares raw MD5 values | `if (md5(input) === storedHash) allow()` | `if (timingSafeEqual(sha256(input + salt), storedHash)) allow()` | TypeScript/React | `CWE-916` | Weak hash verifier allows offline cracking and bypass attempts. |
|
|
206
|
+
| FR-204 | Legacy hash algorithm selected via user-controlled param | `const alg = search.get("alg") || "md5"; hashWith(alg, data)` | `const alg = "sha256"; hashWith(alg, data)` | TypeScript/React | `CWE-916` | Attacker can downgrade hash algorithm at runtime. |
|
|
207
|
+
| FR-205 | Excessive fingerprinting: full userAgent sent to third-party telemetry | `telemetry.send({ ua: navigator.userAgent })` | `telemetry.send({ browserFamily: coarseUA(navigator.userAgent) })` | TypeScript/React | `CWE-359` | Detailed device fingerprint data exfiltrated externally. |
|
|
208
|
+
| FR-206 | Excessive fingerprinting: screen dimensions + color depth exported | `telemetry.send({ w: screen.width, h: screen.height, depth: screen.colorDepth })` | `telemetry.send({ viewportBucket: bucketViewport(screen.width, screen.height) })` | TypeScript/React | `CWE-359` | High-entropy display attributes increase user re-identification. |
|
|
209
|
+
| FR-207 | Excessive fingerprinting: timezone and locale sent raw to analytics | `send({ tz: Intl.DateTimeFormat().resolvedOptions().timeZone, lang: navigator.language })` | `send({ region: coarseRegion(), langFamily: coarseLang(navigator.language) })` | TypeScript/React | `CWE-359` | Combined locale signals contribute to unique fingerprint profile. |
|
|
210
|
+
| FR-208 | Excessive fingerprinting: Battery API values exported to telemetry | `navigator.getBattery().then(b => send({ level: b.level, charging: b.charging }))` | `// do not collect battery telemetry for external analytics` | TypeScript/React | `CWE-359` | Battery characteristics create unstable but identifying fingerprint. |
|
|
211
|
+
| FR-209 | Excessive fingerprinting: canvas fingerprint hash uploaded | `const fp = canvasFingerprint(); send({ fp })` | `// disable canvas fingerprint collection; use consented coarse metrics only` | TypeScript/React | `CWE-359` | Canvas hash is high-entropy tracking identifier. |
|
|
212
|
+
| FR-210 | Excessive fingerprinting: WebGL renderer/vendor sent externally | `send({ glVendor, glRenderer })` | `send({ gpuClass: coarseGpuClass(glVendor, glRenderer) })` | TypeScript/React | `CWE-359` | GPU details materially increase tracking uniqueness. |
|
|
213
|
+
| FR-211 | Excessive fingerprinting: installed fonts probe uploaded | `send({ fonts: detectedFonts })` | `send({ fontSupport: summarizeFontSupport(detectedFonts) })` | TypeScript/React | `CWE-359` | Font probing reveals highly identifying client profile. |
|
|
214
|
+
| FR-212 | Excessive fingerprinting: plugin list and mime types sent to vendor | `send({ plugins: navigator.plugins, mimes: navigator.mimeTypes })` | `send({ pluginCount: navigator.plugins.length })` | TypeScript/React | `CWE-359` | Plugin inventory contributes to persistent fingerprinting. |
|
|
215
|
+
| FR-213 | Excessive fingerprinting: hardwareConcurrency and deviceMemory exported | `send({ hc: navigator.hardwareConcurrency, mem: navigator.deviceMemory })` | `send({ deviceTier: coarseDeviceTier() })` | TypeScript/React | `CWE-359` | Hardware traits used for cross-session re-identification. |
|
|
216
|
+
| FR-214 | Excessive fingerprinting: full network info sent to analytics | `send({ downlink: conn.downlink, rtt: conn.rtt, type: conn.effectiveType })` | `send({ networkTier: coarseNetworkTier(conn.effectiveType) })` | TypeScript/React | `CWE-359` | Network metrics can be combined into tracking signature. |
|
|
217
|
+
| FR-215 | postMessage handler accepts wildcard from multiple origins map | `if (allowed.includes("*")) handle(e.data)` | `if (!allowedOrigins.includes(e.origin)) return; handle(e.data)` | TypeScript/React | `CWE-346` | Wildcard allowlist nullifies origin-based trust model. |
|
|
218
|
+
| FR-216 | Message listener lacks schema validation for sensitive command payload | `if (e.origin===TRUSTED) execute(e.data)` | `if (e.origin!==TRUSTED) return; const cmd = CommandSchema.parse(e.data); execute(cmd)` | TypeScript/React | `CWE-346` | Trusted origin alone insufficient without payload contract checks. |
|
|
219
|
+
| FR-217 | Mixed content image beacon leaks tokenized URL over HTTP | `new Image().src = "http://metrics.example/collect?sid=" + sid` | `new Image().src = "https://metrics.example/collect?sidRef=" + sidRef` | TypeScript/React | `CWE-319` | Tokenized identifiers exposed in plaintext telemetry beacon. |
|
|
220
|
+
| FR-218 | Insecure websocket reconnection fallback to `ws://` | `const wsUrl = secure ? "wss://api" : "ws://api"` | `const wsUrl = "wss://api"` | TypeScript/React | `CWE-319` | Reconnect path silently downgrades transport security. |
|
|
221
|
+
| FR-219 | Weak hash used for anti-tamper check in local storage | `if (md5(raw) !== tag) throw` | `if (sha256(raw + salt) !== tag) throw` | TypeScript/React | `CWE-916` | MD5 anti-tamper tag can be bypassed/collided. |
|
|
222
|
+
| FR-220 | Hash salt reused across environments via hardcoded constant | `const SALT = "prod-salt-v1"` | `const SALT = loadPerTenantSaltFromTrustedConfig()` | TypeScript/React | `CWE-916` | Shared constant salt weakens hash separation boundaries. |
|
|
223
|
+
| FR-221 | Fingerprinting collector runs without user consent gate | `collectFingerprintAndSend()` | `if (hasTelemetryConsent()) collectCoarseTelemetry()` | TypeScript/React | `CWE-359` | Privacy-invasive fingerprint collection executed without consent. |
|
|
224
|
+
| FR-222 | Telemetry SDK receives raw fingerprint bundle from client helper | `sdk.track("fp", buildFingerprintBundle())` | `sdk.track("fp", buildMinimizedTelemetryBundle())` | TypeScript/React | `CWE-359` | External SDK stores high-entropy user fingerprint bundle. |
|
|
225
|
+
| FR-223 | Privacy leak: combining battery + screen + UA in one export payload | `send({ ua, battery, screen })` | `send({ clientClass: coarseClientClass() })` | TypeScript/React | `CWE-359` | Multi-signal bundle significantly raises re-identification risk. |
|
|
226
|
+
| FR-224 | postMessage reply uses `event.origin` reflection without allowlist | `event.source?.postMessage(resp, event.origin)` | `if (!ALLOWLIST.includes(event.origin)) return; event.source?.postMessage(resp, event.origin)` | TypeScript/React | `CWE-346` | Origin reflection can amplify malicious cross-window channels. |
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"skill_id": "frontend-react",
|
|
3
|
+
"name": "Frontend React Security",
|
|
4
|
+
"activation_triggers": [
|
|
5
|
+
"react-tsx-component",
|
|
6
|
+
"react-dangerouslysetinnerhtml",
|
|
7
|
+
"react-router-navigate",
|
|
8
|
+
"react-state-storage"
|
|
9
|
+
],
|
|
10
|
+
"relevant_extensions": [
|
|
11
|
+
".ts",
|
|
12
|
+
".tsx",
|
|
13
|
+
".js",
|
|
14
|
+
".jsx"
|
|
15
|
+
],
|
|
16
|
+
"tools": [
|
|
17
|
+
"semgrep",
|
|
18
|
+
"syft",
|
|
19
|
+
"trufflehog"
|
|
20
|
+
],
|
|
21
|
+
"rules_path": "core/skills/frontend-react/patterns.md",
|
|
22
|
+
"few_shot_examples": "core/gold-standard-testbed/typescript/react_xss_danger.tsx",
|
|
23
|
+
"security_priority": 5
|
|
24
|
+
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
# Go Core
|
|
2
|
+
|
|
3
|
+
## Stack overview
|
|
4
|
+
|
|
5
|
+
**Go LTS 1.21/1.22** services: `net/http`, SQL/ORM, gRPC, `unsafe`/CGO edges, and concurrency. Metrics are prefixed **`GO`**.
|
|
6
|
+
|
|
7
|
+
## Top threats
|
|
8
|
+
|
|
9
|
+
- Command injection and unsafe `exec` (`GO-001`–`GO-008`, `GO-021`).
|
|
10
|
+
- SSRF, path traversal, open redirect (`GO-010`, `GO-011`, `GO-014`, `GO-026`).
|
|
11
|
+
- Weak crypto and JWT mistakes (`GO-013`, `GO-016`, `GO-018`, `GO-031`, `GO-040`).
|
|
12
|
+
- Concurrency and resource limits (`GO-009`, `GO-019`, `GO-023`, `GO-030`, `GO-032`).
|
|
13
|
+
|
|
14
|
+
## Pattern catalog
|
|
15
|
+
|
|
16
|
+
Complete Anti-Pattern / Safe-Pattern definitions live in [`patterns.md`](patterns.md). The table below is a **table of contents** by metric ID.
|
|
17
|
+
|
|
18
|
+
| ID | Metric | Stack |
|
|
19
|
+
|---|---|---|
|
|
20
|
+
| `GO-001` | Command Injection: `exec.Command("sh","-c", userInput)` | `action := r.URL.Query().Get("action")` `allowed := map[string][]string{"uptime": {"uptime"}}` `...` `exec.Command(allowed[action][0]).Run()` |
|
|
21
|
+
| `GO-002` | OS Exec Injection: `exec.Command("bash","-c",...)` с конкатенацией | `host := r.URL.Query().Get("host")` `if !hostRe.MatchString(host) { return }` `...` `exec.Command("ping", "-c", "1", host).Run()` |
|
|
22
|
+
| `GO-003` | Unsafe SQL Fragment Injection | `order := r.URL.Query().Get("order")` `if order != "name" && order != "created_at" { order = "name" }` `...` `q := "SELECT * FROM users ORDER BY " + order` |
|
|
23
|
+
| `GO-004` | Unsafe Reflection by Name | `m := r.URL.Query().Get("method")` `if m != "Health" && m != "Status" { return }` `...` `reflect.ValueOf(handler).MethodByName(m).Call(nil)` |
|
|
24
|
+
| `GO-005` | Plugin Loading from User Input | `name := r.URL.Query().Get("plugin")` `if _, ok := allowedPlugins[name]; !ok { return }` `...` `plugin.Open(allowedPlugins[name])` |
|
|
25
|
+
| `GO-006` | JavaScript Injection via goja/otto eval | `cmd := r.FormValue("cmd")` `if cmd != "normalize" { return }` `...` `vm.RunString("normalize(input)")` |
|
|
26
|
+
| `GO-007` | Template Expression Injection | `name := r.FormValue("template")` `if _, ok := safeTemplates[name]; !ok { return }` `...` `template.Must(template.ParseFiles(safeTemplates[name]))` |
|
|
27
|
+
| `GO-008` | Unsafe Command Router from User Field | `tool := payload["tool"]` `allowed := map[string][]string{"date": {"date"}}` `if _, ok := allowed[tool.(string)]; !ok { return }` `...` `exec.Command(allowed[tool.(string)][0]).Run()` |
|
|
28
|
+
| `GO-009` | Goroutine Leak: бесконечная goroutine без `context`-остановки | `go func(ctx context.Context) {` ` for {` ` select {` ` case <-ctx.Done():` ` return` ` default:` ` ...` ` }` ` }` `}(ctx)` |
|
|
29
|
+
| `GO-010` | Path Traversal: небезопасный путь через `filepath.Join(root, userInput)` | `name := r.URL.Query().Get("file")` `clean := filepath.Clean("/" + name)` `target := filepath.Join(root, clean)` `if !strings.HasPrefix(target, root) {` ` return` `}` |
|
|
30
|
+
| `GO-011` | SSRF: прямой `http.Get(userInputURL)` | `url := r.URL.Query().Get("url")` `host := parseHost(url)` `allowed := map[string]bool{"api.example.com": true}` `if !allowed[host] {` ` return` `}` `resp, _ := http.Get(url)` |
|
|
31
|
+
| `GO-012` | Unsafe Pointer Conversion: арифметика через `unsafe.Pointer` | `buf := make([]byte, n)` `...` `_ = buf[offset:]` `// avoid unsafe pointer arithmetic` |
|
|
32
|
+
| `GO-013` | Weak Crypto: использование MD5/SHA1 | `...` `h := sha256.New()` |
|
|
33
|
+
| `GO-014` | Open Redirect: redirect на URL из query без проверки | `next := r.URL.Query().Get("next")` `if !isRelativeOrAllowed(next) {` ` next = "/"` `}` `http.Redirect(w, r, next, http.StatusFound)` |
|
|
34
|
+
| `GO-015` | Log Injection: CR/LF в логах из пользовательского ввода | `userInput := r.URL.Query().Get("user")` `safe := strings.NewReplacer("\\n", "\\\\n", "\\r", "\\\\r").Replace(userInput)` `...` `log.Printf("User: %s", safe)` |
|
|
35
|
+
| `GO-016` | Hardcoded Credentials: секреты в константах/строках | `apiKey := os.Getenv("API_KEY")` `if apiKey == "" {` ` panic("missing API_KEY")` `}` |
|
|
36
|
+
| `GO-017` | Data Race: запись в общую переменную без `Mutex` | `...` `mu.Lock()` `counter = counter + 1` `mu.Unlock()` |
|
|
37
|
+
| `GO-018` | JWT Signature Validation Bypass: отсутствие проверки `alg` в `Keyfunc` | `token, _ := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {` ` if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {` ` return nil, fmt.Errorf("unexpected signing method")` ` }` ` return key, nil` `})` |
|
|
38
|
+
| `GO-019` | DB Connection Leak: `db.Query` без `defer rows.Close()` | `rows, err := db.Query(query)` `if err != nil {` ` return err` `}` `defer rows.Close()` `...` |
|
|
39
|
+
| `GO-020` | Insecure TLS Config: `InsecureSkipVerify: true` | `tr := &http.Transport{` ` TLSClientConfig: &tls.Config{InsecureSkipVerify: false},` `}` |
|
|
40
|
+
| `GO-021` | Unclosed File/Resource: `os.Open` без `defer Close()` | `f, err := os.Open(path)` `if err != nil {` ` return err` `}` `defer f.Close()` `...` |
|
|
41
|
+
| `GO-022` | Improper Output Encoding (XSS): небезопасный вывод пользовательского ввода | `tmpl := template.Must(template.New("x").Parse("Hello {{.Name}}"))` `...` `tmpl.Execute(w, map[string]string{"Name": name})` |
|
|
42
|
+
| `GO-023` | Missing Request Body Limit: чтение тела без лимита | `body, _ := io.ReadAll(io.LimitReader(r.Body, maxBytes))` `...` `_ = body` |
|
|
43
|
+
| `GO-024` | Debug Endpoint in Production: подключен `pprof` без feature flag | `if debugEnabled {` ` mux := http.NewServeMux()` ` ...` ` http.ListenAndServe("127.0.0.1:6060", mux)` `}` |
|
|
44
|
+
| `GO-025` | gRPC Missing Auth: RPC метод без проверки metadata/auth | `srv := grpc.NewServer(grpc.UnaryInterceptor(grpc_auth.UnaryServerInterceptor(authFunc)))` `...` `func authFunc(ctx context.Context) (context.Context, error) {` ` md, _ := metadata.FromIncomingContext(ctx)` ` ...` ` return ctx, nil` `}` |
|
|
45
|
+
| `GO-026` | Zip Slip: распаковка архива без проверки пути назначения | `for _, f := range zipReader.File {` ` targetPath := filepath.Join(dest, f.Name)` ` clean := filepath.Clean(targetPath)` ` if !strings.HasPrefix(clean, filepath.Clean(dest)+string(os.PathSeparator)) {` ` return fmt.Errorf("zip slip detected")` ` }` ` writeFile(clean, f)` `}` |
|
|
46
|
+
| `GO-027` | HTTP Proxy Header Injection: прямой прокси hop-by-hop заголовков | `proxy := &httputil.ReverseProxy{` ` Director: func(req *http.Request) {` ` ...` ` req.Header = cloneAllowedHeaders(r.Header)` ` stripHopByHop(req.Header)` ` },` `}` |
|
|
47
|
+
| `GO-028` | Unsafe Reflect-based Deep Copy: рекурсивный `reflect` без type-guard | `func CopyMessage(msg proto.Message) proto.Message {` ` ...` ` return proto.Clone(msg)` `}` |
|
|
48
|
+
| `GO-029` | Hardcoded Root CAs: встроенные PEM в `tls.Config` | `pem, err := os.ReadFile("/etc/ssl/certs/internal-ca.pem")` `...` `pool.AppendCertsFromPEM(pem)` |
|
|
49
|
+
| `GO-030` | gRPC Message Size Limit Missing: сервер без `MaxRecvMsgSize` | `srv := grpc.NewServer(grpc.MaxRecvMsgSize(4*1024*1024))` `...` `pb.RegisterApiServer(srv, api)` |
|
|
50
|
+
| `GO-031` | Insecure Randomness: `math/rand` для токенов/секретов | `b := make([]byte, 32)` `...` `if _, err := cryptorand.Read(b); err != nil {` ` return err` `}` |
|
|
51
|
+
| `GO-032` | Unbounded JSON Unmarshal: парсинг тела запроса без ограничения размера | `raw, _ := io.ReadAll(io.LimitReader(r.Body, maxBytes))` `...` `json.Unmarshal(raw, &payload)` |
|
|
52
|
+
| `GO-033` | GORM Raw SQL Injection: конкатенация в `.Where()`/`.Raw()` | `name := r.URL.Query().Get("name")` `...` `db.Where("name = ?", name).Find(&users)` `...` `db.Raw("SELECT * FROM users WHERE name = ?", name).Scan(&users)` |
|
|
53
|
+
| `GO-034` | Bypassing XSS protection via `template.HTML` | `input := r.URL.Query().Get("html")` `tmpl := template.Must(template.New("x").Parse("{{.Content}}"))` `...` `tmpl.Execute(w, map[string]string{"Content": input})` |
|
|
54
|
+
| `GO-035` | Sensitive Info Leak in Error Messages | `err := someInternalError` `...` `log.Printf("internal error: %v", err)` `return errors.New("internal server error")` |
|
|
55
|
+
| `GO-036` | Unsafe CGO Buffer: указатели в C без валидации буфера | `buf := []byte(input)` `cbuf := C.CBytes(buf)` `defer C.free(cbuf)` `...` `C.process(cbuf)` |
|
|
56
|
+
| `GO-037` | Prototype Pollution / Map Assignment: копирование JSON-ключей без валидации | `allowed := map[string]bool{"name": true, "email": true}` `for k, v := range incomingMap {` ` if allowed[k] {` ` targetMap[k] = v` ` }` `}` |
|
|
57
|
+
| `GO-038` | Improper XML Entity Handling: парсер с дефолтными внешними сущностями | `xmlParser := customxml.NewParser()` `...` `xmlParser.DisableExternalEntities(true)` `xmlParser.Parse(rawXML)` |
|
|
58
|
+
| `GO-039` | Regex DoS (ReDoS): сложный regex на длинном пользовательском вводе | `if len(longUserInput) > 2048 {` ` return` `}` `re := regexp.MustCompile(userRegex)` `...` `re.MatchString(longUserInput)` |
|
|
59
|
+
| `GO-040` | Hardcoded JWT Secret: ключ подписи зашит в коде | `jwtKey := []byte(os.Getenv("JWT_SECRET"))` `if len(jwtKey) == 0 {` ` panic("missing JWT_SECRET")` `}` |
|
|
60
|
+
|
|
61
|
+
## Verification
|
|
62
|
+
|
|
63
|
+
**Verification:** Check the gold testbed file(s) below for `Vulnerable: <ID>` markers (static Semgrep + `detection-matrix.md` ground truth).
|
|
64
|
+
|
|
65
|
+
- [`gold-standard-testbed/multi_lang_vulnerable/go_vulnerable.go`](../gold-standard-testbed/multi_lang_vulnerable/go_vulnerable.go)
|
|
66
|
+
|
|
67
|
+
After changing [`patterns.md`](patterns.md), run from the repo root:
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
python scripts/sync_semgrep.py
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## Workflow: Recon → Scan → Verify
|
|
74
|
+
|
|
75
|
+
### 1) Recon
|
|
76
|
+
- Map entrypoints, data flows, and trust boundaries for this stack.
|
|
77
|
+
- Identify which metrics in [`patterns.md`](patterns.md) apply to the code under review.
|
|
78
|
+
|
|
79
|
+
### 2) Scan
|
|
80
|
+
- Run Semgrep with `semgrep-rules/<skill>.yaml` (generated) and correlate with Anti-Patterns.
|
|
81
|
+
- Eliminate findings that cannot bind to a metric row.
|
|
82
|
+
|
|
83
|
+
### 3) Verify
|
|
84
|
+
- Confirm markers or scanner hits for touched IDs in the gold testbed when adding metrics.
|
|
85
|
+
- Emit findings as `Vulnerable: <PREFIX>-<NNN>` in written reviews.
|
|
86
|
+
|