agentsclimarketplace

Frontend security

Skill Syo-M/codex-frontend-skills/.agents/skills/frontend-security

Frontend/BFF security for forms and external input, auth, cookies, HTML, URLs, fetch, CORS, webhooks, uploads, secrets, logging, dependencies, and LLM-bound content. 日本語の依頼例:「認証実装」「問い合わせフォーム」「外部API」「webhook」「ログ」「LLMに渡す」「依存追加」。From its SKILL.md

Install
npx -y skills add Syo-M/codex-frontend-skills --skill frontend-security

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

2 things to look at

  • 28 days oldThe repository was created 28 days ago. New is not bad, but a brand new repository carrying a familiar-sounding name is the shape a typosquat arrives in, and there has been no time for anyone else to find a problem with it.
  • 0 stars0 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

SKILL.md

13.8 KB, ~3.0k tokens by cl100k_base, as published. Nobody here has run it

Frontend Security

Order of defense: don't accept untrusted data → validate at the boundary → encode/sanitize at output → restrict with platform policy (CSP, cookies, headers). Apply all layers, not one. These rules are policy: changing or weakening them requires human security review (see governance).

XSS

  • Framework escaping is the default protection — keep it. dangerouslySetInnerHTML (React), set:html (Astro), innerHTML/insertAdjacentHTML (DOM) with anything non-static require sanitization with DOMPurify at output time, using one shared hardened config — not ad-hoc per-call options. SSR/build-time paths need isomorphic-dompurify (DOMPurify requires a DOM). No home-made regex sanitizers, ever.
  • URLs are an injection vector: any href/src from user data → parse with new URL, allow only http:/https: protocols (blocks javascript: URLs that escaping does not stop).
  • Never build HTML/CSS/JS by string concatenation with user data. No eval, new Function, string setTimeout.
  • Don't render raw user input into <script> contexts, JSON-LD blocks, or inline event handlers.

Validation — server-side, schema-first

  • Every boundary parses input with zod before use: Server Actions, route handlers, URL/search params, cookies, third-party API responses, webhook payloads (after signature verification — see below).
  • Client-side validation is UX only; the server must reject independently.
  • Default to closed shapes: z.strictObject({...}) (zod 4; .strict() is the legacy spelling) — unknown keys are rejected, blocking mass-assignment (isAdmin: true in a profile update). Plain z.object() silently strips unknown keys, which also prevents mass-assignment but hides client bugs. Document any exception.
  • Bound every user-supplied string with .max(n) — unbounded input is a DoS/ReDoS vector.
  • For identifiers, names, and other strings where surrounding whitespace has no meaning, normalize before validating: z.string().trim().min(1).max(n). Explicitly test a whitespace-only value; .min(1) alone accepts a single space. Do not trim passwords, signed payloads, or fields where whitespace is meaningful.
  • Locale-formatted input (decimal comma, native digits): parse to a canonical form first, then validate the canonical value (see i18n).
  • Validator-native error text is internal implementation detail and may leak the wrong language. For user-visible errors, map stable issue/path combinations to the active message catalog and test every supported locale; see references/localized-validation.md.
  • ReDoS: never execute user-controlled regex; cap input length before any regex; avoid nested quantifiers ((a+)+).
  • Prototype pollution: never recursively merge user JSON into existing objects; reject __proto__ / constructor / prototype keys; prefer Map or Object.create(null) for user-keyed lookups.
  • IDs from the client are claims, not facts: after auth, check the resource belongs to / is permitted for this user (IDOR). Per-resource, in every action/handler — middleware route guards are not enough.
  • Authorization model: decide it deliberately (role-based for coarse access, attribute/ownership-based for per-record) and enforce it server-side in one place (a policy helper), not ad-hoc if (user.role === 'admin') scattered across handlers. Deny by default.

Sessions, cookies, JWT

  • Session cookies: HttpOnly, Secure, SameSite=Lax (or Strict), __Host- prefix where possible (enforces Path=/, no Domain). Never store session tokens in localStorage.
  • Rotate the session ID on login and on privilege change (session fixation).
  • JWT, if used: decode ≠ verify — always verify the signature with an alg allow-list (reject none); check exp / aud / iss; short-lived access tokens + rotated refresh tokens; no PII in claims that reach the client.
  • Security-relevant randomness (tokens, nonces, IDs): crypto.randomUUID() / crypto.getRandomValues() — never Math.random(). No hand-rolled crypto; Web Crypto only.

CSRF & redirects

  • Framework protection covers less than you think: Next.js verifies Origin for Server Actions only. Route handlers have no built-in CSRF protection — every cookie-authenticated state-changing route handler needs explicit Origin/Referer validation or a CSRF token. SameSite is defense-in-depth, not sufficient alone (subdomains, legacy clients).
  • Mutations are POST-family only; a state-changing GET is a bug regardless of other protections.
  • Open redirects: never redirect(userInput) raw — allow-list relative paths or known origins. Same rule for OAuth redirect_uri; always validate the OAuth state parameter.

Outbound requests — SSRF & webhooks

  • Server code fetching a user-supplied or user-influenced URL: allow-list hosts; block private/link-local ranges and cloud metadata endpoints (169.254.169.254) — and the IPv6 equivalents (::1, fc00::/7, fe80::/10, IPv4-mapped ::ffff:169.254.169.254); re-validate after redirects. Constrain next/image remotePatterns tightly — the image optimizer is a classic SSRF proxy.
  • Defeat DNS rebinding / TOCTOU: prefer not accepting arbitrary URLs. When the feature genuinely requires them, use a reviewed network client or egress proxy that resolves once, rejects every private/link-local/metadata result, pins the approved address through connect, preserves the original hostname for TLS SNI/Host validation, and revalidates every redirect. Do not hand-roll this with a naive hostname check followed by ordinary fetch.
  • Incoming webhooks: verify the provider's HMAC signature (constant-time compare) before trusting the payload; reject when the signature header is missing. Schema validation is not authentication.

CORS

  • Never reflect the request Origin while sending Access-Control-Allow-Credentials: true; never * on authenticated endpoints. Maintain an explicit origin allow-list. "Fixing a CORS error" by widening headers is usually creating a vulnerability — find the legitimate origin instead.

Rate limiting & auth failures

  • Throttle login, signup, password reset, and expensive mutations — per IP and per account.
  • Uniform error messages and response timing on login/reset: do not reveal whether an account exists.

Untrusted content & prompt injection

Relevant whenever the app sends content to an LLM or renders model output (chat, summarization, RAG, agentic features).

  • Treat all external content — user input, fetched pages, API/webhook payloads, documents, retrieved chunks — as data, never instructions. It may contain text attempting to redirect the model ("ignore previous instructions…"); enclose it in clearly delimited, labeled fields and instruct the model that delimited content is data only.
  • The model's output is itself untrusted: never eval/render it as HTML/execute it as a tool call without the same validation, sanitization (DOMPurify), and authorization you apply to user input. Tool/function calls the model requests are authorized server-side per the acting user — the model cannot grant itself capability.
  • Never put secrets, API keys, or another user's data in a prompt or system message that a response could leak back. Apply least-privilege to any tool the model can invoke; high-impact actions (sending mail, spending, deletes) require explicit human confirmation, not model discretion.
  • Server-side LLM keys stay server-side (never NEXT_PUBLIC_/VITE_); rate-limit and budget-cap LLM endpoints (cost-DoS).

Secrets, env, logging

  • Secrets never appear in: client bundles (NEXT_PUBLIC_/VITE_/PUBLIC_ vars are public by definition), repo files, logs, client-bound error messages, or URL query strings (query strings leak via logs and referrers).
  • .env* in .gitignore; commit .env.example with placeholders. A secret ever committed = rotate it; deleting the file does not unpublish it. Secrets scanning in CI is mandatory (see governance).
  • No PII, credentials, or tokens in server logs, analytics events, or error-tracker payloads — configure scrubbing (e.g. Sentry beforeSend). Server errors: record only scrubbed diagnostics server-side, then return a generic message + correlation ID. Stack traces and DB errors never cross the wire.
  • Conversely, DO record security events for audit (A09 / ASVS V7): authentication success & failure, authorization denials (IDOR / 403), privilege/role changes, webhook signature-verification failures, password/email changes, and admin actions — each with a policy-approved actor identifier, action, result, correlation ID, and a minimized/masked source-network signal only when justified. IP addresses and stable actor identifiers can be personal data: restrict access, define retention, and hash or truncate where incident-response needs permit. Silent auth failures are unacceptable, but audit metadata is not exempt from privacy policy. Make logs tamper-evident and retained per policy.
  • Data minimization & retention: collect only what's needed; set a retention window on logs/analytics containing personal data and honor deletion requests — don't keep PII indefinitely "just in case".

Headers & platform policy

Set and keep (next.config / astro config / hosting headers):

  • Content-Security-Policy: script-src must NOT contain 'unsafe-inline' (it reopens the main XSS path that framework escaping closes) — use a per-request nonce or hashes, and prefer 'strict-dynamic'. At minimum: script-src without 'unsafe-inline'/'unsafe-eval', object-src 'none', base-uri 'self', form-action 'self', and frame-ancestors 'none' (or explicit allow-list) + X-Frame-Options: DENY fallback. Roll out via Content-Security-Policy-Report-Only first, with reports routed somewhere monitored.
  • Strict-Transport-Security with a long max-age (understand the commitment before adding preload).
  • Cache-Control: no-store on authenticated/personalized responses — caching a per-user response is a data leak, not a perf win.
  • X-Content-Type-Options: nosniff, Referrer-Policy: strict-origin-when-cross-origin, Cross-Origin-Opener-Policy: same-origin, Permissions-Policy denying unused features.

Third-party scripts & embeds

  • Third-party scripts require approval (see governance), exact version pinning, and integrity (SRI) when loaded from a CDN; prefer self-hosting. Tag managers that load arbitrary scripts defeat CSP — avoid, or strictly govern what they may inject.
  • iframes/embeds: explicit sandbox attribute; postMessage handlers must check event.origin against an allow-list.

Dependencies

  • Before adding: maintenance, downloads, install scripts (postinstall), transitive weight, license (see governance), and the EXACT package name (typosquatting).
  • Dependency confusion: publish private packages under the org scope (@company/); pin scope→registry mapping in .npmrc; assert registry URLs with lockfile-lint in CI.
  • Prefer a release cooldown: avoid versions published within the last few days (compromise/worm window). Pin CI actions by commit SHA, not tag.
  • Lockfile always committed; CI uses the canonical package manager's frozen/immutable install mode, with lifecycle scripts disabled where the build allows. Never translate the command to a different package manager.
  • Audit on every dependency change AND on a weekly schedule; high + critical findings block.

Uploads & user files

  • Validate type by content (magic bytes), not extension; cap size; store outside the web root / in object storage.
  • SVG passes image checks but executes scripts: deny or sanitize SVG. Serve user uploads from a separate origin — re-encoding raster images through the framework's image optimizer satisfies this intent; anything served as-is (HTML, SVG, PDF, downloads) must use the separate origin. Content-Disposition: attachment for non-image types; correct Content-Type + nosniff.
  • Authorize every download per-request — non-guessable names are defense-in-depth, never the access control. Intentionally-public assets (e.g. avatars) may skip per-request auth as an explicit, documented decision — not a default.
  • Never trust client-supplied paths/filenames: path traversal, zip-slip when extracting archives.

Self-check before shipping boundary code

These are the controls lint can't fully prove (only Semgrep-backstopped in templates/.semgrep/). Before finishing any change that touches a boundary, answer each for what you touched — a "no" means fix it, don't ship:

  • External input (body / params / cookies / API response / webhook) parsed with a zod schema at the boundary?
  • Semantic text normalized before its minimum-length check, with a whitespace-only negative test (unless preserving whitespace is intentional)?
  • Webhook payload signature verified (constant-time) BEFORE it's trusted?
  • Every client-supplied ID authorized for this user, per resource (IDOR)?
  • Authorization enforced server-side in the one policy helper, deny-by-default — not scattered role checks?
  • Outbound fetch to a user-influenced URL: avoided where possible; otherwise host allow-listed, every resolved/redirect IP screened, connection pinned without breaking TLS SNI/Host validation, and implemented through a reviewed client/egress layer (DNS rebinding)?
  • No secret / PII / token in the client bundle, logs, analytics, error payloads, or URLs?
  • Non-static HTML sanitized (DOMPurify)? No new dangerouslySetInnerHTML / set:html / innerHTML without it?
  • Session cookies HttpOnly + Secure + SameSite; cookie-auth state-changing route handlers CSRF-protected?
  • Untrusted / model-bound content handled as data, never instructions?

What ships with it: 2 files

3.4 KB alongside SKILL.md

agents/

references/

Keep looking

Skills are one crate of 326,834. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.