文章 代码库 城市生活记忆 Claude Skill AI分享 问龙虾
返回 Claude Skill

安全最佳实践

软件安全最佳实践,漏洞防御、认证授权和安全编码

开发 社区公开 by Community

Security Best Practices

Overview

This skill provides a description of how to identify the language and frameworks used by the current context, and then to load information from this skill’s references directory about the security best practices for this language and or frameworks.

This information, if present, can be used to write new secure by default code, or to passively detect major issues within existing code, or (if requested by the user) provide a vulnerability report and suggest fixes.

Workflow

The initial step for this skill is to identify ALL languages and ALL frameworks which you are being asked to use or already exist in the scope of the project you are working in. Focus on the primary core frameworks. Often you will want to identify both frontend and backend languages and frameworks.

Then check this skill’s references directory to see if there are any relevant documentation for the language and or frameworks. Make sure you read ALL reference files which relate to the specific framework or language. The format of the filenames is <language>-<framework>-<stack>-security.md. You should also check if there is a <language>-general-<stack>-security.md which is agnostic to the framework you may be using.

If working on a web application which includes a frontend and a backend, make sure you have checked for reference documents for BOTH the frontend and backend!

If you are asked to make a web app which will include both a frontend and backend, but the frontend framework is not specified, also check out javascript-general-web-frontend-security.md. It is important that you understand how to secure both the frontend and backend.

If no relevant information is available in the skill’s references directory, think a little bit about what you know about the language, the framework, and all well known security best practices for it. If you are unsure you can try to search online for documentation on security best practices.

From there it can operate in a few ways.

  1. The primary mode is to just use the information to write secure by default code from this point forward. This is useful for starting a new project or when writing new code.

  2. The secondary mode is to passively detect vulnerabilities while working in the project and writing code for the user. Critical or very important vulnerabilities or major issues going against security guidance can be flagged and the user can be told about them. This passive mode should focus on the largest impact vulnerabilities and secure defaults.

  3. The user can ask for a security report or to improve the security of the codebase. In this case a full report should be produced describe anyways the project fails to follow security best practices guidance. The report should be prioritized and have clear sections of severity and urgency. Then offer to start working on fixes for these issues. See #fixes below.

Workflow Decision Tree

  • If the language/framework is unclear, inspect the repo to determine it and list your evidence.
  • If matching guidance exists in references/, load only the relevant files and follow their instructions.
  • If no matching guidance exists, consider if you know any well known security best practices for the chosen language and or frameworks, but if asked to generate a report, let the user know that concrete guidance is not available (you can still generate the report or detect for sure critical vulnerabilities)

Overrides

While these references contain the security best practices for languages and frameworks, customers may have cases where they need to bypass or override these practices. Pay attention to specific rules and instructions in the project’s documentation and prompt files which may require you to override certain best practices. When overriding a best practice, you MAY report it to the user, but do not fight with them. If a security best practice needs to be bypassed / ignored for some project specific reason, you can also suggest to add documentation about this to the project so it is clear why the best practice is not being followed and to follow that bypass in the future.

Report Format

When producing a report, you should write the report as a markdown file in security_best_practices_report.md or some other location if provided by the user. You can ask the user where they would like the report to be written to.

The report should have a short executive summary at the top.

The report should be clearly delineated into multiple sections based on severity of the vulnerability. The report should focus on the most critical findings as these have the highest impact for the user. All findings should be noted with an numeric ID to make them easier to reference.

For critical findings include a one sentence impact statement.

Once the report is written, also report it to the user directly, although you may be less verbose. You can offer to explain any of the findings or the reasons behind the security best practices guidance if the user wants more info on any findings.

Important: When referencing code in the report, make sure to find and include line numbers for the code you are referencing.

After you write the report file, summarize the findings to the user.

Also tell the user where the final report was written to

Fixes

If you produced a report, let the user read the report and ask to begin performing fixes.

If you passively found a critical finding, notify the user and ask if they would like you to fix this finding.

When producing fixes, focus on fixing a single finding at a time. The fixes should have concise clear comments explaining that the new code is based on the specific security best practice, and perhaps a very short reason why it would be dangerous to not do it in this way.

Always consider if the changes you want to make will impact the functionality of the user’s code. Consider if the changes may cause regressions with how the project works currently. It is often the case that insecure code is relied on for other reasons (and this is why insecure code lives on for so long). Avoid breaking the user’s project as this may make them not want to apply security fixes in the future. It is better to write a well thought out, well informed by the rest of the project, fix, then a quick slapdash change.

Always follow any normal change or commit flow the user has configured. If making git commits, provide clear commit messages explaining this is to align with security best practices. Try to avoid bunching a number of unrelated findings into a single commit.

Always follow any normal testing flows the user has configured (if any) to confirm that your changes are not introducing regressions. Consider the second order impacts the changes may have and inform the user before making them if there are any.

General Security Advice

Below is a few bits of secure coding advice that applies to almost any language or framework.

Avoid Using Incrementing IDs for Public IDs of Resources

When assigning an ID for some resource, which will then be used by exposed to the internet, avoid using small auto-incrementing IDs. Use longer, random UUID4 or random hex string instead. This will prevent users from learning the quantity of a resource and being able to guess resource IDs.

A note on TLS

While TLS is important for production deployments, most development work will be with TLS disabled or provided by some out-of-scope TLS proxy. Due to this, be very careful about not reporting lack of TLS as a security issue. Also be very careful around use of “secure” cookies. They should only be set if the application will actually be over TLS. If they are set on non-TLS applications (such as when deployed for local dev or testing), it will break the application. You can provide a env or other flag to override setting secure as a way to keep it off until on a TLS production deployment. Additionally avoid recommending HSTS. It is dangerous to use without full understanding of the lasting impacts (can cause major outages and user lockout) and it is not generally recommended for the scope of projects being reviewed by codex.


Reference: Golang General Backend Security

Go (Golang) Security Spec (Go 1.25.x, Standard Library, net/http)

This document is designed as a security spec that supports:

  1. Secure-by-default code generation for new Go code.
  2. Security review / vulnerability hunting in existing Go code (passive “notice issues while working” and active “scan the repo and report findings”).

It is intentionally written as a set of normative requirements (“MUST/SHOULD/MAY”) plus audit rules (what bad patterns look like, how to detect them, and how to fix/mitigate them).


0) Safety, boundaries, and anti-abuse constraints (MUST FOLLOW)

  • MUST NOT request, output, log, or commit secrets (API keys, passwords, private keys, session cookies, JWTs, database URLs with credentials, signing keys, client secrets).
  • MUST NOT “fix” security by disabling protections (e.g., InsecureSkipVerify, GOSUMDB=off for public modules, wildcard CORS + credentials, removing auth checks, disabling CSRF defenses on cookie-auth apps).
  • MUST provide evidence-based findings during audits: cite file paths, code snippets, build/deploy configs, and concrete values that justify the claim.
  • MUST treat uncertainty honestly: if a control might exist in infrastructure (reverse proxy, WAF, service mesh, platform config), report it as “not visible in app code; verify at runtime/config.”
  • MUST keep fixes minimal, correct, and production-safe; avoid introducing breaking changes without warning (especially around auth/session flows, and proxies).

1) Operating modes

1.1 Generation mode (default)

When asked to write new Go code or modify existing code:

  • MUST follow every MUST requirement in this spec.
  • SHOULD follow every SHOULD requirement unless the user explicitly says otherwise.
  • MUST prefer safe-by-default APIs and proven libraries over custom security code.
  • MUST avoid introducing new risky sinks (shell execution, dynamic template execution, serving user files as HTML, unsafe redirects, weak crypto, unbounded parsing, etc.).

1.2 Passive review mode (always on while editing)

While working anywhere in a Go repo (even if the user did not ask for a security scan):

  • MUST “notice” violations of this spec in touched/nearby code.
  • SHOULD mention issues as they come up, with a brief explanation + safe fix.

1.3 Active audit mode (explicit scan request)

When the user asks to “scan”, “audit”, or “hunt for vulns”:

  • MUST systematically search the codebase for violations of this spec.
  • MUST output findings in a structured format (see §2.3).

Recommended audit order:

  1. Build/deploy entrypoints: main.go, cmd/*, Dockerfiles, Kubernetes manifests, systemd units, CI workflows.
  2. Go toolchain & dependency policy: Go version, modules, go.mod/go.sum, proxy/sumdb settings, govulncheck usage.
  3. Secret management and config loading (env, files, secret stores) + logging patterns.
  4. HTTP server configuration (timeouts, body limits, proxy trust, security headers).
  5. AuthN/AuthZ boundaries, session/cookie settings, token validation.
  6. CSRF protections for cookie-authenticated state-changing endpoints.
  7. Template usage and output encoding (XSS), and any “render template from string” behavior (SSTI).
  8. File handling (uploads/downloads/path traversal/temp files), static file serving.
  9. Injection sinks: SQL, OS command execution, SSRF/outbound fetch, open redirects.
  10. Concurrency/resource exhaustion (unbounded goroutines/queues, missing timeouts/contexts).
  11. Use of unsafe / cgo / reflect in security-sensitive paths.
  12. Debug/diagnostic endpoints (pprof/expvar/metrics) exposure.
  13. Cryptography usage (randomness, password hashing).

2) Definitions and review guidance

2.1 Untrusted input (treat as attacker-controlled unless proven otherwise)

Examples include:

  • *http.Request fields: r.URL.Path, r.URL.RawQuery, r.Form, r.PostForm, headers, cookies, r.Body
  • Path parameters from routers (including values extracted from URL paths)
  • JSON/XML/YAML bodies, multipart form parts, uploaded files
  • Any data from external systems (webhooks, third-party APIs, message queues)
  • Any persisted user content (DB rows) that originated from users
  • Configuration values that might be attacker-influenced in some deployments (headers set by upstream proxies, environment variables in multi-tenant systems)

2.2 State-changing request

A request is state-changing if it can create/update/delete data, change auth/session state, trigger side effects (purchase, email send, webhook send), or initiate privileged actions.

2.3 Required audit finding format

For each issue found, output:

  • Rule ID:
  • Severity: Critical / High / Medium / Low
  • Location: file path + function/handler name + line(s)
  • Evidence: the exact code/config snippet
  • Impact: what could go wrong, who can exploit it
  • Fix: safe change (prefer minimal diff)
  • Mitigation: defense-in-depth if immediate fix is hard
  • False positive notes: what to verify if uncertain (edge configs, proxy behavior, auth assumptions)

3) Secure baseline: minimum production configuration (MUST in production)

This is the smallest “production baseline” that prevents common Go misconfigurations.

3.1 Toolchain, patching, and dependency hygiene (MUST)

  • MUST run a supported Go major version and keep to the latest patch releases.
  • MUST treat Go standard library patch releases as security-relevant (many security fixes land in stdlib components like net/http, crypto/*, parsing packages).
  • MUST use Go modules with committed go.mod and go.sum.
  • MUST NOT disable module authenticity mechanisms for public modules (checksum DB) unless you have a controlled, documented replacement.
  • MUST run govulncheck (source scan and/or binary scan) in CI and address findings.

3.2 HTTP server baseline (MUST for network-facing services)

If the program serves HTTP (directly or via a framework built on net/http):

  • MUST configure an http.Server with explicit timeouts and header limits.
  • MUST set request body size limits (global and per-route as needed).
  • MUST avoid exposing diagnostic endpoints (pprof/expvar) publicly.
  • SHOULD set a consistent set of security headers (or verify they are set at the edge).
  • MUST set cookie security attributes for any cookies you issue.
  • SHOULD implement rate limiting and abuse controls for auth and expensive endpoints.

Illustrative baseline skeleton (adjust to your project):

  • Create a dedicated mux (avoid implicit global defaults unless intentionally managed).
  • Wrap handlers with: panic-safe error handling, request ID, logging, auth, and limits.

4) Rules (generation + audit)

Each rule contains: required practice, insecure patterns, detection hints, and remediation.

GO-DEPLOY-001: Keep the Go toolchain and standard library updated (security releases)

Severity: Medium

NOTE: Upgrading dependencies and the core Go version can break projects in unexpected ways. Focus on only security-critical dependencies and if noticed, let the user know rather than upgrading automatically.

Required:

  • MUST run a supported Go major release and apply patch releases promptly.
  • SHOULD treat patch releases as security-relevant, even if your application code didn’t change.

Insecure patterns:

  • Production builds pinned to old Go versions without a patching process.
  • Docker images like golang:1.xx or custom base images that are not updated regularly.
  • CI pipelines that intentionally suppress Go updates.

Detection hints:

  • Inspect CI (.github/workflows, gitlab-ci.yml, etc.) for go-version: or toolchain setup.
  • Inspect Dockerfiles for FROM golang: tags.
  • Inspect go.mod go directive and any toolchain pinning.

Fix:

  • Upgrade to the latest patch of a supported Go version.
  • Add an automated check (CI) that fails when Go is below an approved minimum.

Notes:

  • Go publishes regular minor releases that frequently include security fixes across standard library packages.

GO-SUPPLY-001: Go module authenticity MUST NOT be disabled for public dependencies

Severity: High

Required:

  • MUST keep module checksum verification enabled for public modules.
  • SHOULD commit go.sum and treat changes as security-sensitive.
  • MUST NOT use insecure module fetching settings for public modules.
  • MAY configure private module behavior using GOPRIVATE/GONOSUMDB for private repos, but must do so narrowly and intentionally.

Insecure patterns:

  • GOSUMDB=off in CI or production build environments for public modules.
  • GONOSUMDB=* or overly broad patterns that effectively disable verification.
  • GOINSECURE=* or broad GOINSECURE patterns for public modules.
  • GOPROXY=direct everywhere without a clear policy.

Detection hints:

  • Search build configs for GOSUMDB, GONOSUMDB, GOINSECURE, GOPROXY, GOPRIVATE.
  • Look for documentation/scripts that recommend disabling checksum DB “to make builds work”.

Fix:

  • Restore defaults for public module verification.
  • For private modules:
    • Set GOPRIVATE=your.private.domain/*
    • Configure an internal proxy or direct fetching, and restrict GONOSUMDB to private patterns only.

Notes:

  • Disabling checksum verification removes an important integrity layer against targeted or compromised upstream delivery.

GO-CONFIG-001: Secrets must be externalized and never logged or committed

Severity: High (Critical if credentials are committed)

Required:

  • MUST load secrets from environment variables, secret managers, or secure config files with restricted permissions.
  • MUST NOT hard-code secrets in Go source, test fixtures that may reach production, or build args.
  • MUST NOT log secrets or full credential-bearing connection strings.
  • SHOULD fail closed in production if required secrets are missing.

Insecure patterns:

  • String constants containing tokens/keys/passwords.
  • .env files or config files with secrets committed to repo.
  • Logging os.Environ(), dumping full configs, or printing DSNs.

Detection hints:

  • Search for suspicious literals (API_KEY, SECRET, PASSWORD, Authorization:).
  • Inspect config loaders and logging statements.
  • Inspect CI logs or debug print paths.

Fix:

  • Move secrets to a secret store / environment variables.
  • Redact sensitive fields in logs.
  • Add secret scanning to CI and pre-commit.

GO-HTTP-001: HTTP servers MUST set timeouts and MaxHeaderBytes

Severity: High (DoS risk)

Required:

  • MUST set: ReadHeaderTimeout, and SHOULD set ReadTimeout, WriteTimeout, IdleTimeout as appropriate for the service.
  • MUST set MaxHeaderBytes to a justified limit for your application.
  • MUST NOT rely on default zero-values for timeouts in production for internet-facing servers.

Insecure patterns:

  • http.ListenAndServe(":8080", handler) with a default http.Server (no explicit timeouts).
  • &http.Server{} with timeouts left at zero.
  • Missing MaxHeaderBytes.

Detection hints:

  • Search for http.ListenAndServe(, ListenAndServeTLS(, Server{ and inspect configured fields.
  • Check for reverse proxies; even with a proxy, app-level timeouts still matter.

Fix:

  • Use http.Server{ReadHeaderTimeout: ..., ReadTimeout: ..., WriteTimeout: ..., IdleTimeout: ..., MaxHeaderBytes: ...}.
  • Calibrate timeouts per endpoint type (streaming vs JSON APIs).

Notes:

  • Net/http documents that these timeouts exist and that zero/negative values mean “no timeout”; production services should choose explicit values.

GO-HTTP-002: Request body and multipart parsing MUST be size-bounded

Severity: Medium (DoS risk; can be High for upload-heavy apps)

Required:

  • MUST enforce a global maximum request body size for endpoints that accept bodies.
  • MUST enforce strict multipart upload limits and avoid unbounded form parsing.
  • SHOULD enforce per-route limits when some endpoints legitimately need larger bodies.
  • SHOULD set upstream (proxy) limits as defense-in-depth.

Insecure patterns:

  • Reading r.Body with io.ReadAll(r.Body) without a size cap.
  • Calling r.ParseMultipartForm(...) with overly large limits (or forgetting size controls).
  • Accepting file uploads with no limits on file size, number of parts, or total body size.

Detection hints:

  • Search for io.ReadAll(r.Body), json.NewDecoder(r.Body), ParseMultipartForm, FormFile, multipart.
  • Look for missing http.MaxBytesReader or equivalent per-handler limiting.
  • Look for “upload” endpoints and check limits.

Fix:

  • Wrap request bodies with http.MaxBytesReader(w, r.Body, maxBytes) before parsing.
  • For multipart, set conservative limits and validate file sizes/part counts explicitly.
  • Set proxy limits (e.g., at ingress) in addition to app limits.

Notes:

  • There are known vulnerability classes and advisories related to excessive resource consumption in multipart/form parsing; treat unbounded parsing as a security issue.

GO-DEPLOY-002: Diagnostic endpoints (pprof/expvar/metrics) MUST NOT be publicly exposed

Severity: High

NOTE: This only applies to production configurations. These endpoints are often used for debug or dev endpoints. If found, confirm that it would be reachable from the actual production deployment.

Required:

  • MUST NOT expose net/http/pprof handlers on a public internet-facing listener without strong access controls.
  • SHOULD run diagnostics on a separate, internal-only listener (loopback/VPC-only) and require auth.
  • MUST review what diagnostic endpoints reveal (stack traces, memory, command lines, environment, internal URLs).

Insecure patterns:

  • Side-effect import import _ "net/http/pprof" in a server binary with a public mux.
  • /debug/pprof/* reachable without auth.
  • /debug/vars (expvar) reachable without auth.

Detection hints:

  • Search for net/http/pprof imports (including blank imports).
  • Search for route prefixes /debug/pprof, /debug/vars.
  • Check whether http.DefaultServeMux is used and whether any debug handlers register globally.

Fix:

  • Remove diagnostics from production builds, or bind them to an internal-only listener.
  • Add strong authentication/authorization (and ideally network-level restrictions).

Notes:

  • pprof is typically imported for its side effect of registering HTTP handlers under /debug/pprof/.

GO-HTTP-003: Reverse proxy and forwarded header trust MUST be explicit

Severity: High (auth, URL generation, logging/auditing correctness)

Required:

  • If behind a reverse proxy, MUST define which proxy is trusted and how client IP/scheme/host are derived.
  • MUST NOT trust X-Forwarded-For, X-Forwarded-Proto, Forwarded, or similar headers from the open internet.
  • MUST ensure “secure cookie” logic, redirects, and absolute URL generation do not rely on spoofable headers.

Insecure patterns:

  • Using r.Header.Get("X-Forwarded-For") as the client IP without validating the proxy boundary.
  • Deriving “is HTTPS” from X-Forwarded-Proto without confirming it came from a trusted proxy.
  • Using forwarded Host values for password reset links without allowlisting.

Detection hints:

  • Search for X-Forwarded-For, X-Forwarded-Proto, Forwarded, Real-IP, and any custom “client IP” helpers.
  • Inspect ingress/proxy configs; if not visible, mark as “verify at edge”.

Fix:

  • Enforce proxy trust at the edge and in app:
    • Accept forwarded headers only from known proxy IP ranges.
    • Prefer platform-provided mechanisms where available.
  • If generating external links, use a configured allowlisted canonical origin (not the request’s Host header).

GO-HTTP-004: Security headers SHOULD be set (in app or at the edge)

Severity: Medium

Required (typical web app serving browsers):

  • SHOULD set:
    • Content-Security-Policy (CSP) appropriate to the app. NOTE: It is most important to set the CSP’s script-src. All other directives are not as important and can generally be excluded for the ease of development.
    • X-Content-Type-Options: nosniff
    • Clickjacking protection (X-Frame-Options and/or CSP frame-ancestors)
    • Referrer-Policy and Permissions-Policy where appropriate
  • MUST ensure cookies have secure attributes (see GO-HTTP-005).

NOTE:

  • These headers may be set via reverse proxy/CDN; if not visible in app code, report as “verify at edge”.

Insecure patterns:

  • No security headers anywhere (app or edge) for a browser-facing app.
  • CSP missing for apps rendering untrusted content.

Detection hints:

  • Search for middleware setting headers: w.Header().Set("Content-Security-Policy", ...), etc.
  • Search for reverse proxy config that sets headers.

Fix:

  • Add centralized header middleware in Go, or configure at the edge.
  • Keep CSP realistic; avoid unsafe-inline where possible.

GO-HTTP-005: Cookies MUST use secure attributes in production

Severity: Medium

Required (production, HTTPS):

  • MUST set Secure on cookies that carry auth/session state. IMPORTANT NOTE: Only set Secure in production environment when TLS is configured. When running in a local dev environment over HTTP, do not set Secure property on cookies. You should do this conditionally based on if the app is running in production mode. You should also include a property like SESSION_COOKIE_SECURE which can be used to disable Secure cookies when testing over HTTP.
  • MUST set HttpOnly on auth/session cookies.
  • SHOULD set SameSite=Lax by default (or Strict if compatible), and only use None when necessary (and only with Secure).
  • SHOULD set bounded lifetimes (Max-Age/Expires) appropriate to the app.

Insecure patterns:

  • Setting auth/session cookies without Secure in HTTPS deployments.
  • Cookies without HttpOnly for session identifiers.
  • SameSite=None for cookie-authenticated apps without a strong CSRF strategy.

Detection hints:

  • Search for http.SetCookie, &http.Cookie{, Set-Cookie.
  • Inspect cookie flags in auth/session code.

Fix:

  • Set the correct fields on http.Cookie and centralize cookie creation.

Notes:

  • SameSite is defense-in-depth and does not replace CSRF protections for cookie-auth apps.

Severity: High

  • IMPORTANT NOTE: If cookies are not used for auth (e.g., pure bearer token in Authorization header with no ambient cookies), CSRF is not a risk for those endpoints.

Required:

  • MUST protect all state-changing endpoints (POST/PUT/PATCH/DELETE) that rely on cookies for authentication.
  • SHOULD use a well-tested CSRF library/middleware rather than rolling your own.
  • MAY use additional defenses (Origin/Referer checks, Fetch Metadata, SameSite cookies), but tokens remain the primary defense for cookie-authenticated apps. If tokens are impractical, or for small applications:
  • MUST at a minimum require a custom header to be set and set the session cookie SESSION_COOKIE_SAMESITE=lax, as this is the strongest method besides requiring a form token, and may be much easier to implement.

Insecure patterns:

  • Cookie-authenticated JSON endpoints that mutate state with no CSRF checks.
  • Using GET for state-changing actions.

Detection hints:

  • Enumerate all non-GET routes and identify auth mechanism.
  • Look for CSRF middleware usage; if absent, treat as suspicious in browser-facing apps.

Fix:

  • Add CSRF middleware and ensure it covers all state-changing routes.
  • If the service is an API intended for non-browser clients, avoid cookie auth; use Authorization headers.

GO-HTTP-007: CORS must be explicit and least-privilege

Severity: Medium (High if misconfigured with credentials)

Required:

  • If CORS is not needed, MUST keep it disabled.
  • If CORS is needed:
    • MUST allowlist trusted origins (do not reflect arbitrary origins)
    • MUST be careful with credentialed requests; do not combine broad origins with cookies
    • SHOULD restrict allowed methods/headers

Insecure patterns:

  • Access-Control-Allow-Origin: * paired with cookies (Access-Control-Allow-Credentials: true).
  • Reflecting Origin without validation.

Detection hints:

  • Search for Access-Control-Allow- header setting.
  • Search for CORS middleware configuration.

Fix:

  • Implement strict origin allowlists and minimal methods/headers.
  • Ensure cookie-auth endpoints are not exposed cross-origin unless required.

GO-XSS-001: Use html/template and avoid bypassing auto-escaping with untrusted data

Severity: High

Required:

  • MUST use html/template for HTML rendering (not text/template).
  • MUST NOT convert untrusted data into “trusted” template types (template.HTML, template.JS, template.URL, etc.).
  • SHOULD keep templates static and controlled by developers; treat dynamic templates as high risk.
  • MUST NOT serve user-uploaded HTML/JS as active content unless explicitly intended and safely sandboxed.

Insecure patterns:

  • text/template used to generate HTML.
  • Using template.HTML(userInput) or similar typed wrappers.
  • Directly writing unescaped user content into HTML responses.

Detection hints:

  • Search for text/template, template.New(...).Parse(...), and typed wrappers like template.HTML(.
  • Inspect handlers that return HTML with string concatenation.

Fix:

  • Use html/template and pass untrusted data as data, not markup.
  • If you must allow limited HTML, use a vetted HTML sanitizer and still be careful with attributes/URLs.

GO-SSTI-001: Never parse/execute templates from untrusted input (SSTI)

Severity: Critical

Required:

  • MUST NOT call template.Parse / template.ParseFiles / template.New(...).Parse(...) on template text influenced by untrusted input.
  • MUST treat “user-defined templates” as a special high-risk design:
    • MUST use heavy sandboxing and strict allowlists
    • MUST isolate execution (process/container boundary) if truly required

Insecure patterns:

  • tmpl := template.Must(template.New("x").Parse(r.FormValue("tmpl")))
  • Reading templates from uploads / DB entries and executing them in the same trust domain as server code.

Detection hints:

  • Search for .Parse( and trace the origin of the template string.
  • Look for “custom email templates”, “user theming templates”, etc.

Fix:

  • Replace with safe substitution mechanisms (no code execution).
  • If templates must be user-controlled, isolate and sandbox aggressively.

GO-PATH-001: Prevent path traversal and unsafe file serving

Severity: High

Required:

  • MUST NOT pass user-controlled paths to os.Open, os.ReadFile, http.ServeFile, or http.FileServer without strict validation and base-dir enforcement.
  • MUST treat .., absolute paths, and OS-specific path tricks as hostile input.
  • SHOULD store user uploads outside any static web root; serve through controlled handlers.
  • MUST avoid directory listing for sensitive file trees.

Insecure patterns:

  • http.ServeFile(w, r, r.URL.Query().Get("path"))
  • os.Open(filepath.Join(baseDir, userPath)) without checking that the result stays under baseDir
  • http.FileServer(http.Dir(".")) serving the project root or user-writable directories

Detection hints:

  • Search for ServeFile(, FileServer(, http.Dir(, os.Open(, ReadFile(, filepath.Join(.
  • Trace whether path components come from request/DB.

Fix:

  • Use an allowlist of file identifiers (e.g., database IDs) mapped to server-side paths.
  • Enforce base directory containment after cleaning and joining.
  • Serve active formats as downloads (Content-Disposition: attachment) unless explicitly intended.

GO-UPLOAD-001: File uploads must be validated, stored safely, and served safely

Severity: High

Required:

  • MUST enforce upload size limits (app + edge).
  • MUST validate file type using allowlists and content checks (not only extensions).
  • MUST store uploads outside executable/static roots when possible.
  • SHOULD generate server-side filenames (random IDs) and avoid trusting original names.
  • MUST serve potentially active formats safely (download attachment) unless explicitly intended.

Insecure patterns:

  • Accepting arbitrary file types and serving them back inline.
  • Using user-supplied filename as storage path.
  • Missing size/type validation.

Detection hints:

  • Search for multipart, FormFile, ParseMultipartForm, io.Copy to disk.
  • Check where files are stored and how they are served.

Fix:

  • Implement allowlist validation + safe storage + safe serving.
  • Add scanning/quarantine workflows where applicable.

GO-INJECT-001: Prevent SQL injection (parameterized queries / ORM)

Severity: High

Required:

  • MUST use parameterized queries or an ORM that parameterizes under the hood.
  • MUST NOT build SQL by string concatenation / fmt.Sprintf / string interpolation with untrusted input.

Insecure patterns:

  • fmt.Sprintf("SELECT ... WHERE id=%s", r.URL.Query().Get("id"))
  • query := "UPDATE users SET role='" + role + "' WHERE id=" + id

Detection hints:

  • Grep for SELECT, INSERT, UPDATE, DELETE and check how query strings are built.
  • Trace untrusted data into db.Query, db.Exec, QueryRow, etc.

Fix:

  • Replace with placeholders (?, $1, etc.) and pass parameters separately.
  • Validate and type-check IDs before use.

GO-INJECT-002: Prevent OS command injection; avoid shelling out with untrusted input

Severity: Critical to High (depends on exposure)

Required:

  • MUST avoid executing external commands with attacker-controlled strings.
  • If subprocess is necessary:
    • MUST use exec.CommandContext with an argument list (not sh -c).
    • MUST NOT pass untrusted input to a shell (bash -c, sh -c, PowerShell).
    • SHOULD use strict allowlists for any variable component (subcommand, flags, filenames).
  • MUST assume CLI tools may interpret attacker-controlled args as flags or special values.

Insecure patterns:

  • exec.Command("sh", "-c", userString)
  • exec.Command("bash", "-c", fmt.Sprintf("tool %s", user))
  • Calling the shell to get glob expansion for user-supplied globs.

Detection hints:

  • Search for os/exec, exec.Command(, CommandContext(, "sh", "bash", "-c".
  • Trace untrusted input into command name/args.

Fix:

  • Use library APIs instead of subprocesses.
  • Hardcode command and allowlist/validate args.
  • If a shell is unavoidable, escape robustly and treat as high risk (prefer avoiding).

Notes:

  • The Go os/exec package intentionally does invoke a shell; introducing sh -c reintroduces shell injection hazards.

GO-SSRF-001: Prevent SSRF in outbound HTTP requests

Severity: Medium (High in cloud/LAN environments)

  • Note: For small stand alone projects this is less important. It is most important when deploying into an LAN or with other services listening on the same server.

Required:

  • MUST treat outbound requests to user-provided URLs as high risk.
  • SHOULD allowlist hosts/domains for any user-influenced URL fetch.
  • SHOULD block access to localhost/private IP ranges/link-local addresses and cloud metadata endpoints.
  • MUST restrict schemes to http/https (no file:, gopher:, etc.).
  • MUST set client timeouts and restrict redirects.

Insecure patterns:

  • http.Get(r.URL.Query().Get("url"))
  • “URL preview” / “webhook test” endpoints that fetch arbitrary URLs.

Detection hints:

  • Search for http.Get, client.Do, and URL values derived from requests/DB.
  • Identify features that fetch remote resources.

Fix:

  • Parse URLs strictly; enforce scheme and allowlisted hostnames.
  • Resolve DNS and enforce IP-range restrictions (with care for DNS rebinding).
  • Set timeouts, disable redirects unless needed, and cap response sizes.

GO-HTTPCLIENT-001: Outbound HTTP clients MUST set timeouts and close bodies

Severity: High (DoS and resource exhaustion)

Required:

  • MUST set an overall timeout on http.Client usage (or equivalent per-request deadlines via context + transport timeouts).
  • MUST ensure resp.Body.Close() is called for all successful requests (typically defer resp.Body.Close() immediately after error check).
  • SHOULD limit response body reads (do not io.ReadAll unbounded responses).
  • SHOULD restrict redirects for security-sensitive fetches (SSRF, auth flows).

Insecure patterns:

  • Using http.DefaultClient / http.Get for user-influenced destinations with no timeout policy.
  • Missing defer resp.Body.Close() leading to resource leaks.
  • io.ReadAll(resp.Body) with no limit.

Detection hints:

  • Search for http.Get(, http.Post(, client := &http.Client{} without Timeout, client.Do( and missing closes.
  • Search for io.ReadAll(resp.Body).

Fix:

  • Use a configured client with timeouts.
  • Always close response bodies.
  • Use bounded readers (io.LimitReader) for large/untrusted responses.

Notes:

  • The net/http package exposes DefaultClient as a zero-valued http.Client, which can easily lead to “no timeout” behavior unless configured.

GO-REDIRECT-001: Prevent open redirects

Severity: Medium (can be High with auth flows)

Required:

  • MUST validate redirect targets derived from untrusted input (next, redirect, return_to).
  • SHOULD prefer only same-site relative paths.
  • SHOULD fall back to a safe default on validation failure.

Insecure patterns:

  • http.Redirect(w, r, r.URL.Query().Get("next"), http.StatusFound) with no validation.

Detection hints:

  • Search for http.Redirect( and check origin of the location.

Fix:

  • Allowlist internal paths or known domains.
  • Reject absolute URLs unless explicitly needed and allowlisted.

GO-CRYPTO-001: Cryptographic randomness MUST come from crypto/rand

Severity: High (Critical if used for auth/session tokens or keys)

Required:

  • MUST use crypto/rand for:
    • session IDs, password reset tokens, API keys, CSRF tokens, nonces
    • encryption keys, signing keys, salts when required
  • MUST NOT use math/rand for any security-sensitive value.
  • SHOULD use built-in helpers that produce appropriately strong tokens when available.

Insecure patterns:

  • math/rand.Seed(time.Now().UnixNano()) followed by token generation for auth or sessions.
  • Using UUIDv4-like constructs built from math/rand.

Detection hints:

  • Search for math/rand, rand.Seed, rand.Intn in code that touches auth/session/token flows.
  • Search for custom token generators.

Fix:

  • Switch to crypto/rand (rand.Reader, rand.Read, or secure token helpers).
  • Ensure sufficient entropy and use URL-safe encoding.

Notes:

  • The crypto/rand package provides secure randomness APIs and token generation helpers.

GO-AUTH-001: Password storage MUST use adaptive hashing (bcrypt/argon2id) and safe comparisons

Severity: High

Required:

  • MUST hash passwords using an adaptive password hashing function (bcrypt or argon2id).
  • MUST NOT store plaintext passwords or reversible encryption of passwords.
  • MUST compare secrets in constant time when relevant (tokens, MACs, API keys) to reduce timing leaks.
  • SHOULD ensure password policies do not exceed algorithm constraints (e.g., bcrypt has input length limits; handle long passphrases appropriately).

Insecure patterns:

  • sha256(password) stored as password hash.
  • Plaintext password storage.
  • Comparing secrets with == in timing-sensitive contexts.

Detection hints:

  • Search for sha1, sha256, md5 used on passwords.
  • Search for bcrypt/argon2 usage; if absent, suspect.
  • Search for == comparisons on tokens/API keys.

Fix:

  • Use bcrypt.GenerateFromPassword / CompareHashAndPassword or argon2id with recommended parameters.
  • Use constant-time compare helpers when comparing MACs/tokens.

Notes:

  • Go provides bcrypt in golang.org/x/crypto/bcrypt, and constant-time comparisons in crypto/subtle.

GO-CONC-001: Data races and concurrency hazards MUST be treated as security-relevant

Severity: Medium to High (depends on what races affect)

Required:

  • MUST run tests with the race detector (go test -race) in CI for security-sensitive services.
  • MUST fix detected races; do not suppress without deep justification.
  • SHOULD treat shared mutable state in handlers as high risk; enforce synchronization or avoid shared mutability.

Insecure patterns:

  • Global maps/slices mutated from multiple goroutines without a mutex.
  • Caches or auth/session state stored in globals without concurrency protection.
  • Racy access to authorization state (can lead to bypasses or inconsistent enforcement).

Detection hints:

  • Search for var someMap = map[...]... used in handlers.
  • Look for missing sync.Mutex, sync.Map, channels, or other synchronization.
  • Ensure CI includes -race and that it runs relevant tests.

Fix:

  • Add proper synchronization or redesign to avoid shared mutable state.
  • Add race tests and run them continuously.

Notes:

  • The Go race detector only finds races that occur in executed code paths; improve test coverage and run realistic workloads with -race where feasible.

GO-UNSAFE-001: Use of unsafe/cgo MUST be minimized and audited like memory-unsafe code

Severity: High (Critical in high-risk code paths)

Required:

  • SHOULD avoid importing unsafe in application code unless absolutely necessary.
  • If unsafe is used, MUST treat it as “manual memory safety” requiring careful review and test coverage.
  • If cgo is used, MUST treat the C/C++ boundary as memory-unsafe; apply secure coding practices on the C side and isolate where possible.

Insecure patterns:

  • Widespread unsafe.Pointer casts in parsing, serialization, auth, or network code.
  • cgo used for parsing or security boundaries without sandboxing.

Detection hints:

  • Search for import "unsafe", unsafe.Pointer, // #cgo, import "C".
  • Prioritize review where unsafe touches untrusted input.

Fix:

  • Replace unsafe/cgo usage with safe standard library alternatives where possible.
  • Isolate unsafe code in small, well-tested modules with fuzz/race tests.

Notes:

  • The unsafe package explicitly provides operations that step around Go’s type safety guarantees.

5) Practical scanning heuristics (how to “hunt”)

When actively scanning, use these high-signal patterns:

Toolchain & dependencies:

  • FROM golang: (Dockerfiles), go-version: (CI), toolchain go (go.mod), pinned old versions
  • GOSUMDB=off, GOINSECURE, GONOSUMDB, GOPROXY=direct
  • replace directives in go.mod to forks/paths
  • govulncheck missing in CI

HTTP server hardening:

  • http.ListenAndServe(, ListenAndServeTLS(, &http.Server{ with missing timeouts
  • ReadHeaderTimeout: 0, ReadTimeout: 0, WriteTimeout: 0, IdleTimeout: 0, missing MaxHeaderBytes

Body parsing / DoS:

  • io.ReadAll(r.Body), json.NewDecoder(r.Body) without size cap
  • ParseMultipartForm, FormFile, multipart.NewReader without explicit limits
  • Missing http.MaxBytesReader

Debug exposure:

  • import _ "net/http/pprof"
  • /debug/pprof, /debug/vars

Templates / XSS / SSTI:

  • text/template used for HTML output
  • template.HTML(, template.JS(, template.URL( with user-controlled data
  • .Parse( on user-controlled strings

Files:

  • http.ServeFile( with user path
  • http.FileServer(http.Dir( pointing at repo root or uploads
  • os.Open(filepath.Join(base, user)) without containment checks

Injection:

  • SQL building with fmt.Sprintf, string concatenation near db.Query/Exec
  • exec.Command("sh","-c", ...), exec.Command("bash","-c", ...)

SSRF / outbound HTTP:

  • http.Get(userURL), client.Do(req) where URL comes from request/DB
  • Missing client timeout, missing resp.Body.Close(), unbounded io.ReadAll(resp.Body)

Crypto:

  • math/rand in token/session generation
  • InsecureSkipVerify: true
  • Password hashing with sha256/md5 instead of bcrypt/argon2

Concurrency:

  • Shared maps/slices mutated from handlers without locks
  • CI lacking go test -race

Always try to confirm:

  • data origin (untrusted vs trusted)
  • sink type (template/SQL/subprocess/files/http)
  • protective controls present (limits, validation, allowlists, middleware, network controls)

6) Sources (accessed 2026-01-28)

Primary Go documentation:

OWASP Cheat Sheet Series (general web security):


Reference: Javascript Express Web Server Security

Express (Node.js) Web Security Spec (Express 5.x / 4.19.2+, Node.js LTS)

This document is designed as a security spec that supports:

  1. Secure-by-default code generation for new Express apps and routes.
  2. Security review / vulnerability hunting in existing Express code (passive “notice issues while working” and active “scan the repo and report findings”).

It is intentionally written as a set of normative requirements (“MUST/SHOULD/MAY”) plus audit rules (what bad patterns look like, how to detect them, and how to fix/mitigate them).


0) Safety, boundaries, and anti-abuse constraints (MUST FOLLOW)

  • MUST NOT request, output, log, or commit secrets (API keys, passwords, private keys, session secrets, cookies, tokens).
  • MUST NOT “fix” security by disabling protections (e.g., weakening cookie flags, disabling CSRF defenses for cookie-authenticated apps, enabling permissive CORS, trusting proxy headers from the open internet, turning on debugging/stack traces in production, disabling TLS without a replacement).
  • MUST provide evidence-based findings during audits: cite file paths, code snippets, middleware/config values, and runtime assumptions that justify the claim.
  • MUST treat uncertainty honestly: if a protection might exist in infrastructure (reverse proxy, gateway, WAF, CDN), report it as “not visible in app code; verify at runtime/config.”
  • MUST prefer vetted libraries and platform controls over “roll your own” crypto/auth/session/CSRF. Express explicitly expects the application to validate/handle user input correctly; it does not do this automatically. (Express)

1) Operating modes

1.1 Generation mode (default)

When asked to write new Express code or modify existing code:

  • MUST follow every MUST requirement in this spec.
  • SHOULD follow every SHOULD requirement unless the user explicitly says otherwise.
  • MUST prefer safe-by-default APIs and proven libraries over custom security code.
  • MUST avoid introducing new risky sinks (shell execution, dynamic code evaluation, unsafe redirects, serving user files as HTML, template rendering from untrusted strings, unsafe filesystem paths, SSRF URL fetch endpoints, etc.).

1.2 Passive review mode (always on while editing)

While working anywhere in an Express repo (even if the user did not ask for a security scan):

  • MUST “notice” violations of this spec in touched/nearby code.
  • SHOULD mention issues as they come up, with a brief explanation + safe fix.

1.3 Active audit mode (explicit scan request)

When the user asks to “scan”, “audit”, or “hunt for vulns”:

  • MUST systematically search the codebase for violations of this spec.
  • MUST output findings in a structured format (see §2.3).

Recommended audit order:

  1. Entrypoints (server/app bootstrap), deployment manifests, Dockerfiles, process manager config, CI/CD.
  2. Express settings + middleware stack order (helmet, parsers, auth, sessions, CSRF, CORS).
  3. Proxy trust (trust proxy) and IP/protocol/host handling. (Express)
  4. Auth flows, sessions, cookies, password reset links, redirect handling. (Express)
  5. State-changing routes + CSRF protections (cookie-authenticated apps). (OWASP Cheat Sheet Series)
  6. Template rendering and XSS defenses (HTML generation, CSP, res.locals). (OWASP Cheat Sheet Series)
  7. File handling (uploads + downloads + static files) and path traversal. (Express)
  8. Injection classes (SQL, NoSQL, command execution, unsafe deserialization). (OWASP Cheat Sheet Series)
  9. Outbound requests (SSRF) and webhook/callback delivery. (OWASP Cheat Sheet Series)
  10. Rate limiting / brute-force defenses / abuse controls. (Express)
  11. Dependency hygiene / lockfiles / npm audit / vulnerable Express versions. (Express)

2) Definitions and review guidance

2.1 Untrusted input (treat as attacker-controlled unless proven otherwise)

In Express, common untrusted inputs include:

  • req.params (route parameters)
  • req.query (query string parameters; can be strings/arrays/objects depending on parsing) (OWASP Cheat Sheet Series)
  • req.body from express.json(), express.urlencoded(), express.text(), express.raw() (Express)
  • req.headers / req.get(...)
  • req.cookies / req.signedCookies (if cookie parsing middleware is used)
  • Upload metadata and filenames (e.g., multer file.originalname, file.mimetype)
  • Any data from external systems (webhooks, third-party APIs, message queues)
  • Any persisted user content (DB rows) that originated from users

Special proxy note:

  • If trust proxy is enabled, values like req.ip, req.hostname, and req.protocol may be derived from X-Forwarded-* headers which can be attacker-controlled if your proxy chain is not correctly overwriting/removing them. (Express)

2.2 State-changing request

A request is state-changing if it can create/update/delete data, change auth/session state, trigger side effects (purchase, email send, webhook send), or initiate privileged actions.

2.3 Required audit finding format

For each issue found, output:

  • Rule ID:
  • Severity: Critical / High / Medium / Low
  • Location: file path + function/route/middleware name + line(s)
  • Evidence: the exact code/config snippet
  • Impact: what could go wrong, who can exploit it
  • Fix: safe change (prefer minimal diff)
  • Mitigation: defense-in-depth if immediate fix is hard
  • False positive notes: what to verify if uncertain

3) Secure baseline: minimum production configuration (MUST in production)

This is the smallest “production baseline” that prevents common Express misconfigurations.

Minimum baseline targets:

  • helmet() is used and configured (especially CSP where applicable), and fingerprinting is reduced (disable x-powered-by). (Express)

  • A custom 404 handler and a custom error handler exist, and production does not leak internal stack traces. (Express)

  • Cookie/session usage is deliberate:

    • Not using default session cookie names
    • Cookies use secure attributes (Secure, HttpOnly, SameSite) as appropriate
    • Cookie-backed sessions never store secrets (they are readable by the client)
    • Server-side sessions never use MemoryStore in production. (Express)
  • Request body parsing has explicit limits (express.json({ limit }), express.urlencoded({ limit, parameterLimit, depth })). (Express)

  • trust proxy is configured explicitly to match your proxy topology; not blindly true. (Express)

  • Login/auth endpoints have brute-force protection and rate limiting. (Express)

  • Dependencies are regularly audited/updated (npm audit + advisory response). (Express)


4) Rules (generation + audit)

Each rule contains: required practice, insecure patterns, detection hints, and remediation.

EXPRESS-INPUT-001: Treat all user input as untrusted and validate it

Severity: High

Required:

  • MUST validate and normalize untrusted inputs before using them in security-sensitive logic or dangerous sinks (DB queries, redirects, filesystem, HTML output, shell commands). Ensure the untrusted inputs are type checked and structure checked before using or passing forward.
  • SHOULD apply allowlists (known-good) rather than blocklists when feasible.
  • MUST reject or safely handle unexpected types/shapes in req.query, req.params, and req.body.

Insecure patterns:

  • Passing req.query, req.params, req.body directly into database/query builders, redirects, filesystem paths, or templates.
  • Assuming req.query.foo is always a string (it can be an array/object depending on parsing). (OWASP Cheat Sheet Series)

Detection hints:

  • Identify “untrusted-to-sink” flows: request → sink (res.redirect, SQL execution, sendFile, child_process, template render, outbound fetch).
  • Search for direct usage of req.query.*, req.body.*, req.params.* in sensitive calls.

Fix:

  • Add schema validation (e.g., zod/joi/express-validator) at route boundaries.
  • Normalize types (e.g., force IDs to integers; reject arrays when scalar expected).

Notes:

  • Express production security guidance explicitly says input validation/handling is the application’s responsibility. (Express)

EXPRESS-REDIRECT-001: Prevent open redirects; validate redirect targets

Severity: Medium

Required:

  • MUST validate redirect destinations derived from untrusted input (next, return_to, url).
  • SHOULD allowlist only same-site relative paths (preferred) or a strict allowlist of domains.
  • MUST fall back to a safe default when validation fails.

Insecure patterns:

  • res.redirect(req.query.next) with no validation.
  • res.redirect(req.body.url) or res.location(...) using untrusted URLs.

Detection hints:

  • Search for res.redirect( and res.location( and trace the source of the target.
  • Look for query params named next, redirect, return, url.

Fix:

  • Only allow relative paths (starting with /) and disallow //, backslashes, and encoded variants.
  • If cross-domain redirects are required, allowlist exact hosts and enforce https.

Notes:

  • Express documentation calls out open redirects as dangerous user input and shows validating the host before redirecting. (Express)
  • Keep Express updated: Express has had an open-redirect-related CVE affecting some versions, and upgrades are part of the mitigation posture. (NVD)

EXPRESS-HEADERS-001: Use Helmet (or equivalent) to set essential security headers

Severity: Medium

Required:

  • SHOULD use helmet() to set common security headers.
  • SHOULD configure CSP realistically (avoid unsafe-inline where possible) for pages that render user-influenced content.
  • SHOULD set X-Content-Type-Options: nosniff, clickjacking defenses (X-Frame-Options or CSP frame-ancestors), and appropriate referrer policy.

NOTE: It is most important to set the CSP’s script-src. All other directives are not as important and can generally be excluded for the ease of development.

Insecure patterns:

  • No security headers set in app code and no evidence they are set at the edge.
  • CSP missing on apps that display user content.
  • Misconfigured framing headers that unintentionally allow clickjacking.

Detection hints:

  • Search for helmet( usage; check if CSP is configured or disabled.
  • Search for res.setHeader( / res.set( for security header setting.
  • If not visible in app code, check nginx/CDN config; otherwise flag “verify at edge.”

Fix:

  • Add helmet() early in middleware order and configure:

    • CSP (contentSecurityPolicy)
    • Frame protections (frameguard or CSP frame-ancestors)
    • X-Content-Type-Options (noSniff)

Notes:

  • Express production security best practices recommend Helmet and list headers Helmet sets by default. (Express)
  • OWASP HTTP Headers guidance is a useful reference when tuning policies. (OWASP Cheat Sheet Series)

EXPRESS-FINGERPRINT-001: Reduce fingerprinting by disabling x-powered-by and customizing error/404 responses

Severity: Low (defense-in-depth)

Required:

  • SHOULD disable X-Powered-By using app.disable('x-powered-by').
  • SHOULD provide a custom 404 handler and a custom error handler to avoid distinct default responses and to control information leakage.

Insecure patterns:

  • Default X-Powered-By: Express header left enabled.
  • Default Express 404/error responses in production with identifiable formatting and/or stack traces.

Detection hints:

  • Search for app.disable('x-powered-by').
  • Check middleware tail for a custom 404 (app.use((req,res)=>...)) and a custom error handler (app.use((err,req,res,next)=>...)).
  • Check if NODE_ENV is correctly set for production behavior (see EXPRESS-ERROR-001). (Express)

Fix:

  • Add:

    • app.disable('x-powered-by')
    • A custom 404 handler
    • A custom error handler that logs server-side and returns generic messages client-side

Notes:

  • Express docs explicitly recommend disabling x-powered-by and adding your own not-found and error handlers. (Express)

Severity: Medium

Required:

  • MUST set cookie flags appropriately for any authentication/session cookie:

    • Secure when HTTPS (production) IMPORTANT NOTE: Only set Secure in production environment if TLS is configured. When running in a local dev environment over HTTP, do not set Secure property on cookies. You should do this conditionally based on if the app is running in production mode. You should also include a property like SESSION_COOKIE_SECURE which can be used to disable Secure cookies when testing over HTTP.
    • HttpOnly for auth/session cookies
    • SameSite set deliberately (Lax is a common baseline; Strict if compatible; None only with Secure and a justified cross-site need)
  • SHOULD avoid setting domain broadly (avoid “all subdomains” unless required).

  • SHOULD set bounded expiry appropriate to risk and UX.

Insecure patterns:

  • Session/auth cookies without HttpOnly.
  • Cookies without Secure in production HTTPS.
  • SameSite=None + cookie-authenticated state-changing endpoints without CSRF protections.

Detection hints:

  • Search for res.cookie(, Set-Cookie, cookie: { ... }, express-session, cookie-session.
  • Verify cookie flags in session middleware configuration.

Fix:

  • Set these attributes centrally in session/cookie middleware configuration.

Notes:

  • Express production security guidance lists cookie security options (secure, httpOnly, etc.). (Express)
  • res.cookie() ultimately sets Set-Cookie with options; defaults follow RFC 6265 behavior when options are omitted. (Express)
  • OWASP session management guidance is relevant for choosing flags and lifetimes. (OWASP Cheat Sheet Series)

Severity: Low (defense-in-depth)

Required:

  • SHOULD override the default session cookie name (e.g., do not keep connect.sid when using express-session).
  • SHOULD use a generic name (e.g., sessionId) unless you have a compatibility reason.

Insecure patterns:

  • express-session used with no name: configured (default cookie name).
  • Multiple apps on the same domain sharing a cookie name accidentally.

Detection hints:

  • Search for express-session config blocks; check for name:.

Fix:

  • Set name: 'sessionId' (or similar) in express-session options.

Notes:

  • Express docs explicitly recommend not using the default session cookie name to reduce fingerprinting. (Express)

EXPRESS-SESS-002: Session storage and lifecycle must be production-safe

Severity: High

Required:

  • MUST NOT use MemoryStore in production (it is not designed for production use).
  • MUST store session secrets outside source control and rotate them safely.
  • SHOULD regenerate sessions on login / privilege changes to reduce session fixation risk.
  • MUST NOT store sensitive secrets in client-readable cookie sessions.

Insecure patterns:

  • app.use(session({ store: new MemoryStore(), ... })) or missing store (defaults to MemoryStore).
  • Hard-coded for example: secret: 'keyboard cat' / secret: 's3Cur3' in repo.
  • Using cookie-session to store access tokens, refresh tokens, or PII.

Detection hints:

  • Search for express-session and look for MemoryStore usage or missing store.
  • Search for secret: in session config and check if it’s hard-coded.
  • Look for req.session = ... patterns and whether sensitive data is stored.

Fix:

  • Use a production session store (Redis, database-backed, etc.).
  • Load secrets from environment/secret manager.
  • On login: req.session.regenerate(...) or equivalent flow with safe privilege re-binding.

Notes:

  • express-session explicitly warns that MemoryStore is not designed for production. (Express)
  • express-session documents rotating secrets and session regeneration to guard against fixation. (Express)
  • Express notes that cookie-backed sessions serialize data into the cookie and that cookie data is visible to the client; keep it small and non-secret. (Express)

Severity: High

  • IMPORTANT NOTE: If cookies are not being used for auth (ie auth is via Authentication header or other passed token), then there is no CSRF risk.

Required:

  • MUST protect all state-changing endpoints (POST/PUT/PATCH/DELETE) that rely on cookies for authentication.
  • SHOULD use a well-understood CSRF mitigation (token-based is the typical baseline).
  • MAY add defense-in-depth: Origin/Referer validation, Fetch Metadata enforcement, SameSite cookies, custom header requirements for XHR/fetch—but do not treat these as a full replacement unless explicitly designed and justified.
  • MUST use at a minimum require a custom HTTP header if form based CRSF tokens are not practical, as this is the second strongest method.

IMPORTANT NOTE:

  • If authentication is done via Authorization: Bearer ... headers (and not cookies), classic browser CSRF is typically not applicable;

Insecure patterns:

  • Cookie-authenticated endpoints that change state with no CSRF protection.
  • Using GET for state-changing actions (amplifies CSRF risk).
  • “CSRF protection” that only checks a user-controlled field.

Detection hints:

  • Enumerate routes with methods other than GET/HEAD and identify whether cookies gate auth.
  • Look for presence/absence of CSRF middleware and token checks.
  • Check JSON APIs too, not only HTML forms.

Fix:

  • Implement CSRF tokens for cookie-authenticated flows.
  • Add Origin/Referer checks where feasible, and ensure SameSite is set appropriately.

Notes:

  • OWASP CSRF guidance and OWASP Node.js guidance both recommend anti-CSRF tokens as a standard control for web apps. (OWASP Cheat Sheet Series)

EXPRESS-CORS-001: CORS must be explicit and least-privilege

Severity: Medium (High if misconfigured with credentials)

Required:

  • If CORS is not needed, MUST keep it disabled.

  • If CORS is needed:

    • MUST allowlist trusted origins (do not reflect arbitrary Origin without validation).
    • MUST NOT combine broad origins with credentialed cookies (Access-Control-Allow-Credentials: true).
    • SHOULD restrict methods, headers, and exposed headers to what’s required.

Insecure patterns:

  • Access-Control-Allow-Origin: * with Access-Control-Allow-Credentials: true.
  • Reflecting Origin for all requests without allowlist validation.
  • Applying permissive CORS middleware globally when only a subset needs cross-origin access.

Detection hints:

  • Search for cors(, Access-Control-Allow-Origin, Access-Control-Allow-Credentials.
  • Inspect whether cookies are used for auth on endpoints exposed cross-origin.

Fix:

  • Implement strict origin allowlist and ensure credentialed requests only for intended origins.
  • Consider splitting CORS config per route group rather than global.

Notes:

  • OWASP HTTP header guidance covers security implications of response headers, including those that affect browser behavior; use it as a reference when reviewing header posture. (OWASP Cheat Sheet Series)

EXPRESS-PROXY-001: Reverse proxy trust (trust proxy) must be configured correctly

Severity: Medium (High if using IP based authentication)

Required:

  • If behind a reverse proxy/LB, MUST configure app.set('trust proxy', ...) to match the real proxy chain.
  • MUST NOT blindly set trust proxy = true unless you fully control the proxy behavior and header rewriting.
  • MUST ensure the last trusted proxy overwrites/removes X-Forwarded-For, X-Forwarded-Host, and X-Forwarded-Proto so clients cannot spoof them.

Insecure patterns:

  • app.set('trust proxy', true) in an app directly exposed to the internet or behind unknown proxies.
  • Using req.ip, req.protocol, req.hostname for security decisions without correct proxy trust configuration.
  • Rate limiting keyed by req.ip with spoofable forwarded headers.

Detection hints:

  • Search for app.set('trust proxy'.
  • Check infra docs (nginx/LB) for header rewriting behavior.
  • Identify any security logic using req.ip, req.ips, req.protocol, req.hostname.

Fix:

  • Set trust proxy to a hop count, explicit IP/subnet list, or a custom function matching your network.
  • Ensure proxies overwrite forwarded headers.

Notes:

  • Express explicitly warns that when trust proxy is true, the client IP is derived from X-Forwarded-For, and if proxies don’t overwrite forwarded headers, the client can provide any value. It also describes that enabling trust proxy impacts req.hostname and req.protocol derived from forwarded headers. (Express)

EXPRESS-BODY-001: Request body size and parsing limits MUST be set appropriately

Severity: Low

Required:

  • SHOULD set explicit body size limits for:

    • express.json({ limit })
    • express.urlencoded({ limit, parameterLimit, depth })
  • SHOULD only enable the parsers you need; do not parse large bodies by default for all routes.

  • SHOULD enforce additional limits at the reverse proxy / gateway level.

Insecure patterns:

  • No explicit body limits (accepting arbitrarily large JSON/urlencoded).
  • Global parsers applied to all routes when only some need bodies.
  • parameterLimit very high without justification (DoS potential).

Detection hints:

  • Search for express.json( and confirm limit is set (or consciously accepted).
  • Search for express.urlencoded( and inspect limit, parameterLimit, and depth.
  • Review upload/webhook endpoints for special parsing needs.

Fix:

  • Configure parsers with conservative defaults and override per route group when needed.

Notes:

  • Express documents express.json options (including limit, defaulting to 100kb) and explicitly notes req.body is untrusted and should be validated. (Express)
  • Express documents express.urlencoded options including limit, parameterLimit, and depth. (Express)
  • OWASP Node.js guidance also recommends setting request size limits. (OWASP Cheat Sheet Series)

EXPRESS-INPUT-002: Prevent HTTP Parameter Pollution and type confusion in req.query

Severity: Medium

Required:

  • MUST treat req.query values as potentially multi-valued (array/object), depending on query parsing.
  • SHOULD reject ambiguous multi-valued parameters for security-sensitive fields (e.g., role, isAdmin, redirect, amount, userId).
  • SHOULD consider explicit parsing or dedicated middleware if parameter pollution is a concern.

Insecure patterns:

  • if (req.query.admin) { ... } without type checks (arrays/objects may coerce truthy).
  • Passing req.query directly into ORM/NoSQL query objects.

Detection hints:

  • Search for security-sensitive comparisons on req.query.* without type enforcement.
  • Look for code that assumes query params are strings.

Fix:

  • Validate shape: enforce string-only for certain params and reject arrays/objects.
  • Normalize query parsing settings (simple vs extended) where applicable and documented.

Notes:

  • OWASP Node.js cheat sheet explicitly highlights that Express query parsing can produce strings, arrays, or objects and recommends preventing HTTP Parameter Pollution. (OWASP Cheat Sheet Series)

EXPRESS-XSS-001: Prevent reflected/stored XSS in HTML responses and templating

Severity: High

Required:

  • MUST escape untrusted content in HTML output (templates should auto-escape by default; do not bypass).
  • MUST NOT inject untrusted strings into HTML without escaping/sanitization.
  • SHOULD set CSP (via Helmet) for apps rendering user-controlled content.
  • SHOULD keep res.locals free of user-controlled input intended for templates unless it is validated/escaped.

Insecure patterns:

  • res.send("<div>" + req.query.q + "</div>")
  • Passing untrusted HTML through “safe” template flags/filters.
  • Writing untrusted strings into res.locals and then rendering without escaping.

Detection hints:

  • Search for res.send( with strings containing user input.
  • Search for template “safe” flags (engine-specific) and trace data origin.
  • Search for assignments to res.locals and whether they might contain untrusted data.

Fix:

  • Use a template engine with autoescaping; pass only validated data.
  • For rich text that must contain HTML, use a trusted sanitizer and an allowlist policy.
  • Add CSP with realistic directives.

Notes:

  • Express API docs explicitly warn that res.locals “should not contain user-controlled input” and is often used to expose things like CSRF tokens to templates. (Express)
  • OWASP XSS prevention guidance provides standard output-encoding and policy recommendations. (OWASP Cheat Sheet Series)
  • Helmet can mitigate some XSS classes via headers such as CSP. (Express)

EXPRESS-TEMPLATE-001: Never render untrusted templates or template paths (SSTI / LFI risk)

Severity: Critical (if you can prove template strings/paths are user/attacker-controlled)

Required:

  • MUST NOT render templates whose contents or template path/name is influenced by untrusted input.
  • MUST NOT load templates from user-controlled filesystem locations.
  • SHOULD treat “email template editors”, “theme engines”, and “CMS-like template storage” as high-risk designs requiring sandboxing and isolation.

Insecure patterns:

  • res.render(req.query.view, data) where view is not allowlisted.
  • Rendering a template from a string that includes user input (engine-specific).
  • Loading templates from uploads directories.

Detection hints:

  • Search for res.render( where the first argument is derived from request/DB without allowlist.
  • Search for template compilation APIs (engine-specific) fed by user content.

Fix:

  • Use allowlisted template names and a fixed templates directory.
  • If user-defined templates are required, implement strict sandboxing and isolate execution.

Notes:

  • Express’s template system depends on the chosen engine; assume unsafe if user input influences template selection or source.

EXPRESS-FILES-001: Prevent path traversal and unsafe file serving (sendFile/download)

Severity: High

Required:

  • MUST NOT pass user-controlled filesystem paths directly to res.sendFile() / res.download() / filesystem APIs.
  • SHOULD use res.sendFile with a fixed root and strict options (e.g., deny dotfiles) when serving user-selected files from a directory.
  • MUST enforce authorization checks before serving user-specific files.

Insecure patterns:

  • res.sendFile(req.query.path) or res.download(req.params.file) with no root restriction.
  • File-serving routes that accept .. segments, encoded traversal, or absolute paths.

Detection hints:

  • Search for res.sendFile( and trace the path argument origin.
  • Search for res.download( and trace the path argument origin.
  • Look for fs.readFile/createReadStream on paths derived from requests.

Fix:

  • Use an identifier-to-path mapping stored server-side (DB), not raw paths from clients.
  • Use root: <trusted_base_dir> and dotfiles: 'deny' where appropriate; validate the filename component strictly.

Notes:

  • Express’s res.sendFile docs show using a root option and dotfiles: 'deny' as part of a safe serving configuration. (Express)
  • res.download transfers the file as an attachment, but you still must control/validate the underlying path. (Express)

EXPRESS-STATIC-001: Harden express.static / serve-static and never serve untrusted uploads as active content

Severity: Medium (if serving untrusted user files if there are not robust limits tot eh file extensions)

Required:

  • MUST NOT serve user uploads from a public static directory as active content (especially HTML/JS/SVG) unless explicitly intended and sandboxed. If sure that the content is inactive (png, jpg, other images etc) then it may be safe. It may be good to validate image file extensions are allow-listed before serving them.

  • SHOULD configure static serving to:

    • deny/ignore dotfiles
    • avoid unintended directory indexes if not needed
    • apply appropriate cache controls for immutable assets

Insecure patterns:

  • app.use(express.static('uploads')) where users can upload arbitrary files.
  • Serving uploaded HTML or SVG inline from the same origin as the app.

Detection hints:

  • Search for express.static( and identify served directories.
  • Compare served directories with upload storage locations.
  • Check for dotfiles and index options in static middleware.

Fix:

  • Store uploads outside any static web root and serve via controlled routes that set safe Content-Type and Content-Disposition: attachment when appropriate.
  • Configure express.static(root, { dotfiles: 'deny'|'ignore', index: false (if desired) }).

Notes:

  • Express documents express.static options, including dotfiles behavior and index. (Express)

EXPRESS-UPLOAD-001: File uploads must be validated, stored safely, and served safely

Severity: Low - Medium

Required:

  • SHOULD enforce upload size limits (app + edge).
  • MUST validate file type using allowlists and content checks (not only filename extension).
  • MUST store uploads outside executable/static roots when possible.
  • SHOULD generate server-side filenames (random IDs); do not trust original names.
  • MUST serve potentially active formats safely (download attachment) unless explicitly intended.

Insecure patterns:

  • Accepting arbitrary file types and serving them back inline.
  • Using file.originalname as the storage path.
  • Missing size/type validation.

Detection hints:

  • Look for multer/busboy/formidable usage and check for limits.
  • Check where uploaded files are written and how they are served.
  • Check whether uploads end up under public/ or any express.static root.

Fix:

  • Implement allowlist validation + safe storage + safe serving, per OWASP upload guidance.

Notes:

  • OWASP File Upload guidance covers allowlists, content validation, storage, and safe serving patterns. (OWASP Cheat Sheet Series)

EXPRESS-INJECT-001: Prevent SQL injection (use parameterized queries / ORM)

Severity: High

Required:

  • MUST use parameterized queries or an ORM/query builder that parameterizes under the hood.
  • MUST NOT build SQL via string concatenation/template literals with untrusted input.

Insecure patterns:

  • db.query(`SELECT * FROM users WHERE id = ${req.query.id}`)
  • "SELECT ... WHERE name = '" + req.body.name + "'"

Detection hints:

  • Grep for SELECT, INSERT, UPDATE, DELETE strings in JS/TS.
  • Trace untrusted input into .query(...), .execute(...), or raw SQL APIs.

Fix:

  • Replace with parameterized queries (placeholders) or ORM query APIs.
  • Validate types (e.g., integer IDs) before querying.

Notes:


EXPRESS-INJECT-002: Prevent NoSQL injection / operator injection (Mongo-style)

Severity: High (app-dependent)

Required:

  • MUST validate types and schemas for any query object built from untrusted input.
  • MUST prevent operator injection (e.g., $ne, $gt, $where) if user input is merged into query objects.
  • SHOULD consider defensive libraries/middleware when appropriate.

Insecure patterns:

  • collection.find(req.body) where the body is attacker-controlled.
  • Merging req.query/req.body into Mongo queries without schema validation.

Detection hints:

  • Search for find(, findOne(, aggregate( calls where argument is request-derived.
  • Check for patterns like { ...req.query } or Object.assign(query, req.body).

Fix:

  • Use schema validation at boundary; explicitly construct query objects from validated fields only.

Notes:

  • OWASP Node.js cheat sheet discusses input validation and mentions Node ecosystem modules commonly used for sanitization in NoSQL contexts. (OWASP Cheat Sheet Series)

EXPRESS-CMD-001: Prevent OS command injection (child_process)

Severity: Critical to High (depends on exposure), please prove it is user/attacker controlled

Required:

  • MUST avoid executing shell commands with untrusted input.

  • If subprocess is necessary:

    • MUST avoid exec() / execSync() with attacker-influenced strings
    • MUST NOT use shell: true with attacker-influenced data
    • SHOULD use spawn() with an argument array and strict allowlists. Ensure the executable is hardcoded or allow-listed, do not use a user supplied command name.
    • SHOULD place user-controlled values after -- when supported by the subcommand to avoid flag injection

Insecure patterns:

  • exec(req.query.cmd)
  • exec(convert ${userPath} …)
  • spawn('sh', ['-c', userString])
  • spawn(userString, ['foo'])

Detection hints:

  • Search for child_process, exec(, execSync(, spawn(, fork(.
  • Trace request/DB data into command construction.

Fix:

  • If possible, write the functionality in javascript or use a library instead of subprocess.
  • If unavoidable, hard-code command and strictly allowlist parameters.

Notes:


EXPRESS-SSRF-001: Prevent server-side request forgery (SSRF) in outbound HTTP

Severity: Medium (High in cloud/LAN deployments)

NOTE: This is mostly only applicable to apps which will be deployed in a cloud/LAN setup or have other http services on the same box. Sometimes the feature requires this functionality unavoidably (webhooks).

Required:

  • MUST treat outbound requests to user-provided URLs as high risk if there are other reachable private http endpoints.

  • SHOULD validate and restrict destinations (allowlist hosts/domains) for any user-influenced URL fetch.

  • SHOULD block access to:

    • localhost / private IP ranges / link-local
    • cloud metadata endpoints
  • MUST allow only http/https for URL fetch features (to avoid schemas such as file:,javascript:)

  • SHOULD set timeouts and restrict redirects.

Insecure patterns:

  • fetch(req.query.url)
  • “URL preview” / “import from URL” endpoints that accept arbitrary URLs.

Detection hints:

  • Search for fetch(, axios(, got(, request(, node-fetch usage where URL originates from users/DB.
  • Review webhook testers, previewers, image fetchers.

Fix:

  • Enforce scheme allowlist, host allowlist, DNS/IP resolution checks, timeouts, and redirect policy.
  • Consider network egress controls at infrastructure level.

Notes:


EXPRESS-ERROR-001: Error handling MUST not leak sensitive details in production

Severity: Low

Required:

  • SHOULD define a centralized error handler (app.use((err, req, res, next) => ...)) at the end of middleware.
  • MUST avoid returning stack traces, internal error messages, or secrets to clients in production.
  • SHOULD log errors server-side with appropriate redaction.
  • SHOULD ensure the app runs with production settings so default behavior doesn’t leak details.
  • MUST avoid logging or returning sensitive information such as secrets, env vars, sessions, cookies in error messages in production.

Insecure patterns:

  • Returning err.stack to clients.
  • Using dev-only error middleware in production.
  • NODE_ENV left as development, causing verbose error responses.

Detection hints:

  • Verify there is a final error-handling middleware.
  • Search for res.status(500).send(err) or similar.
  • Check production environment variables and startup scripts.

Fix:

  • Add a production-safe error handler that returns generic messages and logs details internally.
  • Ensure environment is configured for production behavior.

Notes:

  • Express production security guidance recommends custom error handling. (Express)
  • Express error handling docs describe the default error handler behavior and how production mode affects what is exposed. (Express)

EXPRESS-AUTH-001: Prevent brute-force attacks against authorization endpoints

Severity: Medium

NOTE: This is highly application specific and while it is good to bring to the attention of the user, it is hard to fix without additional complex configurations. Prefer to inform the user and if they request you to help implement a solution, help walk them through possible solutions.

Required:

  • SHOULD protect login/auth endpoints against brute forcing.

  • SHOULD rate-limit by:

    1. consecutive failed attempts per username+IP
    2. failed attempts per IP over a time window

Insecure patterns:

  • Unlimited login attempts.

Detection hints:

  • Identify all auth endpoints and check for rate limiting/throttling.
  • Search for rate-limiter-flexible, express-rate-limit, or gateway policies.

Fix:

  • Implement rate-limiting/throttling (app or edge). Express docs point to rate-limiter-flexible as a tool for this approach. (Express)

Notes:


EXPRESS-DEPS-001: Dependency and patch hygiene (Express + Node + critical middleware)

Severity: Medium / Low

NOTE: npm audit often returns a large number of insignificant “vulnerabilities” which do not actually matter. You should only focus on Express or other extremely critical packages, ignoring ones listed in dev tools, bundlers, etc.

Do not upgrade packages without concent from the user. This may break existing code in unexpected ways. Instead, inform them of the outdated packages.

Required:

  • MUST keep Express on a maintained version line (avoid EOL major versions).
  • MAY use npm audit in CI and during maintenance work.
  • SHOULD pin dependencies via lockfiles and review major updates carefully.

Insecure patterns:

  • Running EOL Express versions (e.g., very old major lines).
  • Ignoring npm audit findings without triage.
  • Unpinned dependency ranges that auto-upgrade into insecure versions.

Detection hints:

  • Check package.json and lockfiles for express version and other critical middleware versions.
  • Inspect CI pipelines for npm audit/SCA steps.

Fix:

  • Upgrade to latest stable Express and apply patches.
  • Add automated dependency scanning and upgrade process.

Notes:

  • Express production security guidance emphasizes that dependency vulnerabilities can compromise the app, and recommends npm audit. (Express)
  • Track security issues affecting Express versions (including known open-redirect-related CVEs). (NVD)

EXPRESS-DOS-001: Configure DoS protections (timeouts, limits, reverse proxy)

Severity: Low

NOTE: It may be hard to tell from the provided application context if the application runs behind a reverse proxy. You can inform the user or recommend one, but do not attempt to configure one without them initiating it. This is highly deployment dependant.

Required:

  • SHOULD use a reverse proxy to provide caching, load balancing, and filtering controls when feasible.
  • MAY configure server/proxy timeouts and connection limits to reduce exposure to Slowloris and similar DoS patterns.
  • MUST ensure server/socket errors are handled so malformed connections do not crash the process. (Express should handle exceptions, but there are edgecases)

Insecure patterns:

  • No reverse proxy in front of a public Node server, with defaults everywhere.
  • Missing error handlers on server/socket objects.
  • Extremely permissive timeouts and unlimited body sizes.

Detection hints:

  • Inspect server creation (http.createServer, https.createServer) and whether timeouts are set.
  • Check proxy/gateway config for timeouts and max body size.

Fix:

  • Explain how to configure reverse proxy and timeouts, set request size limits
  • add robust error handling middleware

Notes:

  • Node’s security guidance for HTTP DoS discusses using reverse proxies and correctly configuring server timeouts. (Node.js)

EXPRESS-NODE-INSPECT-001: Do not expose the Node inspector in production

Severity: Critical

NOTE: Ensure that this detection is actually in the production path, and not just being used for local debugging.

Required:

  • MUST NOT run Node with --inspect (especially bound to non-loopback) in production.
  • MUST ensure NODE_OPTIONS or startup scripts do not enable inspector in prod.
  • SHOULD firewall/debug locally only.

Insecure patterns:

  • node --inspect=0.0.0.0:9229 app.js in production.
  • Container/PM2/systemd configs enabling inspector.

Detection hints:

  • Search for --inspect in Dockerfiles, Procfiles, systemd units, PM2 configs, npm scripts.
  • Check NODE_OPTIONS.

Fix:

  • Remove inspector flags from production start commands; restrict to local dev.

Notes:

  • Node security guidance discusses inspector exposure risks (e.g., DNS rebinding) and recommends not running inspector in production. (Node.js)

EXPRESS-NODE-HTTP-001: Do not enable insecure HTTP parsing in production

Severity: High

NOTE: Ensure that this detection is actually in the production path, and not just being used for local dev.

Required:

  • MUST NOT use Node’s insecureHTTPParser in production.
  • MAY suggest configuring front-end proxies to normalize ambiguous requests to reduce request smuggling risk.

Insecure patterns:

  • Creating an HTTP server with { insecureHTTPParser: true }.

Detection hints:

  • Search for insecureHTTPParser in server creation code.

Fix:

  • Remove insecure parsing; rely on spec-compliant parsing and normalize at the edge.

Notes:

  • Node security guidance explicitly recommends not using insecureHTTPParser. (Node.js)

5) Practical scanning heuristics (how to “hunt”)

When actively scanning an Express repo, these patterns are high-signal:

  • TLS / transport:

    • app.listen(80 without reverse proxy mention; missing helmet; cookies missing secure (Express) (NOTE this only applies to web facing applications, internal apps likely won’t have TLS)
  • Proxy trust:

    • app.set('trust proxy', true); logic using req.ip/req.protocol/req.hostname (Express)
  • Security headers / fingerprinting:

    • missing helmet(; missing app.disable('x-powered-by') (Express)
  • Cookies / sessions:

    • express-session with missing store (MemoryStore risk), hard-coded secret:, missing cookie: { secure/httpOnly/sameSite } (Express)
    • cookie-session storing large objects or secrets (Express)
  • Body parsing limits:

    • express.json() or express.urlencoded() without limit/parameterLimit/depth (Express)
  • CSRF:

  • Open redirects:

    • res.redirect(req.query.next) or similar (Express)
  • XSS / HTML output:

    • res.send( building HTML with user input; template “safe” flags; untrusted values in res.locals (Express)
  • File handling:

    • res.sendFile( / res.download( where path originates from request; express.static('uploads') (Express)
  • Injection:

  • SSRF:

  • Brute force / abuse:

    • auth endpoints lacking throttling; no rate limiting middleware (Express)
  • Supply chain:

    • outdated Express versions; no lockfiles; no npm audit workflow (Express)
  • Node runtime hazards:

    • --inspect in production scripts; insecureHTTPParser usage (Node.js)

Always try to confirm:

  • data origin (untrusted vs trusted)
  • sink type (HTML/template, SQL/NoSQL, subprocess, filesystem, redirect, outbound HTTP)
  • protective controls present (validation, allowlists, middleware, proxy config, header policies)
  • whether protections are at the edge vs in app code

6) Sources (accessed 2026-01-27)

Primary Express documentation:

  • Express: Production Best Practices — Security: https://expressjs.com/en/advanced/best-practice-security.html (Express)
  • Express: Behind Proxies (trust proxy): https://expressjs.com/en/guide/behind-proxies.html (Express)
  • Express 5.x API Reference (parsers, static, sendFile, redirect, cookies): https://expressjs.com/en/5x/api.html (Express)
  • Express: Error Handling: https://expressjs.com/en/guide/error-handling.html (Express)

Session middleware documentation:

  • express-session docs (cookie flags, secret rotation, fixation mitigation, MemoryStore warning): https://expressjs.com/en/resources/middleware/session.html (Express)

Node.js and npm official references:

  • Node.js — Security Best Practices (DoS, proxy guidance, inspector risks, request smuggling notes): https://nodejs.org/en/learn/getting-started/security-best-practices (Node.js)
  • npm Docs — npm audit: https://docs.npmjs.com/cli/v9/commands/npm-audit/ (npm Docs)

OWASP Cheat Sheet Series:

  • Session Management: https://cheatsheetseries.owasp.org/cheatsheets/Session_Management_Cheat_Sheet.html (OWASP Cheat Sheet Series)
  • CSRF Prevention: https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.html (OWASP Cheat Sheet Series)
  • XSS Prevention: https://cheatsheetseries.owasp.org/cheatsheets/Cross_Site_Scripting_Prevention_Cheat_Sheet.html (OWASP Cheat Sheet Series)
  • Input Validation: https://cheatsheetseries.owasp.org/cheatsheets/Input_Validation_Cheat_Sheet.html (OWASP Cheat Sheet Series)
  • SQL Injection Prevention: https://cheatsheetseries.owasp.org/cheatsheets/SQL_Injection_Prevention_Cheat_Sheet.html (OWASP Cheat Sheet Series)
  • OS Command Injection Defense: https://cheatsheetseries.owasp.org/cheatsheets/OS_Command_Injection_Defense_Cheat_Sheet.html (OWASP Cheat Sheet Series)
  • SSRF Prevention: https://cheatsheetseries.owasp.org/cheatsheets/Server_Side_Request_Forgery_Prevention_Cheat_Sheet.html (OWASP Cheat Sheet Series)
  • File Upload: https://cheatsheetseries.owasp.org/cheatsheets/File_Upload_Cheat_Sheet.html (OWASP Cheat Sheet Series)
  • Unvalidated Redirects: https://cheatsheetseries.owasp.org/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.html (OWASP Cheat Sheet Series)
  • HTTP Headers: https://cheatsheetseries.owasp.org/cheatsheets/HTTP_Headers_Cheat_Sheet.html (OWASP Cheat Sheet Series)

Versioning / advisories:

  • Express package version (npm): https://www.npmjs.com/package/express
  • Express open redirect advisory (CVE): https://nvd.nist.gov/vuln/detail/CVE-2024-29041 (NVD)

Reference: Javascript General Web Frontend Security

Frontend JavaScript/TypeScript Web Security Spec (Vanilla Browser JS/TS, Modern Browsers)

This document is designed as a security spec that supports:

  1. Secure-by-default code generation for new frontend JavaScript/TypeScript (no specific framework assumed).
  2. Security review / vulnerability hunting in existing frontend code (passive “notice issues while working” and active “scan the repo and report findings”).

It is intentionally written as a set of normative requirements (“MUST/SHOULD/MAY”) plus audit rules (what bad patterns look like, how to detect them, and how to fix/mitigate them).


0) Safety, boundaries, and anti-abuse constraints (MUST FOLLOW)

  • MUST NOT request, output, log, hard-code, or commit secrets (API keys intended to be secret, private keys, passwords, OAuth refresh tokens, session tokens, cookies). Notes:

    • Frontend code is inherently observable by end users. If a value must remain secret, it must not be in browser-delivered code.
    • If the project uses “public” keys (e.g., publishable analytics keys), they MUST be treated as non-secret and scoped accordingly.
  • MUST NOT “fix” security by disabling protections (e.g., weakening CSP with unsafe-inline/unsafe-eval without justification, removing origin checks for postMessage, switching to innerHTML for convenience, accepting arbitrary redirects/URLs, or turning off sanitization).

  • MUST provide evidence-based findings during audits: cite file paths, code snippets, and relevant HTML/CSP/config values that justify the claim.

  • MUST treat uncertainty honestly:

    • Security headers (CSP, frame-ancestors, etc.) might be set by server/edge/CDN rather than in repo code. If not visible, report as “not visible here; verify at runtime/edge config.” (Also note that <meta http-equiv=...> only simulates a subset of headers; don’t assume other security headers exist just because a meta tag exists.) (MDN Web Docs)

1) Operating modes

1.1 Generation mode (default)

When asked to write new frontend JS/TS code or modify existing code:

  • MUST follow every MUST requirement in this spec.
  • SHOULD follow every SHOULD requirement unless the user explicitly says otherwise.
  • MUST prefer safe-by-default browser APIs and proven libraries over custom security code (especially for HTML sanitization).
  • MUST avoid introducing new risky sinks (DOM XSS injection sinks like innerHTML, navigation to javascript: URLs, dynamic code execution via eval/Function, unsafe postMessage, unsafe third-party script loading, etc.). (OWASP Cheat Sheet Series)

1.2 Passive review mode (always on while editing)

While working anywhere in a frontend repo (even if the user did not ask for a security scan):

  • MUST “notice” violations of this spec in touched/nearby code.
  • SHOULD mention issues as they come up, with a brief explanation + safe fix.

1.3 Active audit mode (explicit scan request)

When the user asks to “scan”, “audit”, or “hunt for vulns”:

  • MUST systematically search the codebase for violations of this spec.
  • MUST output findings in a structured format (see §2.3).

Recommended audit order:

  1. HTML entrypoints (index.html, server-rendered templates), script/style includes, and any CSP delivery (header vs meta). (W3C)
  2. DOM XSS sinks (innerHTML, document.write, insertAdjacentHTML, event-handler attributes) and their data sources (URL params/hash, storage, postMessage, API responses). (OWASP Cheat Sheet Series)
  3. Navigation/redirect handling (window.location*, link targets, URL allowlists) including javascript: URL hazards. (MDN Web Docs)
  4. Cross-origin communication (postMessage, iframe embed patterns, sandboxing). (MDN Web Docs)
  5. Storage of sensitive data (localStorage/sessionStorage) and assumptions about trust. (OWASP Cheat Sheet Series)
  6. Third-party scripts / tag managers / CDNs, and integrity controls (SRI) and policy controls (CSP). (OWASP Cheat Sheet Series)
  7. DOM clobbering gadgets and unsafe reliance on window/document named properties. (OWASP Cheat Sheet Series)

2) Definitions and review guidance

2.1 Untrusted input (treat as attacker-controlled unless proven otherwise)

Examples include:

  • URL-derived data: location.href, location.search, location.hash, document.baseURI, new URLSearchParams(location.search), routing fragments. (OWASP Cheat Sheet Series)
  • DOM content that may include user-controlled markup (comments, profiles, CMS content, markdown-to-HTML output, etc.), especially if inserted dynamically. (OWASP Cheat Sheet Series)
  • postMessage event data (event.data) and metadata (event.origin) from other windows/frames. (MDN Web Docs)
  • Browser storage: localStorage, sessionStorage, IndexedDB (contents can be attacker-influenced via XSS or local machine access; never treat as “trusted”). (OWASP Cheat Sheet Series)
  • Any data returned from network calls (even if from “your API”), because it may contain stored attacker content that becomes dangerous only when inserted into the DOM. (OWASP Cheat Sheet Series)

2.2 Dangerous sink (DOM XSS / code execution sink)

A sink is any API/operation that can execute script or interpret attacker-controlled strings as HTML/JS/URL in a security-sensitive way. High-signal sinks include:

  • HTML parsing / insertion: innerHTML, outerHTML, insertAdjacentHTML, document.write, document.writeln. (OWASP Cheat Sheet Series)
  • Dynamic code execution: eval, new Function, setTimeout("..."), setInterval("..."). (MDN Web Docs)
  • Navigation to script-bearing URLs (e.g., javascript:) via setters like Location.href/window.location (and via link href if attacker-controlled). (MDN Web Docs)
  • Setting event handler attributes from strings, e.g. setAttribute("onclick", "..."). (OWASP Cheat Sheet Series)

2.3 Required audit finding format

For each issue found, output:

  • Rule ID:
  • Severity: Critical / High / Medium / Low
  • Location: file path + function/class/module + line(s)
  • Evidence: the exact code/config snippet
  • Impact: what could go wrong, who can exploit it
  • Fix: safe change (prefer minimal diff)
  • Mitigation: defense-in-depth if immediate fix is hard
  • False positive notes: what to verify if uncertain

3) Secure baseline: minimum production configuration (MUST in production)

This is the smallest baseline that prevents common frontend JS/TS security misconfigurations. Some items are “in repo” (HTML/JS) and some may live at the server/edge.

3.1 Content Security Policy (CSP) baseline (SHOULD; MUST for high-risk apps)

  • SHOULD deliver CSP via HTTP response headers when possible.

  • MAY deliver CSP via an HTML <meta http-equiv="Content-Security-Policy" ...> tag when you cannot set headers (e.g., purely static hosting constraints). (MDN Web Docs)

  • If using CSP via <meta http-equiv>, MUST understand the limitations:

    • The policy only applies to content that follows the meta element (so it must appear very early, before any scripts/resources you want governed). (W3C)
    • The following directives are not supported in a meta-delivered policy and will be ignored: report-uri, frame-ancestors, and sandbox. (W3C)
    • “Report-only” CSP cannot be set via a meta element. (W3C)

Practical baseline goals:

  • Avoid script sources unsafe-inline and unsafe-eval (they significantly weaken CSP’s value against XSS). (MDN Web Docs)
  • Prefer nonce- or hash-based script policies if you need inline scripts. (MDN Web Docs)
  • Consider enabling Trusted Types enforcement where feasible. (MDN Web Docs)

3.2 Third-party scripts baseline (SHOULD)

  • SHOULD minimize third-party script execution and treat it as equivalent privilege to first-party JS (it runs with your origin’s privileges). (OWASP Cheat Sheet Series)
  • SHOULD use Subresource Integrity (SRI) for third-party scripts/styles loaded from CDNs. (MDN Web Docs)

3.3 Cross-window communication baseline (SHOULD)

  • SHOULD restrict postMessage communications to explicit origins, and validate both origin and message shape. (MDN Web Docs)

4) Rules (generation + audit)

Each rule contains: required practice, insecure patterns, detection hints, and remediation.

JS-XSS-001: Do not inject untrusted HTML into the DOM (avoid innerHTML and friends)

Severity: Critical if you can prove attacker-controlled input can reach these APIs; otherwise Medium

Required:

  • MUST treat innerHTML, outerHTML, and insertAdjacentHTML as dangerous sinks when their input can contain untrusted data. (OWASP Cheat Sheet Series)

  • MUST prefer safe DOM APIs that do not parse HTML:

  • If HTML insertion is truly required, SHOULD sanitize with a well-reviewed HTML sanitizer and strongly consider enforcing Trusted Types to confine usage to audited code paths. (MDN Web Docs)

Insecure patterns:

  • el.innerHTML = userInput
  • el.insertAdjacentHTML('beforeend', userInput)
  • el.outerHTML = userInput

Detection hints:

  • Search for: .innerHTML, .outerHTML, insertAdjacentHTML(.
  • Trace the origin of inserted string: URL params/hash, postMessage, storage, API responses, DOM attributes. (OWASP Cheat Sheet Series)

Fix:

  • Replace with textContent for plain text. (OWASP Cheat Sheet Series)

  • For structured UI, build DOM nodes explicitly.

  • For “rich text” requirements:

    • Sanitize using an allowlist-based sanitizer.
    • Prefer returning safe “components” instead of arbitrary HTML strings.
    • Use Trusted Types enforcement to ensure only TrustedHTML reaches sinks where supported. (MDN Web Docs)

Mitigation:

  • Deploy a strict CSP and consider Trusted Types enforcement (require-trusted-types-for 'script'). (MDN Web Docs)

False positive notes:

  • If the string is provably constant or fully generated from trusted constants, it may be safe. Still prefer safer APIs.

JS-XSS-002: Avoid document.write / document.writeln (XSS + document clobbering hazards)

Severity: Critical if you can prove attacker-controlled input can reach these APIs; otherwise Medium

Required:

  • MUST avoid document.write() and document.writeln() in production code (they are XSS vectors and can be abused with crafted HTML even if some browsers block injected <script> in certain situations). (MDN Web Docs)
  • If legacy use is unavoidable, MUST ensure no untrusted input reaches these APIs and SHOULD enforce Trusted Types (TrustedHTML) where supported. (MDN Web Docs)

Insecure patterns:

  • document.write(userInput)
  • document.writeln(getParam('q'))

Detection hints:

Fix:

  • Replace with DOM manipulation (createElement, appendChild) or safe text insertion (textContent). (OWASP Cheat Sheet Series)

Mitigation:

  • Strict CSP + Trusted Types enforcement reduces blast radius if a sink remains. (MDN Web Docs)

JS-XSS-003: Do not use string-to-code execution (eval, new Function, string timeouts)

Severity: Critical if you can prove attacker-controlled input can reach these APIs; otherwise Medium

Required:

  • MUST NOT pass untrusted data to:

    • eval()
    • new Function(...)
    • setTimeout("...") / setInterval("...") with string arguments (MDN Web Docs)
  • SHOULD avoid these APIs entirely in modern frontend code; refactor to non-eval logic. (MDN Web Docs)

  • MUST NOT “fix CSP breakage” by adding unsafe-eval unless there is a documented, reviewed justification and compensating controls. (MDN Web Docs)

Insecure patterns:

  • eval(userInput)
  • new Function("return " + userInput)()
  • setTimeout(userInput, 0) where userInput is a string

Detection hints:

  • Search for eval(, new Function, setTimeout(", setInterval(".
  • Also search for construction of code strings used later.

Fix:

  • Replace dynamic code with:

    • structured data + explicit branching/handlers,
    • JSON parsing (JSON.parse) instead of eval for JSON. (OWASP Cheat Sheet Series)

Mitigation:

  • CSP that blocks eval()-like APIs by default, and avoid unsafe-eval. (MDN Web Docs)
  • Consider Trusted Types for controlled cases, but treat it as a hardening layer, not a license to keep eval patterns. (MDN Web Docs)

JS-XSS-004: Do not set event handler attributes from strings (e.g., setAttribute("onclick", "..."))

Severity: High

Required:

  • MUST NOT use setAttribute("on…", string) or similar patterns with untrusted data; this coerces strings into executable code in the event-handler context. (OWASP Cheat Sheet Series)
  • SHOULD prefer addEventListener with function references.

Insecure patterns:

  • el.setAttribute("onclick", userInput)
  • el.onclick = userControlledString (string assignment)

Detection hints:

  • Search for .setAttribute("on, .onclick =, .onmouseover =, etc.
  • Trace whether RHS can be influenced by URL/hash/storage/postMessage. (OWASP Cheat Sheet Series)

Fix:

  • Replace with addEventListener("click", () => { ... }).
  • If dynamic dispatch is needed, use an allowlisted mapping from identifiers to functions (no string eval). (OWASP Cheat Sheet Series)

JS-URL-001: Sanitize and allowlist URLs before navigation (especially window.location / location.replace)

Severity: Low (High if you can prove an attacker can fully control the URL)

IMPORTANT: This can cause a lot of false positives. Please perform extra analysis to determine if the url is fully attacker controlled. If not fully attacker controlled, then this is informational at best.

NOTE: It may be important functionality to be able to redirect to any given url. If that is the goal of the feature, then at a minimum, ensure it checks the schema even if the origin is allowed to be anything.

Required:

  • MUST treat any assignment to navigation targets as security-sensitive:

    • window.location = ...
    • location.href = ...
    • location.assign(...)
    • location.replace(...) (MDN Web Docs)
  • MUST prevent navigation to javascript: URLs (and generally other script-bearing/active schemes), especially when input is derived from URL params, storage, or messages. (MDN Web Docs). Only allow http: and https:.

  • SHOULD validate/allowlist the destination. A safe baseline is:

    • Allow only same-origin relative paths, OR
    • Allow only a strict allowlist of origins and protocols (typically https: and optionally http: for localhost dev). (OWASP Cheat Sheet Series)

Insecure patterns:

  • location.replace(getParam("next"))
  • window.location = userSuppliedUrl
  • location.assign(window.redirectTo || "/") where redirectTo can be clobbered or attacker-set (OWASP Cheat Sheet Series)

Detection hints:

  • Search for window.location, location.href, location.assign, location.replace.
  • Search for common redirect parameters: next, returnTo, redirect, url, continue.
  • Search for javascript: literal usage. (MDN Web Docs)

Fix:

  • Parse and validate with new URL(value, location.origin) and then enforce:

    • url.protocol in { "https:" } (and only include http: in explicit dev-only code paths),
    • url.origin equals location.origin for internal redirects, or in a strict allowlist for external redirects,
    • optionally allow only specific path prefixes. (MDN Web Docs)
  • If validation fails, navigate to a safe default (home/dashboard).

Mitigation:

  • Deploy strict CSP and Trusted Types enforcement to reduce the impact of DOM XSS sinks, but note that Trusted Types do not prevent every possible unsafe navigation scenario on their own. (W3C)

False positive notes:

IMPORTANT: This can cause a lot of false positives. Please perform extra analysis to determine if the url is fully attacker controlled. If not fully attacker controlled, then this is informational at best.

  • Some apps intentionally support external redirects (SSO, payment flows). Those MUST be allowlisted and documented.

JS-URL-002: Sanitize URLs before inserting into DOM URL contexts (href, src, etc.)

Severity: Low (High if you can prove an attacker can fully control the URL)

IMPORTANT: This can cause a lot of false positives. Please perform extra analysis to determine if the url is fully attacker controlled. If not fully attacker controlled, then this is informational at best.

Required:

  • MUST treat setting URL-bearing DOM attributes/properties as security-sensitive, especially:

    • a.href, img.src, script.src, iframe.src, form.action, link.href.
  • MUST prevent script-bearing schemes (javascript: and other active schemes) when values can be attacker-influenced. (MDN Web Docs)

  • SHOULD prefer setting properties (e.g., a.href = url.toString()) after parsing and validation, rather than string concatenation.

Insecure patterns:

  • link.href = getParam("u")
  • el.setAttribute("href", userInput) without validation
  • constructing URLs via concatenation with untrusted pieces

Detection hints:

  • Search for .href =, .src =, .action =, setAttribute("href", setAttribute("src".
  • Search for javascript: / data: usage in URLs. (MDN Web Docs)

IMPORTANT: This can cause a lot of false positives. Please perform extra analysis to determine if the url is fully attacker controlled. If not fully attacker controlled, then this is informational at best.

Fix:

  • Use new URL(...) and validate:

    • protocol allowlist
    • avoid passing user-provided values into <script src> at all (treat as code execution). (OWASP Cheat Sheet Series)

JS-CSP-001: Use CSP; meta delivery is allowed

Severity: Medium to High (depends on threat model; High when handling untrusted content)

NOTE: It is most important to set the CSP’s script-src. All other directives are not as important and can generally be excluded for the ease of development.

Required:

  • SHOULD deploy a CSP as a major defense-in-depth against XSS. (MDN Web Docs)

  • MAY provide CSP via <meta http-equiv="Content-Security-Policy" ...> when headers are not available. (MDN Web Docs)

  • If CSP is delivered via meta, MUST:

    • place it early (before scripts/resources you want governed), and
    • not rely on unsupported directives in meta policies (report-uri, frame-ancestors, sandbox). (W3C)
  • MUST avoid adding unsafe-inline as a “quick fix” for CSP issues unless explicitly required and reviewed (it defeats much of CSP’s purpose). (MDN Web Docs)

  • MUST avoid adding unsafe-eval unless explicitly required and reviewed (it allows eval-like APIs that are commonly abused). (MDN Web Docs)

Insecure patterns:

  • No CSP present anywhere (repo HTML or server/edge) for an app that renders untrusted content.
  • CSP includes script-src 'unsafe-inline' and/or script-src 'unsafe-eval' without strong justification. (MDN Web Docs)
  • CSP delivered via meta but includes frame-ancestors (it will be ignored in meta). (W3C)

Detection hints:

  • Search HTML for <meta http-equiv="Content-Security-Policy".
  • Search server/edge configs for Content-Security-Policy header.
  • If CSP is only in meta, check it appears before any <script> tags you want governed. (W3C)

Fix:

  • Prefer header-delivered CSP at the server/edge.
  • If constrained to meta, keep a strong allowlist CSP and document the limitations; implement clickjacking protections (e.g., frame-ancestors) at the server/edge, not in meta. (W3C)

JS-CSP-002: Prefer strict CSP (nonces/hashes); avoid inline/eval patterns in code

Severity: Medium

NOTE: It is most important to set the CSP’s script-src. All other directives are not as important and can generally be excluded for the ease of development.

Required:

  • SHOULD design frontend code to work under a strict CSP:

    • avoid inline scripts and inline event handlers,
    • avoid eval-like APIs (see JS-XSS-003),
    • allow scripts via nonce or hash when needed. (MDN Web Docs)

Insecure patterns:

  • Large amounts of inline script blocks and inline onclick="..." handlers.
  • Libraries that require unsafe-eval.

Detection hints:

  • Search for <script> blocks with inline code, onclick=", onload=", etc.
  • Search for CSP directives containing unsafe-inline or unsafe-eval. (MDN Web Docs)

Fix:

  • Move inline scripts into external JS files (same-origin).
  • Use nonces/hashes for any unavoidable inline blocks. (MDN Web Docs)

JS-TT-001: Use Trusted Types to reduce DOM XSS attack surface (where supported)

Severity: Low

Required:

  • SHOULD consider enabling Trusted Types enforcement with CSP require-trusted-types-for 'script' to make many DOM XSS sinks reject raw strings. (MDN Web Docs)
  • If using Trusted Types, SHOULD also use the CSP trusted-types directive to restrict which policies can be created (reduces policy sprawl and improves auditability). (MDN Web Docs)
  • MUST keep Trusted Types policy code small, heavily reviewed, and used as the only path to produce trusted values for sinks. (W3C)

Insecure patterns:

  • “Trusted Types enabled” but policy simply returns input unchanged (no sanitization/validation).
  • Many ad-hoc policies created across the codebase without restriction.
  • Belief that Trusted Types alone prevents all unsafe navigations or all XSS classes. (It targets DOM injection sinks; it is not a universal sandbox.) (W3C)

Detection hints:

  • Search for CSP directives: require-trusted-types-for and trusted-types.
  • Search code for trustedTypes.createPolicy( and inspect policy implementations. (MDN Web Docs)

Fix:

  • Add a small set of well-reviewed policies (e.g., createHTML that sanitizes).
  • Restrict allowed policies via trusted-types <policyName...>.
  • Migrate sinks to require TrustedHTML / TrustedScriptURL as appropriate. (MDN Web Docs)

JS-MSG-001: postMessage must use strict origin validation and explicit targetOrigin

Severity: Medium (High if dangerous behavior can be triggered via postMessage)

Required:

  • When sending messages, MUST set an explicit targetOrigin (not *) to avoid sending data to an unexpected origin after redirects or window origin changes. (MDN Web Docs)

  • When receiving messages, MUST:

    • Validate event.origin exactly against an allowlist of expected origins (no substring matching). (OWASP Cheat Sheet Series)
    • Consider validating event.source (expected window reference) when applicable. (MDN Web Docs)
    • Validate event.data structure (schema/shape) and treat it purely as data (never evaluate it as code and never insert into DOM with innerHTML). (OWASP Cheat Sheet Series)

Insecure patterns:

  • otherWindow.postMessage(payload, "*")
  • window.addEventListener("message", (e) => { doSomething(e.data) }) with no origin check
  • if (e.origin.includes("trusted.com")) (substring checks)
  • el.innerHTML = e.data (OWASP Cheat Sheet Series)

Detection hints:

  • Search for postMessage(, addEventListener("message", onmessage =.
  • Audit all handlers for explicit allowlist checks on event.origin. (OWASP Cheat Sheet Series)

Fix:

  • Define an allowlist:

    • const ALLOWED = new Set(["https://app.example.com", "https://accounts.example.com"]); NOTE: For ease of development, you can use the current page’s origin window.location.origin as a safe default origin.
  • On receive:

    • if (!ALLOWED.has(event.origin)) return;
    • Validate event.data with a strict schema and reject unknown/extra fields.
  • On send:

Mitigation:

  • Combine with a strict CSP and avoid DOM sinks in message paths. (MDN Web Docs)

JS-STORAGE-001: Web Storage is not a safe place for secrets (and is attacker-influencable)

Severity: Low

Required:

  • MUST NOT store sensitive secrets or session identifiers in localStorage (or sessionStorage) if compromise would matter; a single XSS can exfiltrate everything in storage. (OWASP Cheat Sheet Series)
  • MUST treat values read from storage as untrusted input (attackers can load malicious values into storage via XSS). (OWASP Cheat Sheet Series)
  • SHOULD prefer server-set cookies with HttpOnly for session identifiers (JS cannot set HttpOnly, so avoid storing session IDs in JS-accessible storage). (OWASP Cheat Sheet Series)
  • SHOULD avoid hosting multiple unrelated apps on the same origin if they rely on storage separation (storage is origin-wide). (OWASP Cheat Sheet Series)

Insecure patterns:

  • localStorage.setItem("access_token", token)
  • localStorage.setItem("session", sessionId)
  • Assuming localStorage is “trusted because same-origin.”

Detection hints:

  • Search for localStorage.getItem, localStorage.setItem, sessionStorage.*.
  • Flag storage keys named token, jwt, session, auth, refresh. (OWASP Cheat Sheet Series)

Fix:

  • Use server-managed sessions or short-lived tokens delivered and rotated securely, with careful XSS defenses (CSP/Trusted Types) and minimal JS exposure.
  • If storage must be used for non-sensitive state, keep it non-auth and validate/escape before use.

JS-SUPPLY-001: Third-party JavaScript is a major supply-chain risk; minimize and control it

Severity: Low

Required:

  • MUST treat third-party JS as equivalent to first-party JS in privilege (it can execute arbitrary code in your origin and access DOM data). (OWASP Cheat Sheet Series)

  • SHOULD minimize third-party scripts and prefer:

    • self-hosting / script mirroring,
    • strict CSP allowlists,
    • SRI for any CDN-hosted scripts,
    • ongoing monitoring for unexpected changes. (OWASP Cheat Sheet Series)

Insecure patterns:

  • Loading arbitrary remote scripts from many vendors without review.
  • Using tag managers that can dynamically inject scripts with no integrity controls.
  • Allowing scripts from broad wildcards in CSP (e.g., script-src *). (MDN Web Docs)

Detection hints:

  • Search HTML for <script src="https://..."> and tag manager snippets.
  • Search CSP script-src sources for wildcards or overly broad domains.
  • Search for dynamic script injection: document.createElement("script"), script.src = ..., appendChild(script). (OWASP Cheat Sheet Series)

Fix:

  • Remove unnecessary third-party tags.
  • Self-host or mirror scripts where possible.
  • Lock down CSP script-src to the smallest set of trusted sources.
  • Add SRI for CDN scripts/styles. (OWASP Cheat Sheet Series)

JS-SRI-001: Use Subresource Integrity (SRI) for third-party scripts/styles

Severity: Low

Required:

  • SHOULD use SRI to ensure browsers only load third-party resources if they match an expected cryptographic hash. (MDN Web Docs)
  • MUST update SRI hashes whenever the underlying resource changes (pin versions; avoid “latest” URLs).

Insecure patterns:

  • <script src="https://cdn.example.com/lib.js"></script> with no integrity.
  • Loading latest or unpinned third-party resources.

Detection hints:

  • Search for <script src="https:// and <link rel="stylesheet" href="https:// without integrity=.
  • Check whether integrity is present and uses strong hashes (sha256/384/512 are typical). (MDN Web Docs)

Fix:

  • Add integrity="sha384-..." (or appropriate) and ensure proper CORS mode where needed.
  • Prefer self-hosting critical libraries.

FS-DOMC-001: Prevent DOM clobbering (avoid relying on window/document named properties)

Severity: Medium to High (can become Critical if it enables script loading or javascript: navigation)

Required:

  • MUST NOT rely on implicit global variables or window.someName / document.someName lookups that can be clobbered by injected HTML elements with matching id/name. (OWASP Cheat Sheet Series)
  • MUST avoid patterns like let x = window.redirectTo || "/safe"; location.assign(x); where redirectTo could be clobbered to an <a> element whose href is attacker-controlled (including javascript:). (OWASP Cheat Sheet Series)
  • SHOULD use explicit variable declarations, local scope, and explicit DOM queries (getElementById) rather than named property access. (OWASP Cheat Sheet Series)
  • If the app inserts user-controlled markup (even sanitized), SHOULD ensure sanitization strategies consider id/name collisions. (OWASP Cheat Sheet Series)

Insecure patterns:

  • const cfg = window.config || {}; used for security-sensitive URLs.
  • const redirect = window.redirectTo || "/"; location.assign(redirect); (OWASP Cheat Sheet Series)
  • Loading scripts from window.* config values without strict validation.

Detection hints:

  • Search for window. and document. used as config stores (especially || fallback patterns).
  • Search for usage of location.assign/replace with variables that come from window/document properties.
  • Search for dynamic script creation (createElement('script')) where .src comes from a non-local variable. (OWASP Cheat Sheet Series)

Fix:

  • Store config in module-scoped constants (not on window/document) and pass it explicitly.
  • Validate any URL-like config with protocol/origin allowlists (see FEJS-URL-001). (OWASP Cheat Sheet Series)
  • Consider hardening: sanitization, CSP, and (in limited cases) freezing sensitive objects, but treat these as defense-in-depth, not a substitute for safe coding patterns. (OWASP Cheat Sheet Series)

5) Practical scanning heuristics (how to “hunt”)

When actively scanning, use these high-signal patterns:

  • DOM XSS sinks:

  • Dangerous navigation / URL sinks:

    • window.location, location.href, location.assign, location.replace
    • javascript: literals (and other suspicious schemes like data:text/html) (MDN Web Docs)
  • String-to-code execution:

    • eval(, new Function, setTimeout(", setInterval(" (MDN Web Docs)
  • Event-handler string injection:

  • postMessage:

    • postMessage( with "*" as targetOrigin
    • addEventListener("message" without strict event.origin allowlist checks (MDN Web Docs)
  • Storage:

    • localStorage.setItem( / getItem(, sessionStorage.*
    • keys containing token, jwt, session, auth, refresh (OWASP Cheat Sheet Series)
  • CSP and related:

    • Content-Security-Policy header config (server/edge)
    • <meta http-equiv="Content-Security-Policy" ...>
    • CSP containing unsafe-inline or unsafe-eval
    • require-trusted-types-for / trusted-types directives (MDN Web Docs)
  • Third-party scripts:

    • <script src="https://..."> without integrity=
    • Tag manager snippets and dynamic script injection code paths (MDN Web Docs)
  • DOM clobbering gadgets:

    • window.<name> || ... and document.<name> || ... patterns
    • security-sensitive usage of window/document properties as config sources (OWASP Cheat Sheet Series)

Always try to confirm:

  • data origin (untrusted vs trusted),
  • sink type (HTML parse, navigation, code execution, message handling, storage),
  • protective controls present (CSP, Trusted Types, sanitizers, strict allowlists, schema validation).

6) Sources (accessed 2026-01-27)

Primary standards / platform docs:

  • W3C Content Security Policy Level 2 (HTML <meta> delivery restrictions; unsupported directives in meta CSP): https://www.w3.org/TR/CSP2/ (W3C)
  • MDN: CSP Guide (strict CSP, nonces/hashes, unsafe-inline/unsafe-eval, eval blocking): https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/CSP (MDN Web Docs)
  • MDN: <meta http-equiv> (CSP via meta and warning about meta-based security headers): https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/meta/http-equiv (MDN Web Docs)
  • MDN: frame-ancestors (and note it’s not supported in <meta>): https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Content-Security-Policy/frame-ancestors (MDN Web Docs)

DOM XSS and dangerous sinks:

  • OWASP: DOM Based XSS Prevention Cheat Sheet (dangerous sinks + safe patterns like textContent): https://cheatsheetseries.owasp.org/cheatsheets/DOM_based_XSS_Prevention_Cheat_Sheet.html (OWASP Cheat Sheet Series)
  • MDN: innerHTML (security considerations): https://developer.mozilla.org/en-US/docs/Web/API/Element/innerHTML (MDN Web Docs)
  • MDN: insertAdjacentHTML (security considerations): https://developer.mozilla.org/en-US/docs/Web/API/Element/insertAdjacentHTML (MDN Web Docs)
  • MDN: document.write() / document.writeln() (security considerations): https://developer.mozilla.org/en-US/docs/Web/API/Document/write and https://developer.mozilla.org/en-US/docs/Web/API/Document/writeln (MDN Web Docs)

URL scheme hazards:

  • MDN: javascript: URLs (execution on navigation; discouraged; references window.location): https://developer.mozilla.org/en-US/docs/Web/URI/Reference/Schemes/javascript (MDN Web Docs)

Trusted Types:

  • W3C: Trusted Types spec (DOM XSS sinks include Element.innerHTML and Location.href setters; goals and limitations): https://www.w3.org/TR/trusted-types/ (W3C)
  • MDN: require-trusted-types-for directive: https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Content-Security-Policy/require-trusted-types-for (MDN Web Docs)
  • MDN: trusted-types directive: https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Content-Security-Policy/trusted-types (MDN Web Docs)

Cross-window messaging:

  • MDN: window.postMessage (security guidance: specify targetOrigin; validate origin): https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage (MDN Web Docs)
  • OWASP: HTML5 Security Cheat Sheet (Web Messaging guidance: explicit origin, strict checks, no innerHTML): https://cheatsheetseries.owasp.org/cheatsheets/HTML5_Security_Cheat_Sheet.html (OWASP Cheat Sheet Series)

Third-party scripts and integrity:

  • OWASP: Third Party JavaScript Management Cheat Sheet (risks and mitigations including SRI/mirroring): https://cheatsheetseries.owasp.org/cheatsheets/Third_Party_Javascript_Management_Cheat_Sheet.html (OWASP Cheat Sheet Series)
  • MDN: Subresource Integrity overview: https://developer.mozilla.org/en-US/docs/Web/Security/Defenses/Subresource_Integrity (MDN Web Docs)
  • W3C: Subresource Integrity spec: https://www.w3.org/TR/sri-2/ (W3C)

DOM clobbering:

  • OWASP: DOM Clobbering Prevention Cheat Sheet (named property access risk; example attacks involving location.assign and javascript:): https://cheatsheetseries.owasp.org/cheatsheets/DOM_Clobbering_Prevention_Cheat_Sheet.html (OWASP Cheat Sheet Series)

Reference: Javascript Jquery Web Frontend Security

jQuery Frontend Security Spec (jQuery 4.0.x, modern browsers)

This document is designed as a security spec that supports:

  1. Secure-by-default code generation for new jQuery-based frontend code.
  2. Security review / vulnerability hunting in existing jQuery-based code (passive “notice issues while working” and active “scan the repo and report findings”).

It is intentionally written as a set of normative requirements (“MUST/SHOULD/MAY”) plus audit rules (what bad patterns look like, how to detect them, and how to fix/mitigate them).


0) Safety, boundaries, and anti-abuse constraints (MUST FOLLOW)

  • MUST NOT request, output, log, or commit secrets (API keys, passwords, private keys, session tokens, refresh tokens, CSRF tokens, session cookies).

  • MUST treat the browser as an attacker-controlled environment:

    • Frontend checks (UI gating, “disable button”, hidden fields, client-side validation) MUST NOT be treated as authorization or a security boundary.
    • Server-side authorization and validation MUST exist even if frontend is “correct”.
  • MUST NOT “fix” security by disabling protections (e.g., relaxing CSP to allow unsafe-inline, enabling JSONP “because it works”, adding broad CORS, disabling sanitization, suppressing security checks).

  • MUST provide evidence-based findings during audits: cite file paths, code snippets, and relevant configuration values.

  • MUST treat uncertainty honestly: if a protection might exist at the edge (CDN/WAF/reverse proxy headers like CSP), report it as “not visible in repo; verify at runtime/config”.


1) Operating modes

1.1 Generation mode (default)

When asked to write new jQuery code or modify existing jQuery code:

  • MUST follow every MUST requirement in this spec.
  • SHOULD follow every SHOULD requirement unless the user explicitly says otherwise.
  • MUST prefer safe-by-default patterns: text insertion, DOM node construction, allowlists, and proven sanitization libraries over custom escaping.
  • MUST avoid introducing new risky sinks (HTML string building, dynamic script loading, JSONP, inline script/event-handler attributes, unsafe URL assignment, unsafe object merging).

1.2 Passive review mode (always on while editing)

While working anywhere in a repo that uses jQuery (even if the user did not ask for a security scan):

  • MUST “notice” violations of this spec in touched/nearby code.
  • SHOULD mention issues as they come up, with a brief explanation + safe fix.

1.3 Active audit mode (explicit scan request)

When the user asks to “scan”, “audit”, or “hunt for vulns”:

  • MUST systematically search the codebase for violations of this spec.
  • MUST output findings in the structured format (see §2.3).

Recommended audit order:

  1. jQuery sourcing, versions, and dependency hygiene (script tags, lockfiles, CDN usage, SRI).
  2. CSP / Trusted Types / security headers posture (in repo and at runtime if observable).
  3. DOM XSS: untrusted sources → jQuery sinks (.html, .append, $("<…>"), .load, etc.).
  4. Script execution sinks: JSONP, dataType:"script", $.getScript, dynamic <script> insertion.
  5. URL/attribute assignment (href, src, style, on* attributes).
  6. Prototype pollution / unsafe object merging ($.extend patterns).
  7. AJAX auth patterns + CSRF for cookie-based sessions.
  8. Third-party plugins and untrusted content rendering paths (comments, WYSIWYG, markdown-to-HTML).

2) Definitions and review guidance

2.1 Untrusted input (treat as attacker-controlled unless proven otherwise)

Examples include:

  • Any data from the server that originates from users (user profiles, comments, “display name”, rich text, filenames).

  • Data from third-party APIs or services.

  • Browser-controlled sources:

    • location.href, location.search, location.hash
    • document.URL, document.baseURI, document.referrer
    • window.name
    • localStorage / sessionStorage
    • postMessage event data (unless strict origin and schema validation exists)
    • Any DOM content that could have been injected previously (stored XSS)

2.2 High-risk “sinks” in jQuery contexts

A sink is a code path where untrusted input can become interpreted as executable code or HTML.

Key jQuery sink categories:

  • HTML insertion / parsing:

    • DOM manipulation methods that accept HTML strings such as .html(), .append(), and related methods (see CVE notes below). (NVD)
    • $(htmlString) (when the argument can be interpreted as HTML markup).
    • jQuery.parseHTML(html, …, keepScripts) especially with keepScripts=true. (jQuery API)
    • .load(url) (loads HTML into DOM; has special script execution behavior). (jQuery API)
  • Script execution / dynamic code loading:

    • $.getScript() / $.ajax({ dataType: "script" }) (executes fetched JavaScript). (jQuery API)
    • JSONP (dataType: "jsonp" or implicit JSONP behavior) (executes remote JavaScript as a response). (jQuery API)
    • eval, new Function, setTimeout("…"), setInterval("…"), $.globalEval (if present)
  • Dangerous attribute assignment:

    • Assigning untrusted strings to href, src, srcdoc, style, or event-handler attributes (onload, onclick, etc.)
    • javascript: URLs are particularly dangerous and discouraged. (MDN Web Docs)

2.3 Required audit finding format

For each issue found, output:

  • Rule ID:
  • Severity: Critical / High / Medium / Low
  • Location: file path + function/component + line(s)
  • Evidence: the exact code/config snippet
  • Impact: what could go wrong, who can exploit it
  • Fix: safe change (prefer minimal diff)
  • Mitigation: defense-in-depth if immediate fix is hard
  • False positive notes: what to verify if uncertain

3) Secure baseline: minimum production configuration (MUST in production)

This is the smallest “production baseline” that prevents common jQuery-related security failures.

3.1 Use a supported, patched jQuery version (MUST)

  • MUST use a supported jQuery major version and keep it updated.
  • As of 2026-01-27, the jQuery project ships jQuery 4.0.0 as the latest major release. (blog.jquery.com)
  • If you must support very old browsers (notably IE < 11), jQuery 4 does not support them and you may need to stay on jQuery 3.x; treat this as a higher risk posture and patch aggressively. (blog.jquery.com)

3.2 Load jQuery safely (MUST)

  • MUST load jQuery only from:

    • Your own build pipeline (bundled via npm/yarn + lockfile), or
    • The official jQuery CDN / a trusted CDN with Subresource Integrity (SRI) enabled.
  • If loading from a CDN, SHOULD use SRI (integrity) and correct crossorigin settings; the jQuery project explicitly supports and recommends SRI on its CDN. (Retrieved from jquery.com)

3.3 CSP + Trusted Types (SHOULD, and MUST where available/required by policy)

  • SHOULD deploy a Content Security Policy (CSP) that reduces XSS impact (especially script-src restrictions and avoiding unsafe-inline). If not done through HTTP server, this can be done through the <meta http-equiv="Content-Security-Policy" content="..."> tag. (OWASP Cheat Sheet Series) NOTE: It is most important to set the CSP’s script-src. All other directives are not as important and can generally be excluded for the ease of development.
  • SHOULD consider Trusted Types as a strong defense-in-depth against DOM XSS. (W3C)
  • If you deploy the CSP directive require-trusted-types-for, then code MUST route DOM-injection through Trusted Types policies. (MDN Web Docs)
  • Note: jQuery 4.0 explicitly added Trusted Types support so that TrustedHTML can be used with jQuery manipulation methods without violating require-trusted-types-for. (blog.jquery.com)

Even though these are typically set server-side, they materially reduce the blast radius of jQuery-related mistakes. However if the context is only the frontend web application, these cannot be acted on.

  • SHOULD set common security headers (CSP, X-Content-Type-Options: nosniff, clickjacking protection via frame-ancestors / X-Frame-Options, Referrer-Policy). (OWASP Cheat Sheet Series)
  • SHOULD avoid storing long-lived secrets/tokens in places accessible to JavaScript (like localStorage) unless the threat model explicitly accepts “XSS == account takeover”. This is not jQuery-specific, but jQuery-heavy DOM manipulation increases the chance of DOM XSS regressions; reduce the payoff.

4) Rules (generation + audit)

Each rule contains: required practice, insecure patterns, detection hints, and remediation.

JQ-SUPPLY-001: jQuery MUST be patched; do not run known vulnerable versions

Severity: Medium (High if internet-facing app AND version is known-vulnerable)

NOTE: Before performing an upgrade, get concent from the user and try to understand if they have reasons to keep it back. Upgrading can break applications in unexpected ways. Report and recommend upgrades rather than just performing them.

Required:

  • MUST NOT use jQuery versions with known high-impact vulnerabilities when a patched version exists.

  • MUST upgrade past:

    • CVE-2019-11358 (prototype pollution in jQuery before 3.4.0). (NVD)
    • CVE-2020-11022 / CVE-2020-11023 (XSS risks in DOM manipulation methods when handling untrusted HTML; patched in 3.5.0). (NVD)

Insecure patterns:

  • Script tags or package manifests referencing old jQuery (e.g., jquery-1.*, jquery-2.*, jquery-3.3.*, jquery-3.4.*, jquery-3.4.1, etc.).
  • Bundled vendor directories containing old minified jQuery without an upgrade path.

Detection hints:

  • Search HTML/templates for jquery- and parse version strings.
  • Check package.json, package-lock.json, yarn.lock, pnpm-lock.yaml.
  • Check vendor/, public/, static/, assets/, wwwroot/ for jquery*.js.

Fix:

  • Upgrade to current jQuery (prefer latest stable major; as of 2026-01-27, 4.0.0 is current). (blog.jquery.com)
  • If upgrade is constrained, at minimum upgrade beyond the CVE thresholds and add compensating controls (strong CSP, strict sanitization, remove risky APIs like JSONP, remove deep-extend of untrusted objects).

Notes:

  • If a product requirement forces old versions, report as “accepted risk requiring compensating controls”.

JQ-SUPPLY-002: Third-party script loading SHOULD use integrity and trusted origins

Severity: High

Required:

  • MUST load jQuery and plugins only from trusted origins.
  • If loaded from CDN, SHOULD use SRI (integrity) and correct crossorigin handling. (jquery.com)

Insecure patterns:

  • <script src="https://…/jquery.min.js"></script> with no integrity.
  • Loading jQuery from random third-party CDNs without an explicit trust decision.

Detection hints:

  • Scan HTML for <script src= and check for integrity= + crossorigin=.
  • Identify dynamic script insertion with untrusted URLs (see JQ-EXEC-001).

Fix:

  • Prefer bundling via npm + lockfile.
  • If using CDN, copy official script tag (jQuery CDN supports SRI). (jquery.com)

Note: If unable to get the correct SRI tag, skip this step but tell the user. If you end up using the wrong one the app will not function. In that case remove it and inform the user.


JQ-XSS-001: Untrusted data MUST NOT be inserted as HTML via jQuery DOM-manipulation methods

Severity: High (if attacker-controlled content reaches these sinks)

Required:

  • MUST treat any HTML string insertion as a code execution boundary.

  • MUST use safe alternatives for untrusted text:

    • .text(untrusted) (text, not HTML). (jQuery API)
    • .val(untrusted) for form fields. (jQuery API)
    • Create elements and set text/attributes safely instead of concatenating HTML strings.

Insecure patterns (examples):

  • $(selector).html(untrusted)
  • $(selector).append(untrusted)
  • $(selector).before(untrusted) / .after(untrusted) / .replaceWith(untrusted) / .wrap(untrusted) (and similar)
  • Building markup: "<div>" + untrusted + "</div>" then passing to jQuery

Detection hints:

  • Grep for: .html(, .append(, .prepend(, .before(, .after(, .replaceWith(, .wrap(, .wrapAll(, .wrapInner(
  • Trace dataflow into these calls from sources in §2.1.

Fix:

  • Replace with .text() / .val() or node construction:

    • const $el = $("<span>").text(untrusted); container.append($el);
  • If the output must contain limited markup, see JQ-XSS-002 (sanitization).

Notes:

  • Older jQuery versions had additional edge cases even when attempting sanitization; patched in 3.5.0+. Still: never rely on “string sanitization” alone—prefer structured creation or proven sanitizers. (GitHub)

JQ-XSS-002: If rendering user-controlled HTML is required, it MUST be sanitized with a proven HTML sanitizer

Severity: Medium (High if rich HTML is attacker-controlled and sanitizer is weak/misconfigured)

Required:

  • MUST NOT “roll your own” HTML sanitizer with regexes.

  • If user-controlled HTML must be displayed (e.g., rich text comments), MUST sanitize using a well-maintained HTML sanitizer and a restrictive allowlist.

    • DOMPurify is a common choice; use conservative configuration and keep it updated. (GitHub)
    • Where available, MAY consider the browser HTML Sanitizer API (note: limited browser availability). (MDN Web Docs)
  • SHOULD pair sanitization with CSP and, where feasible, Trusted Types for defense in depth. (OWASP Cheat Sheet Series)

Insecure patterns:

  • Regex-based “strip <script>” or “escape <” attempts followed by .html() insertion.
  • DOMPurify (or similar) configured to allow overly broad tags/attributes, or configuration that’s not reviewed.

Detection hints:

  • Search for “sanitize” helper functions, regex replacing </> patterns, or “allow all tags” configs.
  • Identify features that render user-generated “rich text” or “custom HTML”.
  • Check if sanitizer results are inserted with .html() or equivalent sinks.

Fix:

  • Introduce a sanitizer with strict allowlist.
  • Centralize the “sanitize then inject” pattern into a single reviewed module.
  • Add regression tests covering representative malicious inputs (don’t store payloads in logs or telemetry).

False positive notes:

  • If content is guaranteed trusted (e.g., compiled templates shipped by you), document the trust boundary and why it is not attacker-controlled.

JQ-XSS-003: $(untrustedString) and jQuery.parseHTML MUST NOT process attacker-controlled markup

Severity: High (if attacker-controlled)

Required:

  • MUST NOT pass attacker-controlled strings to $() when they might be interpreted as HTML.
  • MUST treat jQuery.parseHTML(html, …, keepScripts) as a high-risk primitive; keepScripts MUST be false for any untrusted input. (jQuery API)

Insecure patterns:

  • const $node = $(untrusted);
  • $.parseHTML(untrusted, /* context */, true) (scripts preserved)

Detection hints:

  • Search for $( calls where the argument is not a static selector or static markup.
  • Search for $.parseHTML( and inspect the keepScripts argument.

Fix:

  • Use DOM creation with constant tag names and .text() for untrusted values.
  • If parsing HTML is necessary, sanitize first (JQ-XSS-002) and keep scripts disabled.

JQ-XSS-004: .load() MUST be treated as an HTML+script injection surface

Severity: Medium (High if URL/content is attacker-controlled)

Required:

  • MUST NOT use .load() with attacker-controlled URLs or attacker-controlled HTML fragments.

  • MUST understand jQuery .load() script behavior:

    • Without a selector in the URL, content is passed to .html() before scripts are removed, which can execute scripts. (jQuery API)
  • SHOULD prefer fetch()/XHR to retrieve data, then render with safe DOM creation or sanitize explicitly.

Insecure patterns:

  • $("#target").load(untrustedUrl)
  • $("#target").load("/path?param=" + untrusted)

Detection hints:

  • Search for .load( across JS/TS files.
  • Identify whether a selector is appended to the URL (the behavior differs). (jQuery API)
  • Trace whether the URL can be influenced by user input.

Fix:

  • Replace .load() with:

    • fetch() to retrieve JSON, then render via .text() / node construction, or
    • fetch() to retrieve HTML, sanitize it, then inject.
  • If .load() must remain, ensure the URL is constant or strictly allowlisted and the returned content is trusted.


JQ-EXEC-001: Dynamic script execution and script fetching MUST NOT be reachable from untrusted input

Severity: High

Required:

  • MUST NOT fetch-and-execute scripts from untrusted or user-influenced URLs.

  • MUST treat these as code execution primitives:

    • $.getScript(url) executes the fetched script in the global context. (jQuery API)
    • $.ajax({ dataType: "script" }) and other script-typed requests that execute responses.
  • SHOULD remove these patterns unless there is a strong, reviewed justification.

Insecure patterns:

  • $.getScript(untrustedUrl)
  • $.ajax({ url: untrustedUrl, dataType: "script" })
  • Dynamic <script src=...> injection where src is derived from untrusted input.

Detection hints:

  • Search for getScript(, dataType: "script", globalEval, eval, new Function.
  • Look for “plugin loader” or “theme loader” features that accept URLs.

Fix:

  • Bundle scripts at build time.
  • If runtime-loading is required, restrict to allowlisted, versioned, integrity-checked assets (and ideally still avoid runtime code loading).

JQ-AJAX-001: JSONP MUST be disabled unless the endpoint is fully trusted (and even then, avoid)

Severity: Medium (High if attacker can influence URL/endpoint)

Required:

  • MUST NOT use JSONP for untrusted endpoints because it executes JavaScript responses.
  • When using $.ajax, MUST explicitly disable JSONP for non-fully-trusted targets; jQuery’s own docs recommend setting jsonp: false “for security reasons” if you don’t trust the target. (jQuery API)
  • SHOULD prefer CORS with JSON (dataType: "json") and explicit origin allowlists server-side.

Insecure patterns:

  • dataType: "jsonp"
  • URLs containing callback=? or patterns that trigger JSONP behavior. callback arguments are historically XSS vectors.
  • $.get(untrustedUrl) without pinning dataType and disabling JSONP (risk depends on options and jQuery behavior)

Detection hints:

  • Search for jsonp, dataType: "jsonp", callback=?.
  • Search for cross-domain AJAX where the URL is not hard-coded or allowlisted.

Fix:

  • Use JSON over HTTPS with CORS configured server-side.

  • Set:

    • dataType: "json"
    • jsonp: false (defense in depth when URL might be ambiguous) (jQuery API)

Severity: High

NOTE: This only matters when using cookie based auth. If the request use Authorization header, there is no CSRF potential.

Required:

  • If authentication uses cookies, MUST protect state-changing requests (POST/PUT/PATCH/DELETE) against CSRF.
  • SHOULD use server-verified CSRF tokens; for AJAX calls, tokens are commonly sent in a custom header. (OWASP Cheat Sheet Series)
  • MUST NOT treat “it’s an AJAX request” as CSRF protection by itself.

Insecure patterns:

  • $.post("/transfer", {...}) or $.ajax({ method: "POST", ... }) with cookie auth and no CSRF token/header.
  • “CSRF protection” that only checks for X-Requested-With (defense-in-depth only, not primary).

Detection hints:

  • Enumerate state-changing AJAX calls and locate whether they include CSRF tokens.
  • Identify how the server expects CSRF validation (meta tag, cookie-to-header double submit, synchronizer token, etc.).

Fix:

  • Add CSRF token inclusion in a centralized place, e.g., $.ajaxSetup({ headers: { "X-CSRF-Token": token } }), and ensure server verifies.
  • Follow OWASP CSRF guidance for token properties and validation. (OWASP Cheat Sheet Series)

False positive notes:

  • If auth is not cookie-based (e.g., Authorization header bearer token) CSRF risk is different; verify actual auth mechanism.

JQ-ATTR-001: Untrusted values MUST NOT be written into dangerous attributes without validation/allowlisting

Severity: Low (High for events like onclick)

Required:

  • MUST validate/allowlist URLs written into href, src, action, etc.
  • MUST block dangerous schemes; javascript: URLs are discouraged because they can execute code. (MDN Web Docs)
  • MUST NOT set event-handler attributes (onclick, onerror, etc.) from strings.
  • SHOULD avoid writing untrusted strings into style attributes; prefer toggling predefined CSS classes.

Insecure patterns:

  • $("a").attr("href", untrustedUrl)
  • $("img").attr("src", untrustedUrl)
  • $(el).attr("style", untrustedCss)
  • $(el).attr("onclick", untrustedJs)

Detection hints:

  • Search for .attr("href", .attr("src", .attr("style", .prop("href", .prop("src".
  • Trace whether inputs come from URL params, server JSON, DOM, or storage.

Fix:

  • Parse and validate URLs with new URL(value, location.origin) and allowlist protocols (https: etc.) and hostnames when needed.
  • For navigation targets, prefer relative paths you construct rather than full URLs.
  • Replace style strings with addClass/removeClass using predefined class names.

JQ-SELECTOR-001: User-controlled selector fragments MUST be escaped with jQuery.escapeSelector

Severity: Medium (can become High if it enables wrong-element selection in security-relevant UI)

Required:

  • If you must select by an ID/class that can contain special CSS characters, SHOULD use jQuery.escapeSelector() (available in jQuery 3.0+). (jQuery API)
  • MUST NOT concatenate raw attacker-controlled strings into selector expressions.

Insecure patterns:

  • $("#" + untrustedId)
  • $("[data-id='" + untrusted + "']") (especially without strict quoting/escaping)

Detection hints:

  • Search for "#" +, ". " +, or template strings used inside $( selectors.
  • Look for “select by user-supplied id”.

Fix:

  • $("#" + $.escapeSelector(untrustedId)) (jQuery API)
  • Prefer stable internal IDs over user-derived selectors.

Notes:

  • This is often “robustness”, but it can become security-relevant if incorrect selection causes UI to reveal/modify the wrong data or skip security-related prompts.

JQ-PROTOTYPE-001: Do not deep-merge untrusted objects; prevent prototype pollution

Severity: Medium

Required:

  • MUST NOT deep-merge ($.extend(true, …)) attacker-controlled objects into application objects without filtering dangerous keys.
  • MUST ensure jQuery is >= 3.4.0 to avoid CVE-2019-11358 prototype pollution behavior. (NVD)

Insecure patterns:

  • $.extend(true, target, untrustedObj)
  • $.extend(true, {}, defaults, untrustedObj) where untrustedObj comes from URL/JSON/storage

Detection hints:

  • Search for $.extend(true and inspect sources of merged objects.
  • Search for “merge options” / “apply config” patterns using untrusted JSON.

Fix:

  • Prefer:

    • Shallow merges with an allowlisted set of keys, or
    • A safe merge helper that explicitly rejects __proto__, prototype, constructor, and nested occurrences.
  • Keep jQuery patched.


JQ-CSP-001: CSP and Trusted Types SHOULD be used to make DOM XSS harder to introduce and exploit

Severity: Medium

Required:

  • SHOULD deploy CSP as defense-in-depth against XSS. (OWASP Cheat Sheet Series)
  • If enabling Trusted Types (require-trusted-types-for), MUST ensure DOM injection goes through Trusted Types policies. (MDN Web Docs)
  • When using jQuery 4, SHOULD take advantage of its Trusted Types support (TrustedHTML inputs). (blog.jquery.com)

Insecure patterns:

  • “Fixing” a jQuery feature by weakening CSP (script-src 'unsafe-inline' / 'unsafe-eval') without a compensating plan.
  • No CSP on applications that render user content or manipulate DOM heavily.

Detection hints:

  • Look for CSP headers (server configs, framework middleware, meta tags).
  • If not visible in repo, flag as “verify at edge/runtime”.

Fix:

  • Add CSP incrementally; start by eliminating inline scripts and inline event handlers, then tighten script-src.
  • Add Trusted Types where supported and feasible.

5) Practical scanning heuristics (how to “hunt”)

When actively scanning, use these high-signal patterns:

  • jQuery version / sourcing:

    • jquery-*.js in vendor/ or static/
    • package.json dependency jquery pinned to old versions
    • CDN script tags lacking integrity/crossorigin (jquery.com)
  • HTML injection sinks (DOM XSS):

    • .html(, .append(, .prepend(, .before(, .after(, .replaceWith(, .wrap(
    • $( where argument might be HTML / template strings
    • $.parseHTML( especially with keepScripts=true (jQuery API)
    • .load( (and whether selector is appended; script behavior differs) (jQuery API)
  • Script execution / dynamic code:

    • $.getScript(, dataType: "script" (jQuery API)
    • dataType: "jsonp" or jsonp: usage; callback=? patterns (jQuery API)
    • eval, new Function, setTimeout("…"), $.globalEval
  • Dangerous attribute writes:

    • .attr("href", …), .attr("src", …), .attr("style", …)
    • Any assignment of javascript:-like schemes or suspicious URL construction (MDN Web Docs)
  • Selector construction:

    • $("#" + user) and similar; fix via $.escapeSelector (jQuery API)
  • Prototype pollution:

    • $.extend(true, …, userObj); ensure jQuery >= 3.4.0 and filter dangerous keys (NVD)
  • CSRF posture for AJAX:

  • Defense-in-depth:

Always try to confirm:

  • data origin (untrusted vs trusted)
  • sink type (HTML insertion / script execution / attribute / selector / object merge)
  • protective controls present (sanitizer, allowlists, CSP, Trusted Types, CSRF validation)

6) Sources (accessed 2026-01-27)

Primary jQuery project documentation and release notes:

  • jQuery 4.0.0 release notes (Trusted Types/CSP changes; version info): https://blog.jquery.com/2026/01/17/jquery-4-0-0/. (blog.jquery.com)
  • Download jQuery (latest version info; CDN + SRI guidance): https://jquery.com/download/. (jquery.com)
  • jQuery API: .html(): https://api.jquery.com/html/. (jQuery API)
  • jQuery API: .text(): https://api.jquery.com/text/. (jQuery API)
  • jQuery API: .append(): https://api.jquery.com/append/. (jQuery API)
  • jQuery API: .load() (script execution behavior): https://api.jquery.com/load/. (jQuery API)
  • jQuery API: jQuery.parseHTML(…, keepScripts): https://api.jquery.com/jQuery.parseHTML/. (jQuery API)
  • jQuery API: $.ajax() (jsonp: false security note): https://api.jquery.com/jQuery.ajax/. (jQuery API)
  • jQuery API: $.getScript() (executes script): https://api.jquery.com/jQuery.getScript/. (jQuery API)
  • jQuery API: jQuery.escapeSelector(): https://api.jquery.com/jQuery.escapeSelector/. (jQuery API)

jQuery vulnerabilities / advisories:

  • NVD CVE-2019-11358 (prototype pollution; jQuery < 3.4.0): https://nvd.nist.gov/vuln/detail/CVE-2019-11358. (NVD)
  • NVD CVE-2020-11022 (XSS risk in DOM manipulation methods; patched in 3.5.0): https://nvd.nist.gov/vuln/detail/CVE-2020-11022. (NVD)
  • NVD CVE-2020-11023 (XSS risk involving <option>; patched in 3.5.0): https://nvd.nist.gov/vuln/detail/CVE-2020-11023. (NVD)
  • GitHub Security Advisory GHSA-gxr4-xjj5-5px2 (jQuery htmlPrefilter XSS; patched in 3.5.0): https://github.com/jquery/jquery/security/advisories/GHSA-gxr4-xjj5-5px2. (GitHub)

OWASP Cheat Sheet Series (web app security foundations relevant to jQuery usage):

  • XSS Prevention: https://cheatsheetseries.owasp.org/cheatsheets/Cross_Site_Scripting_Prevention_Cheat_Sheet.html. (OWASP Cheat Sheet Series)
  • DOM-based XSS Prevention: https://cheatsheetseries.owasp.org/cheatsheets/DOM_based_XSS_Prevention_Cheat_Sheet.html. (OWASP Cheat Sheet Series)
  • CSRF Prevention: https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.html. (OWASP Cheat Sheet Series)
  • HTTP Security Headers: https://cheatsheetseries.owasp.org/cheatsheets/HTTP_Headers_Cheat_Sheet.html. (OWASP Cheat Sheet Series)
  • Content Security Policy Cheat Sheet: https://cheatsheetseries.owasp.org/cheatsheets/Content_Security_Policy_Cheat_Sheet.html. (OWASP Cheat Sheet Series)

Browser/platform references (SRI, CSP, Trusted Types, and dangerous URL schemes):

  • MDN: Subresource Integrity (SRI): https://developer.mozilla.org/en-US/docs/Web/Security/Defenses/Subresource_Integrity. (MDN Web Docs)
  • W3C: SRI specification: https://www.w3.org/TR/sri-2/. (W3C)
  • MDN: CSP guide: https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/CSP. (MDN Web Docs)
  • MDN: require-trusted-types-for directive: https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Content-Security-Policy/require-trusted-types-for. (MDN Web Docs)
  • MDN: Trusted Types API: https://developer.mozilla.org/en-US/docs/Web/API/Trusted_Types_API. (MDN Web Docs)
  • W3C: Trusted Types specification: https://www.w3.org/TR/trusted-types/. (W3C)
  • MDN: javascript: URL scheme warning: https://developer.mozilla.org/en-US/docs/Web/URI/Reference/Schemes/javascript. (MDN Web Docs)
  • DOMPurify project documentation: https://github.com/cure53/DOMPurify. (GitHub)

Reference: Javascript Typescript Nextjs Web Server Security

Next.js (TypeScript/JavaScript) Web Security Spec (Next.js 16.1.x, Node.js 20.9+)

This document is designed as a security spec that supports:

  1. Secure-by-default code generation for new Next.js backend code (Route Handlers, API Routes, Server Actions, Proxy/Middleware).
  2. Security review / vulnerability hunting in existing Next.js repos (passive “notice issues while working” and active “scan the repo and report findings”).

It is intentionally written as a set of normative requirements (“MUST/SHOULD/MAY”) plus audit rules (what bad patterns look like, how to detect them, and how to fix/mitigate them).

Target scope: Next.js 16.1.x (latest line shown in the App Router docs) (Next.js), running on Node.js 20.9+ (per Next.js system requirements). (Next.js)


0) Safety, boundaries, and anti-abuse constraints (MUST FOLLOW)

  • MUST NOT request, output, log, or commit secrets (API keys, passwords, private keys, session cookies, OAuth tokens, process.env dumps, database URLs with credentials).
  • MUST NOT “fix” security by disabling protections (e.g., disabling origin checks, relaxing CORS to *, skipping authz checks, turning off cookie security flags, turning off CSP because it’s “hard”).
  • MUST provide evidence-based findings during audits: cite file paths, code snippets, and configuration values that justify each claim.
  • MUST treat uncertainty honestly: if a protection might exist in infrastructure (reverse proxy, CDN, WAF, platform headers), report it as “not visible in app code; verify at runtime/config”.
  • MUST assume all request-facing server code is reachable by attackers unless there is a clearly enforced auth boundary (not just “the UI doesn’t link to it”).
  • MUST treat TypeScript types as non-security boundaries: types do not validate runtime input; runtime checks are required. (Next.js)

1) Operating modes

1.1 Generation mode (default)

When asked to write new Next.js code or modify existing code:

  • MUST follow every MUST requirement in this spec.
  • SHOULD follow every SHOULD requirement unless the user explicitly says otherwise.
  • MUST prefer safe-by-default APIs and proven libraries over custom security code.
  • MUST avoid introducing new risky sinks (dynamic code execution, unsafe redirects, serving user files as HTML, SSRF URL fetchers, building SQL strings, etc.).

1.2 Passive review mode (always on while editing)

While working anywhere in a Next.js repo (even if the user did not ask for a security scan):

  • MUST “notice” violations of this spec in touched/nearby code.
  • SHOULD mention issues as they come up, with a brief explanation + safe fix.

1.3 Active audit mode (explicit scan request)

When the user asks to “scan”, “audit”, or “hunt for vulns”:

  • MUST systematically search the codebase for violations of this spec.
  • MUST output findings in a structured format (see §2.3).

Recommended audit order:

  1. Deployment entrypoints and environment (Dockerfiles, package.json scripts, hosting config).
  2. Next.js config (next.config.*), Proxy/Middleware, routing patterns.
  3. Authentication, sessions, cookies.
  4. CSRF protections and state-changing endpoints (Server Actions, Route Handlers, API Routes).
  5. XSS (React + CSP) and unsafe HTML rendering.
  6. Cache/data-leak hazards (static rendering + caching + “use cache”).
  7. File handling (uploads/downloads) and path traversal.
  8. Injection classes (SQL/ORM misuse, command execution, unsafe deserialization).
  9. Outbound requests (SSRF).
  10. Redirect handling (open redirects).
  11. CORS and security headers.

2) Definitions and review guidance

2.1 Untrusted input (treat as attacker-controlled unless proven otherwise)

In Next.js backends, untrusted input includes:

App Router:

  • Route Handler params and request data:

    • context.params (dynamic segments), search params (request.url, new URL(request.url).searchParams)
    • request.headers, request.cookies
    • await request.json(), await request.formData(), await request.text()
  • Dynamic APIs used in Server Components/Server Functions:

    • headers() and cookies() values (Next.js)

Pages Router:

  • req.query, req.cookies, req.body in pages/api/* handlers (Next.js)

Plus:

  • Anything from external systems (webhooks, third-party APIs, message queues)
  • Any persisted user content (DB rows) that originated from users

2.2 State-changing request

A request is state-changing if it can create/update/delete data, change auth/session state, trigger side effects (purchase, email send, webhook send), or initiate privileged actions.

Special note for Next.js:

  • Server Actions are invoked via network requests and can mutate state; treat them as state-changing endpoints. (Next.js)

2.3 Required audit finding format

For each issue found, output:

  • Rule ID:
  • Severity: Critical / High / Medium / Low
  • Location: file path + function/route name + line(s)
  • Evidence: the exact code/config snippet
  • Impact: what could go wrong, who can exploit it
  • Fix: safe change (prefer minimal diff)
  • Mitigation: defense-in-depth if immediate fix is hard
  • False positive notes: what to verify if uncertain

3) Secure baseline: minimum production configuration (MUST in production)

This is the smallest “production baseline” that prevents common Next.js backend misconfigurations.

3.1 Run Next.js in production mode (MUST)

  • MUST run next build + next start (or the managed platform equivalent), not next dev. Dev mode has different error/reporting behavior and is not designed for production exposure. (Next.js)
  • MUST ensure NODE_ENV=production in production (Next.js defaults NODE_ENV based on command; verify the runtime environment). (Next.js)

3.2 Put a reverse proxy / edge layer in front when self-hosting (MUST for public internet)

  • If self-hosting, MUST place a reverse proxy (e.g., nginx) or equivalent edge layer in front of the Next.js server to handle malformed requests, slow attacks, payload size limits, rate limiting, and similar concerns. (Next.js)

3.3 Baseline header/cookie posture (SHOULD)

  • SHOULD set a baseline of security headers globally (CSP, X-Content-Type-Options, clickjacking defense via CSP frame-ancestors and/or X-Frame-Options, etc.). Next.js provides guidance for implementing CSP via Proxy/headers. (Next.js)
  • MUST ensure auth/session cookies use secure attributes (Secure, HttpOnly, SameSite) as appropriate. (Next.js) IMPORTANT NOTE: Only set Secure in production environment. When running in a local dev environment over HTTP, do not set Secure property on cookies. You should do this conditionally based on if the app is running in production mode. You should also include a property like SESSION_COOKIE_SECURE which can be used to disable Secure cookies when testing over HTTP.

3.4 Clear separation between server-only and client code (MUST)

  • MUST prevent secrets and privileged logic from being bundled into client code.
  • MUST treat NEXT_PUBLIC_* environment variables as public (browser-exposed and inlined at build time). (Next.js)

4) Rules (generation + audit)

Each rule contains: required practice, insecure patterns, detection hints, and remediation.

NEXT-DEPLOY-001: Do not run next dev in production; ensure production mode behavior

Severity: High (if production)

NOTE: If they are deploying to a specific Next.js hosting provider, they do not need to worry about this.

Required:

  • MUST NOT deploy next dev or any development server mode to production.
  • MUST ensure production builds and production runtime are used for any public deployment. (Next.js)

Insecure patterns:

  • next dev in Docker CMD, Procfile, platform start command.
  • NODE_ENV=development in production environment config.
  • Debug/dev-only endpoints or flags exposed publicly.

Detection hints:

  • Search package.json scripts and deployment manifests for next dev.
  • Search infra for NODE_ENV=development or missing NODE_ENV.
  • Check Kubernetes/PM2/systemd entrypoints for next dev.

Fix:

  • Use next build during CI/build and next start at runtime (or platform-native build/run).
  • Ensure environment sets NODE_ENV=production.

Note:

  • Dev mode is fine for local development. Only flag if it is being used as a production entrypoint.

NEXT-SUPPLY-001: Stay on supported Next.js releases; patch quickly for security advisories

Severity: High (Critical if known-vulnerable version)

Required:

  • MUST run a supported Next.js version line and apply security updates promptly. Next.js documents an LTS/support policy. (Next.js)
  • MUST treat published advisories as urgent upgrade signals (e.g., update to a patched release). (GitHub)

Insecure patterns:

  • Running EOL Next.js major/minor without backported security fixes.
  • Ignoring advisories, or pinning next to a vulnerable range.

Detection hints:

  • Check package.json and lockfiles for next version.
  • Compare against Next.js support policy and advisories.

IMPORTANT: Any versions older than these minor versions are vulnerable to “react2shell” vulnerability (https://nextjs.org/blog/CVE-2025-66478): 15.0.5 15.1.9 15.2.6 15.3.6 15.4.8 15.5.7 16.0.7

Fix:

  • Upgrade next to a supported and patched version.
  • Add a dependency update process + CI checks.

NEXT-SECRETS-001: Secrets MUST NOT be committed or exposed to the browser

Severity: High (Critical if secret is client-exposed)

Required:

  • MUST store secrets in environment variables or a secret manager; MUST NOT commit .env* files.
  • MUST treat .env* as sensitive; Next.js warns you “almost never want to commit these files.” (Next.js)
  • MUST treat any NEXT_PUBLIC_* environment variable as public and browser-visible (inlined into the client bundle at build time). (Next.js)

Insecure patterns:

  • .env, .env.local, .env.production committed to git.
  • NEXT_PUBLIC_API_KEY, NEXT_PUBLIC_SECRET, NEXT_PUBLIC_DATABASE_URL, etc.
  • Rendering process.env values into HTML or returning them from API routes.

Detection hints:

  • Scan git history and repo files for .env content, DB_PASS=, API_KEY=, SECRET=.
  • Grep for NEXT_PUBLIC_ and review any sensitive-looking names.
  • Search for process.env usage in Client Components ("use client") and shared modules.

Fix:

  • Move secrets to server-only env vars (no NEXT_PUBLIC_ prefix).
  • Ensure .env* is ignored and secrets are injected at deploy time.
  • Rotate leaked keys.

NEXT-SECRETS-002: Avoid server-only → client bundling mistakes (server/client boundary is a security boundary)

Severity: High

Required:

  • MUST ensure server-only modules (DB clients, secret-dependent code) are not imported into Client Components or other client-bundled code paths.
  • SHOULD use server-only patterns/layers (e.g., a dedicated DAL and server-only modules) and treat boundary violations as security bugs. Next.js explicitly discusses the “server-only” concept for sensitive modules. (Next.js)

Insecure patterns:

  • Importing DB clients, admin SDKs, or secret-reading modules into "use client" components.
  • Shared lib/ modules imported by both server and client code that reference secrets.

Detection hints:

  • Search for "use client" and examine its imports for server-only dependencies.
  • Look for DB client packages (pg, mysql2, mongoose, prisma, admin SDKs) imported from components/ or other client paths.
  • Search for process.env access in UI components.

Fix:

  • Refactor into lib/server/* and only import from server contexts (Route Handlers, Server Components, Server Actions).
  • Add an explicit “server-only” guard pattern (and/or tests) to prevent accidental imports.

NEXT-AUTH-001: Authentication/authorization MUST be enforced server-side for every protected action

Severity: High

Required:

  • MUST enforce authn/authz in server-side code for:

    • Route Handlers (app/**/route.ts) (Next.js)
    • API Routes (pages/api/**) (Next.js)
    • Server Actions ("use server" functions invoked by clients) (Next.js)
  • MUST NOT rely on client-side checks (hiding UI, route guards on the client) as the only protection.

Insecure patterns:

  • Sensitive Route Handlers with no session verification.
  • Server Actions that mutate data but do not validate user identity/permissions.
  • “Authorization” checks in React components only.

Detection hints:

  • Enumerate all Route Handlers and API Routes; for each, identify whether it requires auth.
  • Grep for "use server" and review all exported actions for auth checks.
  • Search for admin actions triggered by query params / form submits.

Fix:

  • Centralize auth helpers and call them in every protected endpoint/action.
  • Implement least-privilege authorization checks (role/resource ownership) per action.

NEXT-AUTH-002: Proxy/Middleware-based auth MUST NOT create route coverage gaps

Severity: High

Required:

  • If using Proxy or Middleware for authentication checks, MUST ensure it covers every route that needs protection.
  • Next.js documentation notes Proxy can use a matcher, and for auth it’s recommended Proxy runs on all routes. (Next.js)
  • MUST treat matcher mistakes as an auth bypass risk.

Insecure patterns:

  • Proxy/Middleware only matches “pages” but not /api/*, or only matches some route groups.
  • “Denylist” style matchers that miss alternative request forms (framework-internal variants, RSC navigations, etc.).

Detection hints:

  • Inspect proxy.ts / middleware.ts and its matcher.
  • Compare matchers to the full set of routes (including app/api/** and pages/api/**).
  • Ensure static assets and Next internals are excluded only intentionally, and that sensitive routes are included.

Fix:

  • Prefer allowlisting protected route prefixes or running Proxy globally and doing internal allow/deny logic.
  • Add integration tests: request protected route without auth and assert denial.

Notes:

  • Proxy is commonly used for “optimistic checks”; it is not a complete authorization system by itself. (Next.js)

Severity: High

  • IMPORTANT NOTE: If cookies are not being used for auth (ie auth is via Authentication header or other passed token), then there is no CSRF risk.

Required:

  • MUST protect every state-changing endpoint that relies on cookies for auth (POST/PUT/PATCH/DELETE).
  • For Server Actions, Next.js performs an Origin/Host comparison to help prevent CSRF; do not disable or weaken it. (Next.js)
  • If Server Actions must be callable from additional trusted origins (e.g., a trusted proxy domain), MUST use allowedOrigins with a strict allowlist. (Next.js)
  • For Route Handlers and API Routes, MUST implement CSRF protections explicitly (tokens and/or strict Origin/Referer + SameSite + custom headers). Route Handlers are an “escape hatch” and require application-level security decisions. (Next.js)

Insecure patterns:

  • POST endpoints (including Server Actions) that mutate state and accept cross-site requests with no token/origin checks.
  • allowedOrigins: ['*'] (or broad wildcards) or “reflect Origin” logic.
  • Using GET requests to change state.

Detection hints:

  • Enumerate all state-changing endpoints and determine auth mechanism.
  • Search for allowedOrigins and confirm the list is small, specific, and justified. (Next.js)
  • In Route Handlers/API Routes: look for missing CSRF token validation or missing Origin/Referer checks.

Fix:

  • Implement a CSRF token strategy for cookie-auth endpoints.
  • Keep cookies SameSite=Lax or Strict when compatible; don’t treat SameSite alone as sufficient.
  • Use strict Origin validation for JSON API endpoints, especially when not using CSRF tokens.

Notes:

  • XSS can defeat CSRF protections; CSRF defenses do not replace XSS prevention.

NEXT-SESS-001: Session cookies MUST use secure attributes in production

Severity: Medium

Required (production, HTTPS):

  • MUST set session/auth cookies with:

    • Secure: true (HTTPS-only) IMPORTANT NOTE: Only set Secure in production environment. When running in a local dev environment over HTTP, do not set Secure property on cookies. You should do this conditionally based on if the app is running in production mode. You should also include a property like SESSION_COOKIE_SECURE which can be used to disable Secure cookies when testing over HTTP.
    • HttpOnly: true (not readable by JS)
    • SameSite: 'Lax' (recommended) or 'Strict' if compatible
  • Only use SameSite: 'none' when you truly need cross-site cookies, and then MUST also set Secure. Cookie options are supported in Next.js cookie APIs. (Next.js)

Insecure patterns:

  • secure: false in production.
  • httpOnly: false for auth cookies.
  • sameSite: 'none' without a clear need, especially on cookie-authenticated state-changing endpoints.

Detection hints:

  • Search for cookie setting sites (cookies().set(...), Set-Cookie headers, auth library cookie config).
  • Review cookie options used in Route Handlers and Server Actions. (Next.js)

Fix:

  • Set secure cookie attributes at the auth/session layer.
  • Reduce cookie scope: avoid wide domain unless you explicitly need subdomain-wide cookies.

NEXT-SESS-002: Sessions MUST be bounded and resistant to fixation/replay

Severity: Low

Required:

  • SHOULD set bounded session lifetimes appropriate to the app.
  • SHOULD rotate session identifiers on login and privilege changes.
  • MUST NOT store sensitive secrets directly in client-readable storage (including cookies that are not encrypted).

Insecure patterns:

  • Long-lived admin sessions with no rotation.
  • “Remember me forever” for privileged roles without additional risk controls.
  • Storing access tokens/refresh tokens in non-HttpOnly cookies or localStorage.

Detection hints:

  • Review auth library configuration for expiration and rotation.
  • Search for localStorage.setItem('token'...) and non-HttpOnly cookie usage.

Fix:

  • Use short lifetimes for privileged sessions; refresh with rotation.
  • Store only opaque session IDs in cookies; keep sensitive material server-side.

NEXT-INPUT-001: Runtime input validation is mandatory (TypeScript is not validation)

Severity: High

Required:

  • MUST validate and normalize all attacker-controlled input at runtime (schemas, type checks, bounds).
  • Next.js API Routes explicitly note req.body is any and must be validated before use. (Next.js)
  • MUST validate Server Action arguments (treat as hostile). (Next.js)

Insecure patterns:

  • Trusting req.body shape directly.
  • Passing params.id/searchParams directly into DB queries or file paths.
  • Parsing JSON and then assuming types without validation.

Detection hints:

  • Identify endpoints that accept JSON/form input and check for schema validation.
  • Grep for req.body. usage and for await request.json() usage in Route Handlers; verify validation exists.

Fix:

  • Add schema validation (e.g., zod/yup/valibot) and reject invalid input with 4xx.
  • Validate IDs as strict types (UUID/int) and enforce length/charset constraints.

NEXT-HEADERS-001: Essential security headers MUST be set (in app or at the edge)

Severity: Low

Required (typical web app):

  • SHOULD set:

    • CSP (Content-Security-Policy) (see NEXT-CSP-001)
    • X-Content-Type-Options: nosniff
    • Clickjacking defense (frame-ancestors in CSP and/or X-Frame-Options)
    • Referrer-Policy and Permissions-Policy when appropriate
  • MUST ensure cookies are set with secure attributes (see NEXT-SESS-001). (Next.js)

Insecure patterns:

  • No security headers anywhere (app or edge).
  • Allowing iframing unintentionally.
  • Content-Type sniffing possible due to missing nosniff.

Detection hints:

  • Check proxy.ts / middleware for response.headers.set(...). (Next.js)
  • If not visible in app code, flag as “verify at edge/CDN”.

Fix:

  • Set headers centrally (Proxy/Middleware or other centralized mechanism).
  • Ensure consistent headers across routes.

NEXT-CSP-001: Use a CSP to reduce XSS impact; prefer nonces for scripts

Severity: Medium

NOTE: It is most important to set the CSP’s script-src. All other directives are not as important and can generally be excluded for the ease of development.

Required:

  • SHOULD deploy a CSP, ideally with nonces for scripts.
  • SHOULD follow Next.js guidance for CSP implementation (including nonce generation and header application). (Next.js)
  • MUST avoid loosening CSP as a “fix” (e.g., script-src 'unsafe-inline') without explicit risk acceptance.

Insecure patterns:

  • CSP missing on apps that display user-generated HTML/markdown.
  • CSP that broadly enables inline scripts or eval without strict justification.

Detection hints:

  • Search for Content-Security-Policy header setting and examine its directives.
  • Check use of next/script and whether a nonce is provided when CSP requires it.

Fix:

  • Implement CSP per Next.js guidance; use a nonce and apply it consistently.
  • Reduce inline scripts; avoid eval.

Notes:

  • CSP is defense-in-depth; it does not replace proper output encoding and sanitization.

NEXT-XSS-001: Prevent reflected/stored XSS in React/Next rendering

Severity: High

Required:

  • MUST rely on React’s default escaping; MUST NOT insert untrusted HTML into the DOM without sanitization.

  • MUST treat these as high-risk sinks:

    • dangerouslySetInnerHTML
    • rendering user-controlled strings into <script> tags or event handler attributes
  • MUST avoid serving uploaded HTML as active HTML (serve as attachment or sanitize/transform).

Insecure patterns:

  • <div dangerouslySetInnerHTML={{ __html: userContent }} /> with no sanitizer.
  • Markdown renderers configured to allow raw HTML with no sanitizer.
  • Returning user content with Content-Type: text/html from a Route Handler.

Detection hints:

  • Search for dangerouslySetInnerHTML, __html:.
  • Search for template-like string concatenation that builds HTML.
  • Review any “render HTML” or “preview” features.

Fix:

  • Sanitize untrusted HTML with a well-maintained sanitizer; prefer strict allowlists.
  • Prefer rendering user content as text, not HTML.
  • Add CSP to reduce impact.

NEXT-ACTION-001: Server Actions MUST be treated like public endpoints

Severity: High (Critical for privileged actions)

Required:

  • MUST apply the same controls as for Route Handlers:

    • authn/authz
    • input validation
    • CSRF/origin protections
    • rate limiting for sensitive actions
  • MUST NOT assume Server Actions are “not reachable” or “internal”.

  • MUST understand Server Action request protections:

    • Next.js compares Origin with host to mitigate CSRF; extra origins must be explicitly allowlisted via allowedOrigins. (Next.js)

Insecure patterns:

  • "use server" functions that update DB state with no auth check.
  • Adding overly broad allowedOrigins to “make it work”.

Detection hints:

  • Grep for "use server" and inventory all exported actions.
  • Identify any action doing privileged writes; confirm it checks identity and permission.

Fix:

  • Wrap actions with an authz helper (fail closed).
  • Keep allowedOrigins minimal and audited.

NEXT-ACTION-002: Do not accidentally leak secrets through Server Action closure/binding patterns

Severity: Medium (High if important secrets are exposed)

Required:

  • MUST treat Server Action closed-over values as sensitive and design intentionally.
  • Next.js notes that closed-over values are encrypted/signed, but values passed through .bind are not encrypted; do not rely on .bind to protect secrets. (Next.js)
  • If using a stable encryption key for Server Actions across deployments, MUST treat it as a secret and store securely (do not commit/log it). (Next.js)

Insecure patterns:

  • myAction.bind(null, process.env.SECRET) or binding sensitive tokens/IDs that should not be client-influenced.
  • Logging action arguments that include secrets.

Detection hints:

  • Search for .bind( on Server Action functions.
  • Search for process.env usage near Server Actions.

Fix:

  • Avoid binding secrets into actions; fetch secrets server-side inside the action.
  • Keep action arguments minimal and validated.

NEXT-CACHE-001: Prevent data leaks via static rendering and shared caching

Severity: High (Critical if cross-user data leak)

Required:

  • MUST ensure pages/endpoints that return user-specific or sensitive data are not statically generated or cached in a shared way.
  • Route Handlers are not cached by default, but GET handlers can opt into caching/static behavior; do not do this for per-user data. (Next.js)
  • MUST treat use cache and similar caching mechanisms as potentially cross-user unless explicitly proven private; do not cache per-user DB results in shared caches. (Next.js)
  • SHOULD set explicit Cache-Control: no-store / private for sensitive responses (auth/session/user data APIs).

Insecure patterns:

  • export const dynamic = 'force-static' on a route that returns user-specific data. (Next.js)
  • Using use cache around a function that queries user-specific data without a per-user cache key. (Next.js)
  • Returning auth/session responses from GET endpoints with caching enabled.

Detection hints:

  • Search for dynamic = 'force-static', revalidate, use cache, cacheLife, unstable_cache.
  • Inspect all GET Route Handlers that are cached/static and confirm they only return public data.
  • Confirm that use of cookies()/headers() (dynamic APIs) is not accidentally removed in ways that make a route static. (Next.js)

Fix:

  • Mark sensitive routes as dynamic and set Cache-Control: no-store.
  • Ensure caching keys include user identity if caching is truly needed (and store it in a user-private cache).

NEXT-FILES-001: User uploads MUST be validated, stored safely, and served safely

Severity: Medium

Required:

  • MUST enforce upload size limits at the edge and in application logic.
  • MUST validate file type using allowlists and content checks (not only extension).
  • MUST store uploads outside the public/ directory (anything under public/ is served as static content by default).
  • MUST serve potentially active formats safely (Content-Disposition: attachment) unless explicitly intended.

Insecure patterns:

  • Accepting arbitrary file types and serving them back inline.
  • Using user-supplied filename as the storage path.
  • Writing uploads into public/uploads/ and serving them directly.

Detection hints:

  • Search for formData() / multipart parsing, fs.writeFile, storage SDK usage.
  • Look for any write path under public/.
  • Look for “download” endpoints that set Content-Type: text/html or serve user files inline.

Fix:

  • Use a dedicated object store (S3/GCS) or a safe server-side directory outside static roots.
  • Generate random server-side filenames; store metadata separately.

NEXT-PATH-001: Prevent path traversal and unsafe file access

Severity: High

Required:

  • MUST NOT use user-controlled strings as filesystem paths.
  • MUST validate and normalize identifiers; use allowlists and safe base directories.
  • MUST avoid reading arbitrary files based on request parameters.

Insecure patterns:

  • fs.readFile(request.nextUrl.searchParams.get('path'))
  • path.join(base, userPath) without normalization + boundary checks

Detection hints:

  • Search for fs. usage in Route Handlers/API Routes.
  • Search for path.join/path.resolve fed by request params.

Fix:

  • Use opaque IDs that map to server-side stored paths.
  • Enforce that resolved paths remain within an intended base directory.
  • Sanitize and disallow .. from being used when creating urls

NEXT-SSRF-001: Outbound requests using user-influenced URLs MUST be restricted

Severity: Medium (High in internal networks)

NOTE: This is mostly only applicable to apps which will be deployed in a cloud/LAN setup or have other http services on the same box. Sometimes the feature requires this functionality unavoidably (webhooks).

Required:

  • MUST treat any server-side fetch() to a user-provided URL as high-risk.

  • SHOULD allowlist destinations (hosts/domains) for URL fetch features.

  • SHOULD block:

    • localhost / private IP ranges / link-local
    • cloud metadata endpoints
  • MUST restrict protocols to http: and https:.

  • SHOULD set strict timeouts and restrict redirects.

Insecure patterns:

  • await fetch(req.query.url) or await fetch((await request.json()).url)
  • “URL preview” endpoints that fetch arbitrary URLs.

Detection hints:

  • Search for fetch( in server code and trace where the URL comes from.
  • Look for “webhook tester”, “preview”, “import from URL” features.

Fix:

  • Parse URL, enforce http/https, allowlist hostnames, re-resolve DNS/IP to block private ranges.
  • Set timeouts (AbortSignal) and limit redirects.

NEXT-REDIRECT-001: Prevent open redirects (including auth flows)

Severity: Low

Required:

  • MUST validate redirect targets derived from untrusted input (e.g., next, redirect, returnTo).
  • SHOULD prefer redirecting only to same-site relative paths.
  • MUST validate any absolute URL against an allowlist.
  • MUST ensure urls are http or https: schema, disallowing javascript: schema

Insecure patterns:

  • redirect(searchParams.get('next')!)
  • NextResponse.redirect(new URL(req.nextUrl.searchParams.get('to')!, req.url)) without checks

Detection hints:

  • Search for redirect( (server components/actions) and NextResponse.redirect.
  • Search for res.redirect( in API Routes. (Next.js)

Fix:

  • Only allow relative paths (/path) and reject protocol-relative (//evil.com) or absolute URLs.
  • If invalid, fall back to a safe default (home/dashboard).

NEXT-CORS-001: CORS must be explicit and least-privilege

Severity: Medium (High if misconfigured with credentials)

Required:

  • If CORS is not needed, MUST keep it disabled.

  • Next.js API Routes do not set CORS headers by default, meaning they are same-origin by default; only enable CORS when you truly need it. (Next.js)

  • If enabling CORS:

    • MUST allowlist trusted origins (no reflection of arbitrary Origin)
    • MUST be careful with credentialed requests (cookies); never combine broad origins with credentials.
    • SHOULD restrict methods and headers.

Insecure patterns:

  • Access-Control-Allow-Origin: * with Access-Control-Allow-Credentials: true
  • Reflecting Origin without validation.

Detection hints:

  • Search for Access-Control-Allow-Origin, cors, “CORS” middleware/wrappers.
  • Review preflight OPTIONS handlers.

Fix:

  • Implement strict origin allowlist and minimal methods/headers.
  • Ensure cookies aren’t exposed cross-origin unless necessary and reviewed.

NEXT-WEBHOOK-001: Webhook endpoints MUST verify authenticity using the raw body

Severity: Medium

Required:

  • MUST verify webhook signatures using the raw request body (not a re-serialized parsed object).
  • Next.js notes a use case for disabling body parsing is verifying the raw body of a webhook request. (Next.js)

Insecure patterns:

  • Verifying webhook signatures over JSON.stringify(req.body) (can change formatting).
  • Accepting webhooks with no signature verification and no allowlist.

Detection hints:

  • Find webhook endpoints (/api/webhook, /app/api/**/webhook).
  • Check whether they use raw body verification.

Fix:

  • Disable Next.js automatic body parsing only for those webhook routes, read raw bytes safely, verify signature, then parse.

NEXT-INJECT-001: Prevent SQL injection (use parameterized queries / ORM)

Severity: High

Required:

  • MUST use parameterized queries or an ORM that parameterizes under the hood.
  • MUST NOT build SQL by string concatenation / template strings with untrusted input.

Insecure patterns:

  • db.query(`SELECT * FROM users WHERE id = ${id}`)
  • "WHERE name = '" + user + "'"

Detection hints:

  • Grep for SELECT, INSERT, UPDATE, DELETE strings.
  • Trace untrusted input (params, searchParams, req.query, req.body, request.json()) into DB calls.

Fix:

  • Use prepared statements / ORM query APIs.
  • Validate and coerce types before querying.

NEXT-INJECT-002: Prevent OS command injection and unsafe subprocess use

Severity: Critical to High

Required:

  • MUST avoid executing OS commands with attacker-controlled input.

  • If subprocess is necessary:

    • MUST pass args as an array (not a single shell string)
    • MUST NOT use shell: true with attacker-influenced strings
    • SHOULD use strict allowlists for any variable component

Insecure patterns:

  • exec("convert " + filename)
  • spawn("bash", ["-c", userInput])
  • spawn(userInput, ["foo"])

Detection hints:

  • Search for child_process, exec, spawn, shell: true.

Fix:

  • Use library APIs instead of shell commands.
  • Hard-code commands and allowlist validated parameters (and use -- to separate flags where supported).

NEXT-INJECT-003: Avoid dynamic code execution and unsafe deserialization

Severity: High to Critical

Required:

  • MUST NOT use eval, new Function, vm.runIn* on untrusted strings.
  • MUST treat deserializing complex formats (YAML, XML, custom serialization) as risky; use safe parsers and strict schemas.

Insecure patterns:

  • eval(req.body.code)
  • Parsing YAML from user input with a non-safe schema.

Detection hints:

  • Search for eval(, new Function, vm., require( with non-literals.
  • Search for js-yaml, XML parsers, custom serializer usage on untrusted input.

Fix:

  • Remove dynamic execution; use safe interpreters or strict parsers.
  • Validate and constrain input.

NEXT-LOG-001: Logging MUST NOT leak secrets or sensitive headers

Severity: Medium

Required:

  • MUST NOT log:

    • Authorization headers
    • cookies / session tokens
    • request bodies containing credentials
    • environment variables or configuration dumps
  • SHOULD implement structured logging with redaction.

Insecure patterns:

  • console.log(req.headers) in auth endpoints
  • console.log(process.env) in server code

Detection hints:

  • Search for console.log(, logger.info(, debug( in server routes/actions.
  • Check for logs of headers/cookies/body.

Fix:

  • Redact sensitive fields; log only what is needed for debugging.
  • Use safe error messages for clients; keep detail server-side only.

NEXT-ERROR-001: Error handling MUST avoid leaking implementation details in production

Severity: Low

Required:

  • MUST not expose stack traces or internal error details to end users in production.
  • Ensure production mode behavior (Next.js production error handling differs from dev). (Next.js)

Insecure patterns:

  • Returning err.stack in JSON responses.
  • Showing detailed exception data to unauthenticated users.

Detection hints:

  • Search for res.status(500).json(err) or return Response.json(err).
  • Verify error responses are sanitized.

Fix:

  • Return generic error messages to clients; log details server-side with redaction.

NEXT-PROXY-001: Proxy/Middleware must not introduce header smuggling or unsafe header forwarding

Severity: Medium

Required:

  • MUST be careful when copying/forwarding request headers upstream:

    • Do not forward attacker-controlled x-forwarded-* headers unless you have a trusted proxy chain.
    • Do not forward Authorization/cookies to unrelated outbound services.
  • Next.js Proxy patterns often mutate headers; ensure this doesn’t create security issues.

Insecure patterns:

  • Blindly cloning all request headers to an outbound fetch() call.
  • Trusting x-forwarded-host or host to construct sensitive absolute URLs without allowlisting.

Detection hints:

  • Search headers() and request.headers usage (especially for URL building). (Next.js)
  • Search Proxy/Middleware for header rewrites.

Fix:

  • Allowlist forwarded headers explicitly.
  • Validate hostnames before using them to build callback URLs or redirects.

NEXT-HOST-001: Host/Origin-derived URL construction MUST be allowlisted

Severity: Medium

Required:

  • MUST NOT generate security-sensitive absolute URLs (password reset links, OAuth callback URLs, email verification links) directly from unvalidated Host headers.
  • For Server Actions, Origin/Host matching is part of CSRF mitigation; do not weaken it. (Next.js)

Insecure patterns:

  • const base = "https://" + request.headers.get("host")
  • Using unvalidated x-forwarded-host for absolute URL generation.

Detection hints:

  • Grep for .get('host'), .get('x-forwarded-host'), and absolute URL building.
  • Review auth-related email link generation code.

Fix:

  • Use a configured, allowlisted canonical app origin (e.g., APP_ORIGIN=https://example.com).
  • Allowlist hostnames; fail closed.

NEXT-DOS-001: Rate limiting and resource controls MUST exist for abuse-prone endpoints

Severity: Medium

Required:

  • SHOULD implement rate limiting/throttling for:

    • login, password reset, signup
    • expensive Server Actions
    • webhook ingestion
  • MUST implement request size limits (see NEXT-LIMITS-001).

  • If self-hosting, MUST rely on reverse proxy for additional protections. (Next.js)

Insecure patterns:

  • No throttling on login/reset endpoints.
  • Expensive actions callable without auth or with unlimited frequency.

Detection hints:

  • Identify auth endpoints and check for rate limiting.
  • Search for “send email”, “charge”, “generate report” flows.

Fix:

  • Add edge rate limiting and app-level user/IP throttles.
  • Add job queues for heavy work; return 202 when appropriate.

5) Practical scanning heuristics (how to “hunt”)

When actively scanning, use these high-signal patterns:

  • Production misconfig:

    • next dev, NODE_ENV=development, dev-only start commands (Next.js)
  • Secrets exposure:

    • .env committed, NEXT_PUBLIC_ on sensitive variables (Next.js)
    • process.env used in "use client" modules
  • Auth coverage:

    • app/**/route.ts or pages/api/** with no auth checks (Next.js)
    • "use server" actions with DB writes and no authz (Next.js)
    • proxy.ts / middleware.ts matchers that exclude sensitive routes (Next.js)
  • CSRF:

    • cookie-auth POST/PUT/PATCH/DELETE with no token/origin checks
    • serverActions.allowedOrigins too broad (Next.js)
  • XSS:

    • dangerouslySetInnerHTML, raw HTML markdown rendering
    • missing CSP / overly permissive CSP (Next.js)
  • Caching/data leak:

    • dynamic = 'force-static' on sensitive GET handlers (Next.js)
    • use cache, cacheLife, unstable_cache around user-specific data (Next.js)
  • Files:

    • writing uploads under public/
    • fs.readFile / path.join with request input
  • SSRF:

    • fetch(userProvidedUrl) from Route Handlers / Server Actions
  • Redirect:

    • redirect(searchParams.get('next')), NextResponse.redirect(...), res.redirect(req.query.next) (Next.js)
  • CORS:

    • wildcard origins, origin reflection, credentials + broad origins (Next.js)
  • Limits:

    • API routes with bodyParser: false and no raw-body verification for webhooks (Next.js)
    • serverActions.bodySizeLimit raised without justification (Next.js)
  • Dependency hygiene:

    • old next versions that conflict with support policy/advisories (Next.js)

Always try to confirm:

  • data origin (untrusted vs trusted)
  • sink type (HTML/DOM, SQL, subprocess, files, redirect, outbound HTTP)
  • protective controls present (schema validation, allowlists, middleware/proxy checks, authz helpers, edge protections)

6) Sources (accessed 2026-01-27)

Primary framework documentation (Next.js):

  • Next.js Docs: Installation (system requirements / Node version) — https://nextjs.org/docs/app/getting-started/installation
  • Next.js Docs: Route Handlers — https://nextjs.org/docs/app/getting-started/route-handlers
  • Next.js Docs: API Routes (Pages Router) — https://nextjs.org/docs/pages/building-your-application/routing/api-routes
  • Next.js Docs: Environment Variables — https://nextjs.org/docs/pages/guides/environment-variables
  • Next.js Docs: Data Security — https://nextjs.org/docs/app/guides/data-security
  • Next.js Docs: Content Security Policy — https://nextjs.org/docs/app/guides/content-security-policy
  • Next.js Docs: Proxy — https://nextjs.org/docs/app/getting-started/proxy
  • Next.js Docs: serverActions.allowedOrigins and serverActions.bodySizeLimithttps://nextjs.org/docs/app/api-reference/config/next-config-js/serverActions
  • Next.js Docs: cookies()https://nextjs.org/docs/app/api-reference/functions/cookies
  • Next.js Docs: headers()https://nextjs.org/docs/app/api-reference/functions/headers
  • Next.js Docs: Self-hosting (reverse proxy guidance) — https://nextjs.org/docs/pages/guides/self-hosting
  • Next.js Docs: Support policy (supported versions/LTS) — https://nextjs.org/docs/support-policy

Next.js security guidance & advisories:

  • Next.js Blog: How to think about security in Next.js — https://nextjs.org/blog/security-nextjs-server-components-actions
  • GitHub Security Advisory: Next.js DoS via Server Components / Server Actions (CVE-2026-23864) — https://github.com/advisories/GHSA-fq29-rrrv-cq2m
  • Next.js Blog: Security update (example security advisory context) — https://nextjs.org/blog/security-update

General web security references (recommended baseline):

  • OWASP Cheat Sheet Series (CSRF, Session Management, XSS Prevention, SSRF Prevention, File Upload, HTTP Headers) — https://cheatsheetseries.owasp.org/

Reference: Javascript Typescript React Web Frontend Security

React (JavaScript/TypeScript) Web Security Spec (React 19.x, TypeScript 5.x)

This document is designed as a security spec that supports:

  1. Secure-by-default code generation for new React code.
  2. Security review / vulnerability hunting in existing React code (passive “notice issues while working” and active “scan the repo and report findings”).

It is intentionally written as a set of normative requirements (“MUST/SHOULD/MAY”) plus audit rules (what bad patterns look like, how to detect them, and how to fix/mitigate them).


0) Safety, boundaries, and anti-abuse constraints (MUST FOLLOW)

  • MUST NOT request, output, log, or commit secrets (API keys, OAuth client secrets, private keys, session cookies, JWTs, signing keys).

    • Frontend note: anything shipped to the browser is observable by end users and attackers (view-source, devtools, proxies); never treat client code or “env vars in the bundle” as secret. (create-react-app.dev)
  • MUST NOT “fix” security by disabling protections (e.g., turning off CSP to “make it work”, adding unsafe-inline/unsafe-eval without a documented, constrained plan, disabling CSRF protections when using cookies, widening CORS, skipping sanitization, or “temporary” bypasses that ship). (OWASP Cheat Sheet Series)

  • MUST provide evidence-based findings during audits: cite file paths, code snippets, and configuration values that justify the claim.

  • MUST treat uncertainty honestly: if a protection might exist in infra (CDN/WAF/reverse proxy), report it as “not visible in app code; verify via runtime headers / edge config”.

  • MUST assume any data that crosses a trust boundary (URL, storage, network, postMessage, third-party scripts) can be attacker-influenced unless proven otherwise (see §2.1).


1) Operating modes

1.1 Generation mode (default)

When asked to write new React code or modify existing code:

  • MUST follow every MUST requirement in this spec.
  • SHOULD follow every SHOULD requirement unless the user explicitly says otherwise.
  • MUST prefer safe-by-default APIs and proven libraries over custom security code.
  • MUST avoid introducing new risky sinks (raw HTML insertion, direct DOM sinks like innerHTML, dynamic code execution, untrusted redirects/navigation, third‑party script injection, unsafe token storage, etc.). (MDN Web Docs)

1.2 Passive review mode (always on while editing)

While working anywhere in a React repo (even if the user did not ask for a security scan):

  • MUST “notice” violations of this spec in touched/nearby code.
  • SHOULD mention issues as they come up, with a brief explanation + safe fix.

1.3 Active audit mode (explicit scan request)

When the user asks to “scan”, “audit”, or “hunt for vulns”:

  • MUST systematically search the codebase for violations of this spec.
  • MUST output findings in a structured format (see §2.3).

Recommended audit order:

  1. App entrypoints, build tooling (Vite/Webpack/CRA/Next), deployment configs, CDN/static hosting config.
  2. Secrets & configuration exposure (env vars, runtime config injection, source maps).
  3. Rendering of untrusted data (XSS/DOM XSS), especially dangerouslySetInnerHTML, markdown/HTML renderers, URL attributes.
  4. Direct DOM usage and dangerous JS execution (innerHTML, eval, new Function, document.write, etc.).
  5. Auth & session patterns (token storage, cookies, CSRF interactions, OAuth flows).
  6. Network layer (axios/fetch wrappers, dynamic base URLs, credentialed requests, data exfil risks).
  7. Navigation & redirect handling (open redirects, window.location, target=_blank, window.open).
  8. Third-party scripts/tags/analytics and integrity controls (CSP, SRI).
  9. Service worker/PWA behavior (HTTPS, caching rules, update strategy).
  10. Security headers posture (CSP, clickjacking, nosniff, referrer policy) in app or at the edge. (OWASP Cheat Sheet Series)

2) Definitions and review guidance

2.1 Untrusted input (treat as attacker-controlled unless proven otherwise)

Examples include:

  • URL-derived data: window.location, query params, hash fragments, route params.
  • Any data from browser storage: localStorage, sessionStorage, IndexedDB (including data previously written by the app—because XSS or extensions can tamper with it). (OWASP Cheat Sheet Series)
  • Any data from cross-window messaging: window.postMessage payloads. (OWASP Cheat Sheet Series)
  • Any data from remote APIs, webhooks proxied to the client, GraphQL responses, CMS content, feature flag services.
  • Any persisted user content (profiles, comments, rich text, markdown) rendered in the UI.
  • Any data produced by third-party scripts or tag managers (treat as untrusted unless strongly controlled). (OWASP Cheat Sheet Series)

2.2 State-changing request (frontend perspective)

A request is state-changing if it can create/update/delete data, change auth/session state, trigger side effects (purchase, email send, webhook), or initiate privileged actions.

Frontend-specific note:

  • State changes are often triggered by fetch/axios calls or form submissions. If authentication is cookie-based, these calls can be CSRF-relevant (§4 REACT-CSRF-001). (OWASP Cheat Sheet Series)

2.3 Required audit finding format

For each issue found, output:

  • Rule ID:
  • Severity: Critical / High / Medium / Low
  • Location: file path + component/function + line(s)
  • Evidence: the exact code/config snippet
  • Impact: what could go wrong, who can exploit it
  • Fix: safe change (prefer minimal diff)
  • Mitigation: defense-in-depth if immediate fix is hard
  • False positive notes: what to verify if uncertain

3) Secure baseline: minimum production configuration (MUST in production)

This is the smallest “production baseline” that prevents common React frontend misconfigurations.

3.1 Production build and configuration hygiene (MUST)

  • MUST ship a production build (minified, no dev-only overlays/tools, correct mode flags).

  • MUST ensure build-time configuration does not embed secrets into the shipped JS/HTML/CSS. Build-time “environment variables” are not secret; treat them as public. (create-react-app.dev)

  • SHOULD treat source maps as sensitive operational artifacts:

    • Either don’t publish them publicly, or publish them only where intended (e.g., behind auth or to an error-reporting provider), because they can reveal code structure and internal URLs.

3.2 Browser-enforced protections (SHOULD, but baseline expectation for modern apps)

  • SHOULD deploy a CSP as defense-in-depth against XSS, and keep it compatible with your React build (avoid unsafe-inline and unsafe-eval unless strictly necessary and documented). (OWASP Cheat Sheet Series)
  • SHOULD use Subresource Integrity (SRI) for any third-party script/style loaded from a CDN (or self-host instead). (MDN Web Docs)
  • SHOULD enable clickjacking defenses via frame-ancestors (CSP) and/or X-Frame-Options, unless embedding is an explicit product requirement. (MDN Web Docs)

3.3 High-risk features baseline (MUST if used)

  • If rendering any user-provided HTML/markdown/rich text:

  • If using service workers / PWA:

    • MUST serve over HTTPS and implement a safe caching/update strategy (service workers are powerful request/response proxies). (MDN Web Docs)

4) Rules (generation + audit)

Each rule contains: required practice, insecure patterns, detection hints, and remediation.

REACT-CONFIG-001: Never embed secrets in the client bundle (env vars are public)

Severity: Critical (if secrets exposed)

Required:

  • MUST NOT place secrets in React code, in public/ assets, or in build-time environment variables intended for client consumption.
  • MUST assume any value available to the React app at runtime can be extracted by an attacker.

Insecure patterns:

  • Using build-time env vars for secrets:

    • process.env.REACT_APP_* containing private keys or credentials.
    • import.meta.env.VITE_* containing secrets.
  • Hard-coded secrets in JS/TS, .env committed, or secrets in public/config.json served to all users.

Detection hints:

  • Search for:

    • REACT_APP_, VITE_, NEXT_PUBLIC_, process.env., import.meta.env.
    • apiKey, secret, token, private, password, client_secret
  • Inspect public/ for runtime config JSON.

Fix:

  • Move secrets server-side (API, BFF, serverless function).
  • Use a backend to mint short-lived, scoped tokens if the browser needs to call third-party APIs.

Notes:

  • CRA explicitly warns not to store secrets and notes env vars are embedded into the build and visible to anyone inspecting files. (create-react-app.dev)
  • Vite explicitly notes that variables exposed to client code end up in the client bundle and should not contain sensitive info. (vitejs)

REACT-XSS-001: Do not use dangerouslySetInnerHTML with untrusted content (sanitize or avoid)

Severity: High (Only if you can prove attacker-controlled HTML reaches it)

Required:

  • MUST avoid dangerouslySetInnerHTML unless absolutely necessary.

  • If it must be used:

    • MUST sanitize untrusted HTML with a proven sanitizer (e.g., DOMPurify) and an allowlist-oriented configuration.
    • MUST keep the sanitization logic centralized and heavily reviewed.
    • SHOULD add a CSP and consider Trusted Types (see REACT-TT-001).

Insecure patterns:

  • <div dangerouslySetInnerHTML={{ __html: userHtml }} /> where userHtml is from API/URL/storage.
  • “Sanitization” done with regexes, ad-hoc stripping, or incomplete allowlists.

Detection hints:

  • Grep: dangerouslySetInnerHTML, __html:
  • Trace the origin of the HTML string (API/CMS/URL/localStorage).

Fix:

  • Replace with safe rendering:

    • Render structured data as React elements/components instead of HTML strings.
    • If rich text is required, sanitize with DOMPurify (or equivalent) and render the sanitized output.
  • Add CSP; remove dangerous sinks where possible.

Notes:

  • React explicitly warns that dangerouslySetInnerHTML is dangerous and can introduce XSS if misused. (React)
  • OWASP explicitly calls out React’s dangerouslySetInnerHTML without sanitization as a common framework “escape hatch” pitfall. (OWASP Cheat Sheet Series)
  • DOMPurify describes itself as an XSS sanitizer for HTML/SVG/MathML. (GitHub)

REACT-XSS-002: Rely on React’s escaping-by-default behavior; do not bypass it

Severity: High (when bypassed)

Required:

  • MUST render untrusted strings via normal JSX interpolation ({value}) and React props, which are escaped by default.
  • MUST NOT build HTML strings from untrusted data and then inject them into the DOM via any means.
  • SHOULD treat any “escape hatch” as high risk and require review.

Insecure patterns:

  • Converting untrusted text into HTML and injecting it:

    • element.innerHTML = userValue
    • document.write(userValue)
    • insertAdjacentHTML(..., userValue)

Detection hints:

  • Grep for DOM sinks: innerHTML, outerHTML, insertAdjacentHTML, document.write, DOMParser, createContextualFragment.

Fix:

  • Render text content through React (JSX) so it is escaped.
  • If you truly need HTML, sanitize and apply REACT-XSS-001 + REACT-TT-001.

Notes:

  • React documentation (JSX) states that React DOM escapes values embedded in JSX before rendering to help prevent injection attacks. (React)

REACT-DOM-001: Avoid DOM XSS injection sinks in React code (use safe alternatives)

Severity: High

Required:

  • MUST avoid direct DOM injection sinks, even outside React rendering, unless strongly controlled.

  • If a DOM sink is required:

    • MUST ensure inputs are trusted/validated/sanitized.
    • SHOULD enforce Trusted Types (REACT-TT-001).

Insecure patterns:

  • someEl.innerHTML = untrusted
  • document.write(untrusted)
  • new DOMParser().parseFromString(untrusted, 'text/html') followed by insertion

Detection hints:

  • Grep for: innerHTML, outerHTML, document.write, DOMParser, Range().createContextualFragment, insertAdjacentHTML

Fix:

  • Prefer:

    • textContent for text insertion.
    • React rendering rather than manual DOM manipulation.
    • A vetted sanitizer for any required HTML parsing.

Notes:

  • Trusted Types documentation defines HTML sinks like Element.innerHTML and document.write() as injection sinks that can execute script when given attacker-controlled input. (MDN Web Docs)
  • OWASP HTML5 guidance recommends using textContent instead of innerHTML for assigning untrusted data. (OWASP Cheat Sheet Series)

REACT-URL-001: Validate and constrain untrusted URLs used in href, src, navigation, and redirects

Severity: High Only when you can prove they are attacker controlled

Required:

  • MUST treat any URL derived from untrusted input as dangerous.

  • MUST allowlist schemes and (when applicable) hosts:

    • Typically allow only https: (and maybe http: for localhost/dev) and relative URLs for in-app navigation.
    • MUST explicitly block javascript: and dangerous data: uses unless you have specialized validation and a clear use case.
  • SHOULD prefer same-site relative paths (e.g., /settings) over absolute URLs.

  • MUST validate “returnTo/next/redirect” parameters (see REACT-REDIRECT-001).

Insecure patterns:

  • <img src={userProvidedUrl}>... (can be used for tracking / data exfil; also risky if used for scripts/iframes)
  • window.location = next
  • navigate(next) where next comes from query params without validation

Detection hints:

  • Search for:

    • href={, src={, window.location, location.href, window.open, navigate(, redirectTo, returnTo, next=
  • Track whether the value is derived from URL/query/storage/API.

Fix:

  • Implement a shared safeUrl() utility:

    • Parse with new URL(value, base)
    • Enforce scheme allowlist and host allowlist (or enforce same-origin)
    • For redirects: allow only relative paths (starting with /) or a strict allowlist of absolute origins.
  • Fall back to a safe default when validation fails.

Notes:

  • OWASP explicitly notes React’s dangerouslySetInnerHTML risk and also states React cannot safely handle javascript: or data: URLs without specialized validation. (OWASP Cheat Sheet Series)

REACT-MARKUP-001: Markdown / rich text rendering must be configured safely

Severity: Medium

Required:

  • MUST assume markdown/rich text can be attacker-controlled if it comes from users or CMS.

  • MUST ensure raw HTML is not rendered unless sanitized.

  • SHOULD prefer markdown renderers that:

    • Do not allow raw HTML by default, or
    • Can be configured to disallow raw HTML, or
    • Sanitize HTML output before rendering.

Insecure patterns:

  • Markdown rendering with “raw HTML passthrough” enabled (e.g., options/plugins that allow HTML).
  • Rendering user-provided SVG/MathML/HTML inline without sanitization.

Detection hints:

  • Search for common libraries and risky options:

    • marked, markdown-it, react-markdown, rehype-raw, sanitize: false, allowDangerousHtml, etc.
  • Look for dangerouslySetInnerHTML used with “markdown output”.

Fix:

  • Disable raw HTML passthrough.
  • Sanitize output with a proven sanitizer (e.g., DOMPurify) before rendering.

Notes:

  • OWASP XSS guidance emphasizes that framework escape hatches require output encoding and/or HTML sanitization. (OWASP Cheat Sheet Series)

REACT-TT-001: Use Trusted Types (with CSP) to harden DOM XSS sinks where feasible

Severity: Low

Required:

  • SHOULD consider enabling Trusted Types in report-only mode first, then enforce once violations are addressed.
  • SHOULD centralize Trusted Types policies and treat them as high-risk code requiring review.
  • MUST NOT create permissive policies that simply “pass through” untrusted strings.

Insecure patterns:

  • A Trusted Types policy that returns the raw string without sanitization for HTML sinks.
  • Many scattered policies across the codebase (hard to audit).

Detection hints:

  • Search for:

    • trustedTypes.createPolicy
    • CSP directives: require-trusted-types-for, trusted-types
  • Search for remaining DOM sinks (REACT-DOM-001).

Fix:

  • Implement a small number of tightly scoped policies:

    • HTML policy uses sanitizer (DOMPurify or equivalent).
    • Script URL policy uses strict allowlists.
  • Run in report-only mode, fix violations, then enforce.

Notes:

  • MDN describes Trusted Types as a way to ensure input is transformed (commonly sanitized) before being passed to injection sinks, and highlights HTML sinks (innerHTML, document.write) and JS URL sinks (script.src). (MDN Web Docs)
  • The W3C Trusted Types spec frames this as reducing DOM XSS risk by locking down sinks to typed values created by reviewed policies. (W3C)

REACT-CSP-001: Deploy and maintain a CSP as defense-in-depth (especially when rendering untrusted content)

Severity: Medium to High

Required:

  • SHOULD deploy CSP in production; MUST do so for apps that render untrusted content or integrate third-party scripts.
  • SHOULD avoid unsafe-inline and unsafe-eval when possible.
  • SHOULD use CSP nonces/hashes for inline scripts if needed, and keep policy realistic.
  • SHOULD use CSP to require/encourage SRI where appropriate.

Insecure patterns:

  • No CSP at all on the app shell (SPA entry HTML).
  • CSP that relies on unsafe-inline/unsafe-eval broadly without justification.
  • script-src * or overly broad sources.

Detection hints:

  • Look for CSP configuration:

    • Server/CDN config, headers in index.html responses, or framework config.
  • If absent in repo, mark as “verify at edge”.

Fix:

  • Add CSP via HTTP response headers (preferred).
  • Start with report-only to reduce breakage, then enforce.

Notes:

  • OWASP describes CSP as “defense in depth” against XSS and notes it can help enforce SRI even on static sites, but should not be the only defense. (OWASP Cheat Sheet Series)

REACT-SRI-001: Use Subresource Integrity (SRI) for third-party scripts and styles (or self-host)

Severity: Low

Required:

  • MUST treat third-party JS as equivalent to running arbitrary code in your origin.

  • If loading from a CDN or third party:

    • SHOULD use SRI (integrity=...) and crossorigin where applicable.
    • SHOULD pin exact versions (avoid “latest” URLs).
    • SHOULD prefer self-hosting for critical code.

Insecure patterns:

  • <script src="https://cdn.example.com/lib/latest.js"></script> with no integrity.
  • Tag managers that dynamically load arbitrary scripts without governance.

Detection hints:

  • Search in public/index.html, templates, or SSR wrappers for:

    • <script src=, <link rel="stylesheet" href=
    • Tag manager snippets (GTM, Segment, etc.)
  • Identify scripts loaded dynamically in runtime JS.

Fix:

  • Add SRI hashes for stable third-party assets or self-host.
  • Apply governance controls for tag managers (see REACT-3P-001).

Notes:

  • MDN describes SRI as a security feature enabling browsers to verify fetched resources (e.g., from a CDN) haven’t been manipulated by checking a cryptographic hash. (MDN Web Docs)
  • OWASP CSP guidance notes CSP can enforce SRI and is useful even on static sites. (OWASP Cheat Sheet Series)

REACT-3P-001: Third-party JavaScript and tag managers must be minimized and governed

Severity: High

Required:

  • MUST minimize third-party scripts and treat each as a supply-chain risk.

  • MUST know exactly what third-party JS executes in your origin and why.

  • SHOULD implement governance:

    • Review and pin versions (or mirror in-house).
    • Restrict data access (data-layer approach).
    • Use SRI and CSP; consider sandboxing untrusted UI in iframes where possible.

Insecure patterns:

  • Unreviewed analytics/ads scripts running with full access to DOM, cookies, storage, and user data.
  • Tag managers that can be changed by non-engineering roles with no change control.

Detection hints:

  • Search for common vendor snippets in HTML/JS:

    • GTM, Segment, Hotjar, FullStory, etc.
  • Look for dynamic script insertion:

    • document.createElement('script'), .src = ..., .appendChild(script)

Fix:

  • Reduce to only necessary vendors.

  • Where feasible:

    • Self-host or mirror scripts.
    • Use SRI.
    • Limit data exposure via a controlled data layer.

Notes:

  • OWASP notes third-party JS server compromise can inject malicious JS, and highlights risks like arbitrary code execution and disclosure of sensitive info to third parties. (OWASP Cheat Sheet Series)

REACT-AUTH-001: Token and session handling must be resilient to XSS (avoid sensitive storage in Web Storage)

Severity: Medium

Required:

  • SHOULD avoid storing session identifiers or long-lived tokens in localStorage (and generally in Web Storage) because XSS can exfiltrate them.

  • If tokens must exist client-side:

    • SHOULD prefer in-memory storage with short lifetimes and refresh mechanisms.
    • MUST scope and rotate tokens; avoid long-lived bearer tokens in persistent storage.
  • SHOULD prefer HTTPOnly cookies for session tokens when possible (requires CSRF strategy: see REACT-CSRF-001).

Insecure patterns:

  • localStorage.setItem('token', ...) / sessionStorage.setItem('token', ...) for auth tokens.
  • Persisting refresh tokens in localStorage.
  • Treating data from Web Storage as trusted.

Detection hints:

  • Grep for: localStorage., sessionStorage., setItem(, getItem(, token, jwt, refresh
  • Search auth code for “remember me” storing tokens persistently.

Fix:

  • Move to HTTPOnly cookies (server change) + CSRF protections, or use short-lived in-memory tokens.
  • Reduce token scope and lifetime.

Notes:

  • OWASP HTML5 guidance recommends avoiding sensitive info and session identifiers in local storage and warns that a single XSS can steal all data in Web Storage. (OWASP Cheat Sheet Series)
  • OAuth browser-based apps guidance discusses that tokens stored in persistent browser storage like localStorage can be accessible to malicious JS (e.g., via XSS). (IETF Datatracker)

Severity: High

NOTE: If the application does not use cookie based auth (using Authentication header for example), then CSRF is not a concern.

Required:

  • If the app relies on cookies for authentication:

    • MUST protect state-changing requests (POST/PUT/PATCH/DELETE) against CSRF.
    • SHOULD include a CSRF token mechanism (synchronizer token or double-submit cookie) or other robust pattern appropriate to the backend.
    • SHOULD use SameSite cookies as defense-in-depth, not as the sole defense.

Insecure patterns:

  • fetch('/api/transfer', { method: 'POST', credentials: 'include' }) with no CSRF token/header, relying only on cookies.
  • Using GET for state-changing operations.

Detection hints:

  • Enumerate state-changing network calls and check:

    • Is credentials: 'include' or withCredentials: true used?
    • Is a CSRF token header included (e.g., X-CSRF-Token)?
  • Search for “csrf” utilities; if absent, treat as suspicious.

Fix:

  • Add CSRF token flow:

    • Fetch token from a safe endpoint and attach to state-changing requests.
    • Validate server-side.
  • Keep SameSite cookies and Origin/Referer validation as defense-in-depth.

Notes:

  • OWASP CSRF guidance explains SameSite behavior (Lax/Strict/None) as a defense-in-depth technique and why Lax is often the usability/security balance, but it is not a complete substitute for CSRF protections. (OWASP Cheat Sheet Series)

REACT-AUTHZ-001: Do not rely on frontend-only authorization

Severity: High (only if used as primary protection)

Required:

  • MUST treat all frontend authorization checks as UX only.
  • MUST enforce authorization on the server for any protected resource or action.

Insecure patterns:

  • “Protected” actions hidden in UI but callable by API without server checks.
  • Client checks like if (user.isAdmin) { showAdminPanel(); } with no server-side enforcement.

Detection hints:

  • Look for UI gating around sensitive actions and verify server endpoints enforce authorization.
  • In a frontend-only audit, report as “client checks are not security; verify backend”.

Fix:

  • Add/confirm server-side authorization checks.
  • Keep frontend gating only as convenience.

Notes:

  • This is a general web app security property; React cannot protect server resources by itself.

REACT-NET-001: Prevent data exfiltration and credential leakage via dynamic outbound requests

Severity: Medium to High

Required:

  • MUST avoid making authenticated requests to attacker-controlled origins.

  • SHOULD avoid allowing user input to control request destination (scheme/host/port).

  • SHOULD centralize network clients (fetch/axios) with:

    • fixed baseURL (or strict allowlist),
    • strict handling of redirects,
    • explicit credentials usage.

Insecure patterns:

  • fetch(userProvidedUrl, { credentials: 'include' })
  • axios.create({ baseURL: userProvidedBase })
  • “URL fetch/preview” features in the client that hit arbitrary domains with sensitive headers.

Detection hints:

  • Search for fetch( / axios( where the first argument or baseURL is derived from:

    • query params, localStorage, API responses, postMessage
  • Search for credentials: 'include', withCredentials: true.

Fix:

  • Enforce destination allowlists; disallow cross-origin requests unless explicitly required.
  • Strip credentials/Authorization headers for any non-allowlisted destination.

Notes:

  • Even if the browser limits some cross-origin behavior, leaking tokens/headers to untrusted endpoints is still a common failure mode.

REACT-REDIRECT-001: Prevent open redirects and untrusted navigation

Severity: Medium

Required:

  • MUST validate redirect/navigation targets derived from untrusted input (next, returnTo, redirect).
  • SHOULD only allow same-site relative paths, or a strict allowlist of trusted origins for absolute URLs.

Insecure patterns:

  • window.location.href = new URLSearchParams(location.search).get('next')
  • navigate(next) where next comes from query params.

Detection hints:

  • Search for: next, returnTo, redirect, window.location, navigate(
  • Trace origin of the redirect target.

Fix:

  • Only allow relative paths (/^\/[^\s]*$/) or allowlisted origins.
  • Fall back to a safe default (e.g., /) when invalid.

Notes:

  • Open redirects are frequently used in phishing and can undermine SSO/OAuth flows.

REACT-SW-001: Service workers are high-privilege; require HTTPS and safe caching/update rules

Severity: Medium

Required:

  • MUST serve service workers over HTTPS (except localhost dev), and deploy only in secure contexts.
  • MUST avoid caching sensitive authenticated API responses unless explicitly designed and threat-modeled.
  • SHOULD implement safe update strategy (prompt reload, versioned caches, remove old caches on activate).

Insecure patterns:

  • Registering a service worker for an authenticated app and caching “everything” indiscriminately.
  • Long-lived caches containing PII or user-specific content shared across accounts.

Detection hints:

  • Search for:

    • navigator.serviceWorker.register
    • workbox, precacheAndRoute, custom fetch handlers
  • Inspect caching patterns (caches.open, cache.put, respondWith).

Fix:

  • Restrict caching to static assets only (JS/CSS/images) unless you have a designed offline model.
  • Ensure cache keys are user-scoped if user-specific data must be cached.
  • Provide a clear update mechanism.

Notes:

  • MDN notes service workers require HTTPS for security reasons and act like a proxy for requests/responses. (MDN Web Docs)
  • “Secure contexts” exist to prevent MITM attackers from accessing powerful APIs; service workers are an example of such a powerful feature. (MDN Web Docs)

REACT-HEADERS-001: Ensure essential security headers are set for the React app shell (app or edge)

Severity: Medium

Required (typical SPA served from an origin):

  • SHOULD set:

    • CSP (Content-Security-Policy)
    • X-Content-Type-Options: nosniff
    • Clickjacking protection (frame-ancestors in CSP and/or X-Frame-Options)
    • Referrer-Policy
    • Permissions-Policy as appropriate
  • MUST ensure these are set somewhere (CDN/edge/server), even if not in repo.

Insecure patterns:

  • No security headers anywhere (app or edge).
  • CSP missing on apps that render untrusted content or use third-party scripts.

Detection hints:

  • Check server/CDN config in repo (nginx, Cloudflare, Vercel config, etc.).
  • If absent, flag as “verify at runtime/edge”.

Fix:

  • Set headers centrally at the edge.
  • Keep CSP realistic and iterative (report-only → enforce).

Notes:

  • MDN clickjacking guidance discusses defenses including X-Frame-Options and CSP frame-ancestors. (MDN Web Docs)
  • OWASP CSP guidance explains delivery via response headers and recommends headers as the preferred mechanism. (OWASP Cheat Sheet Series)

REACT-POSTMSG-001: postMessage must validate origin and treat payload as untrusted data

Severity: Medium to High (depends on what messages can do)

Required:

  • MUST specify exact targetOrigin when sending messages (not *) unless there is a strict reason.
  • MUST validate event.origin on receipt and validate message shape.
  • MUST NOT evaluate message data as code or insert it into the DOM as HTML.

Insecure patterns:

  • window.postMessage(data, '*') to unknown targets.

  • Receiving:

    • window.addEventListener('message', (e) => { eval(e.data) })
    • element.innerHTML = e.data

Detection hints:

  • Search: postMessage(, addEventListener('message'
  • Check for origin checks and safe handling.

Fix:

  • Add strict origin allowlists and schema validation (e.g., zod).
  • Treat message payload strictly as data; render safely via React.

Notes:

  • OWASP HTML5 guidance recommends specifying expected origin for postMessage, checking sender origin, validating data, and avoiding eval/innerHTML with message content. (OWASP Cheat Sheet Series)

REACT-FILE-001: File uploads and previews must not create client-side active content vulnerabilities

Severity: Medium (can be High if stored-XSS possible)

Required:

  • MUST treat user-uploaded files and previews as potentially malicious.
  • MUST NOT render uploaded HTML/SVG/other active content inline unless sanitized and explicitly required.
  • SHOULD validate file types client-side for UX, but MUST rely on server-side validation for security.

Insecure patterns:

  • Rendering user-uploaded HTML as content.
  • Inline rendering of untrusted SVG/HTML via dangerouslySetInnerHTML or <iframe srcdoc=...> without sanitization.

Detection hints:

  • Search for upload components and preview logic:

    • input type="file", FileReader, URL.createObjectURL, <iframe>, <object>, <embed>.
  • Trace where uploaded content is later displayed.

Fix:

  • Restrict accepted types, sanitize where needed, and prefer download/attachment flows for risky types.
  • Ensure server enforces the real policy (type checking, renaming, scanning, storing outside webroot).

Notes:

  • OWASP file upload guidance highlights allowlisting extensions, validating file type, generating filenames, limiting size, storing outside webroot, and considering “client-side active content (XSS, CSRF, etc.)” when files are publicly retrievable. (OWASP Cheat Sheet Series)

REACT-SUPPLY-001: Dependency and supply-chain hygiene (frontend + build tooling)

Severity: Low

Required:

  • MUST use a lockfile and enforce reproducible installs in CI.

  • SHOULD regularly audit dependencies and respond quickly to advisories for:

    • React, react-dom, router libs, build tooling (Vite/Webpack), sanitizers, auth libs, etc.
  • SHOULD reduce exposure to install-time script attacks and typosquatting risk.

Audit focus:

  • CI should use npm ci (or Yarn frozen lockfile / pnpm equivalent) to prevent drift.
  • Use vulnerability scanning (npm audit, GitHub Dependabot/alerts, etc.).

Insecure patterns:

  • No lockfile or lockfile ignored in CI.
  • npm install in CI producing non-reproducible builds.
  • Unpinned or unreviewed high-risk deps; sudden major updates without review.
  • Blindly running install scripts from third-party packages.

Detection hints:

  • Check for lockfiles: package-lock.json, yarn.lock, pnpm-lock.yaml.
  • Check CI scripts for npm install vs npm ci.
  • Search for postinstall scripts and suspicious build steps.

Fix:

  • Use lockfile and enforce it in CI (e.g., npm ci).
  • Run audits regularly; pin/upgrade responsibly.
  • Consider restricting install scripts where feasible.

Notes:

  • npm docs describe npm audit as submitting the project dependency tree to the registry to receive a report of known vulnerabilities and (optionally) applying remediations via npm audit fix, while noting some vulns require manual review. (npm Docs)
  • npm docs describe npm ci as intended for automated/CI environments, requiring an existing lockfile and failing if package.json and lockfile do not match. (npm Docs)
  • OWASP NPM security guidance recommends enforcing the lockfile and explicitly calls out npm ci / yarn install --frozen-lockfile to abort on inconsistencies, and highlights the risk of install-time scripts and the option to use --ignore-scripts to reduce attack surface. (OWASP Cheat Sheet Series)

5) Practical scanning heuristics (how to “hunt”)

When actively scanning, use these high-signal patterns:

  • Raw HTML / XSS escape hatches:

    • dangerouslySetInnerHTML, __html:
    • Markdown HTML passthrough flags: rehype-raw, allowDangerousHtml, sanitize: false
  • DOM XSS sinks:

    • innerHTML, outerHTML, insertAdjacentHTML, document.write, DOMParser, createContextualFragment
  • Dangerous JS execution:

    • eval(, new Function(, setTimeout(", setInterval("
  • Untrusted URL injection / navigation:

    • href={ / src={ with untrusted values
    • window.location, location.href, window.open, navigate(
    • Query params: next, returnTo, redirect
  • Token/session risk:

    • localStorage.setItem, sessionStorage.setItem, getItem( with token, jwt, refresh
  • Cookie/CSRF coupling:

    • credentials: 'include', withCredentials: true on state-changing requests without CSRF headers
  • Third-party scripts:

    • <script src=...> in public/index.html
    • Tag manager snippets and dynamic script insertion
  • Service workers:

    • navigator.serviceWorker.register, Workbox usage, custom fetch handlers
  • postMessage:

    • postMessage( with *, missing event.origin checks
  • Supply chain:

    • Missing lockfile, CI uses npm install, no audit step, risky postinstall scripts

Always try to confirm:

  • data origin (untrusted vs trusted)
  • sink type (React escape hatch vs DOM sink vs navigation vs storage)
  • protective controls present (sanitization, allowlists, CSP/Trusted Types, CSRF tokens, headers, governance)

6) Sources (accessed 2026-01-26)

Primary React documentation:

  • React 19 stable announcement — https://react.dev/blog/2024/12/05/react-19 (React)
  • React DOM docs: dangerouslySetInnerHTML warning — https://react.dev/reference/react-dom/components/common#dangerouslysetting-the-inner-html (React)
  • React (legacy) JSX escaping statement — https://legacy.reactjs.org/docs/introducing-jsx.html (React)

OWASP Cheat Sheet Series:

  • Cross Site Scripting Prevention (framework escape hatches; React dangerouslySetInnerHTML; URL validation notes) — https://cheatsheetseries.owasp.org/cheatsheets/Cross_Site_Scripting_Prevention_Cheat_Sheet.html (OWASP Cheat Sheet Series)
  • Content Security Policy — https://cheatsheetseries.owasp.org/cheatsheets/Content_Security_Policy_Cheat_Sheet.html (OWASP Cheat Sheet Series)
  • Cross-Site Request Forgery Prevention — https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.html (OWASP Cheat Sheet Series)
  • HTML5 Security (Web Storage, postMessage, tabnabbing, sandboxed frames) — https://cheatsheetseries.owasp.org/cheatsheets/HTML5_Security_Cheat_Sheet.html (OWASP Cheat Sheet Series)
  • Third Party JavaScript Management — https://cheatsheetseries.owasp.org/cheatsheets/Third_Party_Javascript_Management_Cheat_Sheet.html (OWASP Cheat Sheet Series)
  • File Upload — https://cheatsheetseries.owasp.org/cheatsheets/File_Upload_Cheat_Sheet.html (OWASP Cheat Sheet Series)
  • NPM Security best practices — https://cheatsheetseries.owasp.org/cheatsheets/NPM_Security_Cheat_Sheet.html (OWASP Cheat Sheet Series)

Browser / platform references (MDN, W3C):

  • Trusted Types API — https://developer.mozilla.org/en-US/docs/Web/API/Trusted_Types_API (MDN Web Docs)
  • W3C Trusted Types spec — https://www.w3.org/TR/trusted-types/ (W3C)
  • Subresource Integrity — https://developer.mozilla.org/en-US/docs/Web/Security/Subresource_Integrity (MDN Web Docs)
  • Clickjacking defenses overview — https://developer.mozilla.org/en-US/docs/Web/Security/Attacks/Clickjacking (MDN Web Docs)
  • Using Service Workers (HTTPS requirement; proxy-like behavior) — https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API/Using_Service_Workers (MDN Web Docs)
  • Secure contexts (powerful APIs restricted to HTTPS) — https://developer.mozilla.org/en-US/docs/Web/Security/Defenses/Secure_Contexts (MDN Web Docs)
  • Link rel values (noopener/noreferrer) — https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/rel (MDN Web Docs)

Build tooling / env exposure references:

  • Create React App env variables warning — https://create-react-app.dev/docs/adding-custom-environment-variables/ (create-react-app.dev)
  • Vite env variables security notes — https://vite.dev/guide/env-and-mode (vitejs)

Auth/token storage guidance:

  • OAuth 2.0 for Browser-Based Apps (token storage discussion) — https://datatracker.ietf.org/doc/html/draft-ietf-oauth-browser-based-apps (IETF Datatracker)

Dependency tooling references:

  • npm audit docs — https://docs.npmjs.com/cli/v10/commands/npm-audit/ (npm Docs)
  • npm ci docs — https://docs.npmjs.com/cli/v10/commands/npm-ci/ (npm Docs)

Sanitizer reference:

  • DOMPurify — https://github.com/cure53/DOMPurify (GitHub)

16: https://datatracker.ietf.org/doc/html/draft-ietf-oauth-browser-based-apps

            draft-ietf-oauth-browser-based-apps-26
        
    "

Reference: Javascript Typescript Vue Web Frontend Security

Vue.js Web Security Spec (Vue 3.x, TypeScript/JavaScript, common tooling: Vite)

This document is designed as a security spec that supports:

  1. Secure-by-default code generation for new Vue code.
  2. Security review / vulnerability hunting in existing Vue code (passive “notice issues while working” and active “scan the repo and report findings”).

It is intentionally written as a set of normative requirements (“MUST/SHOULD/MAY”) plus audit rules (what bad patterns look like, how to detect them, and how to fix/mitigate them).


0) Safety, boundaries, and anti-abuse constraints (MUST FOLLOW)

  • MUST NOT request, output, log, or commit secrets (API keys, passwords, private keys, session cookies, auth tokens).
  • MUST NOT “fix” security by disabling protections (e.g., weakening CSP, turning on unsafe template compilation, using v-html as a shortcut, bypassing backend auth, or “just store the token in localStorage”).
  • MUST provide evidence-based findings during audits: cite file paths, code snippets, and configuration values that justify the claim.
  • MUST treat uncertainty honestly: if a protection might exist at the edge (CDN, reverse proxy, WAF, server headers), report it as “not visible in repo; verify runtime/infra config”.
  • MUST remember the frontend trust model: any code shipped to browsers is attacker-readable and attacker-modifiable. Secrets and “security enforcement” cannot rely on frontend-only logic.

1) Operating modes

1.1 Generation mode (default)

When asked to write new Vue code or modify existing code:

  • MUST follow every MUST requirement in this spec.
  • SHOULD follow every SHOULD requirement unless the user explicitly says otherwise.
  • MUST prefer safe-by-default framework features and proven libraries over custom security code.
  • MUST avoid introducing new risky sinks (runtime template compilation, v-html / innerHTML, unsafe URL navigation, dynamic script injection, etc.). (Vue.js)

1.2 Passive review mode (always on while editing)

While working anywhere in a Vue repo (even if the user did not ask for a security scan):

  • MUST “notice” violations of this spec in touched/nearby code.
  • SHOULD mention issues as they come up, with a brief explanation + safe fix.

1.3 Active audit mode (explicit scan request)

When the user asks to “scan”, “audit”, or “hunt for vulns”:

  • MUST systematically search the codebase for violations of this spec.
  • MUST output findings in a structured format (see §2.3).

Recommended audit order:

  1. Build/deploy entrypoints and hosting config (Docker, CI, static hosting, SSR server).
  2. Secrets exposure (env usage, .env*, hard-coded keys). (vitejs)
  3. XSS surface: templates, v-html / innerHTML, URL/style injection, DOM APIs. (Vue.js)
  4. Auth/session handling in the browser (token storage, credentialed requests, CSRF integration). (Vue.js)
  5. Routing/navigation (open redirects, “return_to/next”, unsafe external navigation). (Vue.js)
  6. Third-party scripts and content (CDN assets, analytics, widgets, iframes). (Vue.js)
  7. Security headers and browser hardening expectations (CSP, clickjacking). (Vue.js)
  8. SSR-specific concerns (state serialization, template boundaries) when applicable. (Vue.js)

2) Definitions and review guidance

2.1 Untrusted input (treat as attacker-controlled unless proven otherwise)

In a Vue app, untrusted input includes (non-exhaustive):

  • Anything from APIs: fetch, axios, GraphQL responses, webhooks, third-party SDKs.
  • Router-controlled data: route.params, route.query, route.hash, and anything derived from window.location.
  • User-controlled persisted content: DB-backed content displayed in the UI (comments, profiles, CMS content).
  • Browser-controlled storage: localStorage, sessionStorage, IndexedDB.
  • Cross-window messages: postMessage inputs.
  • Anything that can be influenced by an attacker through DOM clobbering or injected HTML (especially if Vue is mounted onto non-sterile DOM). (Vue.js)

2.2 State-changing action (frontend perspective)

An action is state-changing if it can:

  • Create/update/delete data via API calls.
  • Change authentication/session state (login, logout, refresh token).
  • Trigger privileged operations (payments, admin actions).
  • Cause side effects (sending emails, triggering webhooks, changing account settings).

2.3 Required audit finding format

For each issue found, output:

  • Rule ID:
  • Severity: Critical / High / Medium / Low
  • Location: file path + component/function + line(s)
  • Evidence: the exact code/config snippet
  • Impact: what could go wrong, who can exploit it
  • Fix: safe change (prefer minimal diff)
  • Mitigation: defense-in-depth if immediate fix is hard
  • False positive notes: what to verify if uncertain

3) Secure baseline: minimum production configuration (MUST in production)

This is the smallest “production baseline” that prevents common Vue/front-end misconfigurations.

  • MUST ship a production build (not a development build or dev server). (Vue.js)
  • MUST NOT ship secrets in frontend bundles; treat all client-exposed env variables as public. (vitejs)
  • MUST NOT render non-trusted templates or allow user-provided Vue templates (equivalent to arbitrary JS execution). (Vue.js)
  • SHOULD avoid raw HTML injection (v-html, innerHTML) unless content is trusted or strongly sandboxed. (Vue.js)
  • SHOULD deploy baseline security headers (especially CSP and clickjacking defenses) at the server/CDN layer. (OWASP Cheat Sheet Series)
  • SHOULD use safe auth patterns (prefer HttpOnly cookies for session tokens; coordinate with backend on CSRF). (Vue.js)

4) Rules (generation + audit)

Each rule contains: required practice, insecure patterns, detection hints, and remediation.

VUE-DEPLOY-001: Do not run dev/preview servers in production

Severity: High

Required:

  • MUST NOT deploy the Vite/Vue dev server (vite, npm run dev, HMR) as the production server.
  • MUST NOT use vite preview as a production server. (vitejs)
  • MUST build (vite build) and serve the built assets using a production-grade static server/CDN, or a production SSR server if you are doing SSR. (vitejs)

Insecure patterns:

  • Docker/Procfile/systemd running vite, npm run dev, or vite preview as the production entrypoint.
  • Publicly exposed HMR endpoints.

Detection hints:

  • Search: vite, npm run dev, pnpm dev, yarn dev, vite preview, vue-cli-service serve.
  • Check Docker CMD, ENTRYPOINT, CI deploy scripts, platform config.

Fix:

  • Build artifacts with vite build.
  • Serve dist/ with hardened hosting (CDN/static server) or integrate into your backend server as static assets.

Notes:

  • Using dev/preview servers locally is fine; only flag if it is the production entrypoint.

VUE-DEPLOY-002: Use Vue production builds and keep devtools off in production

Severity: Medium (High if production devtools/debug hooks are enabled)

Required:

  • If loading Vue from CDN/self-host without a bundler, MUST use the .prod.js builds in production. (Vue.js)
  • SHOULD ensure production bundles do not enable Vue devtools in production builds, and SHOULD not intentionally enable production devtools flags. (Vue.js)

Insecure patterns:

  • Production includes development build artifacts.
  • Explicitly enabling production devtools/diagnostic hooks.

Detection hints:

  • Search HTML for vue.global.js / non-.prod.js variants when using CDN builds.
  • Search build config for Vue feature flags like __VUE_PROD_DEVTOOLS__. (Vue.js)

Fix:

  • Switch to production build artifacts and ensure compile-time flags are configured for production.

VUE-SECRETS-001: Never ship secrets in frontend code or env variables

Severity: High (Critical if real credentials are exposed)

Required:

  • MUST treat all frontend code and configuration as public.

  • MUST NOT embed secrets in:

    • source code
    • .env files committed to repo
    • import.meta.env.* variables included in the bundle
  • MUST assume any env var that ends up in the client bundle is attacker-readable. (vitejs)

Insecure patterns:

  • VITE_API_KEY=... containing a true secret (not just a public identifier).
  • Hard-coded API keys, private tokens, service credentials, signing keys in JS/TS.

Detection hints:

  • Search: VITE_, import.meta.env, .env, .env.production, .env.*.local.
  • Grep for API_KEY, SECRET, TOKEN, PRIVATE_KEY, BEGIN, sk-, AKIA, etc.

Fix:

  • Move secrets to backend/edge functions.
  • Use backend-minted short-lived tokens for the browser when needed.

Notes:

  • Vite specifically warns that .env.*.local should be gitignored and that VITE_* vars end up in the client bundle, so they must not contain sensitive info. (vitejs)

VUE-SECRETS-002: Do not broaden Vite env exposure

Severity: High

Required:

  • MUST NOT configure Vite to expose all environment variables to the client.
  • SHOULD keep envPrefix strict and explicit.

Insecure patterns:

  • Setting envPrefix to overly broad values (or '') to “make env vars work”.
  • Custom scripts that inject server secrets into global variables in HTML at build time.

Detection hints:

  • Check vite.config.* for envPrefix.
  • Look for define: { 'process.env': ... } or manual injection into window.__CONFIG__.

Fix:

  • Keep secrets server-side.
  • Only expose non-sensitive values intentionally designed to be public.

Notes:

  • Vite’s docs explain that only prefixed variables are exposed and that exposed variables land in the client bundle. (vitejs)

VUE-XSS-001: Prefer Vue’s default escaping; avoid raw HTML injection

Severity: High

Required:

  • MUST rely on Vue’s automatic escaping for text interpolation and attribute binding where possible. (Vue.js)

  • MUST NOT render user-provided HTML via:

    • v-html
    • innerHTML in render functions / JSX
    • direct DOM APIs (element.innerHTML, insertAdjacentHTML) unless the HTML is trusted or robustly sanitized and the risk is explicitly accepted. (Vue.js)

Insecure patterns:

  • <div v-html="userProvidedHtml"></div>
  • h('div', { innerHTML: userProvidedHtml })
  • <div innerHTML={userProvidedHtml}></div>
  • el.innerHTML = untrusted

Detection hints:

  • Search: v-html, innerHTML, insertAdjacentHTML, DOMParser, document.write.

Fix:

  • Render untrusted content as text (interpolation).
  • If HTML rendering is required (e.g., Markdown), sanitize with a well-maintained HTML sanitizer and apply defense-in-depth (CSP, Trusted Types). (Vue.js)

Notes:

  • Vue’s docs explicitly warn that user-provided HTML is never “100% safe” unless sandboxed or strictly self-only exposure. (Vue.js)

VUE-XSS-002: Never use non-trusted templates (client-side template/code injection)

Severity: Critical

Required:

  • MUST NOT use non-trusted content as a Vue component template.
  • MUST treat “user can write a Vue template” as “user can execute arbitrary JavaScript in your app”, and potentially in SSR contexts too. (Vue.js)
  • SHOULD prefer the runtime-only build (templates compiled at build time) and avoid shipping the runtime compiler unless you have a vetted need.

Insecure patterns:

  • createApp({ template: '<div>' + userProvidedString + '</div>' }).mount(...)
  • Storing templates in DB and compiling/rendering them in the browser.
  • Admin/CMS features that allow entering Vue template syntax.

Detection hints:

  • Search: template: where the value is not a static string.
  • Search: @vue/compiler-dom, compile(, “runtime compiler” build selection, dynamic SFC compilation.
  • Search for “template editor”, “custom template”, “theme HTML” features.

Fix:

  • Treat templates as code: keep them developer-controlled.
  • If end-user customization is required, use a safe format (restricted Markdown subset) rendered via a sanitizer, or isolate in a sandboxed iframe.

VUE-XSS-003: Do not mount Vue onto DOM that may contain user-provided server-rendered HTML

Severity: Medium

Required:

  • MUST NOT mount Vue on nodes that may contain server-rendered and user-provided content (because attacker-controlled HTML that is “safe as HTML” may become unsafe as a Vue template). (Vue.js)
  • SHOULD mount Vue into a “sterile” root element and render the app’s DOM from Vue-controlled templates/components.

Insecure patterns:

  • Server renders user content into #app, then Vue mounts on #app and compiles/interprets that DOM as a template.
  • “Sprinkling Vue” on large server-rendered pages that include user-generated content.

Detection hints:

  • Check server templates (e.g., Rails/Django/Express templates) for user HTML inserted inside the Vue mount root.
  • Look for mount('#app') where #app includes server-rendered UGC.

Fix:

  • Move user-rendered HTML outside the Vue mount root, or render it in a safe way (text/sanitized HTML) from Vue components.

VUE-XSS-004: Prevent URL injection in bindings and navigations

Severity: High

Required:

  • MUST validate/sanitize any user-influenced URL before binding to navigation sinks (href, src, action, window.location, window.open, router navigation to external).
  • MUST specifically prevent javascript: URL execution in bindings like <a :href="userProvidedUrl">. (Vue.js)
  • SHOULD validate protocol and destination (allowlist https: and expected hosts; allow mailto:/tel: only if intended).

Insecure patterns:

  • <iframe :src="userProvidedUrl">
  • window.location = route.query.next
  • window.open(userProvidedUrl)

Detection hints:

  • Search: :href=, :src=, window.location, location.href, window.open, router.push( with untrusted input.
  • Look for next, return_to, redirect query params.

Fix:

  • Prefer internal navigation via route names/paths you control.
  • For external URLs: parse with new URL(...), allowlist protocol/host, reject javascript: and other dangerous schemes.
  • Sanitize and validate on the backend before storing user URLs (Vue docs explicitly recommend backend sanitization). (Vue.js)

VUE-XSS-005: Prevent style/CSS injection and UI redress

Severity: Low

Required:

  • MUST NOT bind attacker-controlled CSS strings broadly (e.g., :style="userProvidedStyles").
  • SHOULD use Vue’s style object syntax and only allow safe, specific properties if user customization is needed. (Vue.js)
  • SHOULD isolate “user can control layout/CSS” features inside sandboxed iframes.

Insecure patterns:

  • :style="userProvidedStyles" where styles are attacker-controlled.
  • Rendering user-provided <style> content (even if Vue blocks some patterns, don’t try to work around it).

Detection hints:

  • Search: :style=" bound to non-constant variables that originate from API/user content.
  • Search for “custom CSS”, “theme editor”, “profile CSS”.

Fix:

  • Allowlist properties and values; avoid raw style strings.
  • Use sandboxed iframes for rich user customization.

VUE-XSS-006: Never bind user-provided JavaScript into event handler attributes

Severity: Critical

Required:

  • MUST NOT bind attacker-provided strings into event handler attributes (e.g., onclick, onfocus, etc.).
  • MUST treat “user-provided JS” as unsafe unless sandboxed and self-only exposure is guaranteed. (Vue.js)

Insecure patterns:

  • <div :onclick="userProvidedString">
  • <a :onmouseenter="userProvidedString">

Detection hints:

  • Search: :on followed by event attribute names (:onclick, :onload, etc.).
  • Search for setAttribute('on patterns.

Fix:

  • Use real event listeners with developer-controlled handlers.
  • If you truly need user scripting, isolate it (sandboxed iframe + strict boundaries).

VUE-ROUTER-001: Do not treat client-side route guards as authorization

Severity: High

Required:

  • MUST NOT rely on Vue Router guards, UI hiding, or client-side checks to enforce authorization.
  • MUST enforce authorization on the backend for every privileged action and sensitive data response. (OWASP Cheat Sheet Series)

Insecure patterns:

  • “Admin route is protected because beforeEach checks user.isAdmin.”
  • Sensitive API endpoints that assume “the frontend won’t call this unless allowed.”

Detection hints:

  • Search router.beforeEach for role-based gating and see if the backend is also enforcing.
  • Look for “security by route meta” patterns (meta.requiresAdmin) with no server corroboration.

Fix:

  • Keep route guards as UX only (reduce accidental access), but enforce real checks server-side.

VUE-ROUTER-002: Prevent open redirects and unsafe “return_to/next” handling

Severity: Low

Required:

  • MUST validate redirect destinations derived from untrusted input (next, return_to, redirect).
  • SHOULD allow only same-site relative paths or an explicit allowlist of destinations.
  • MUST NOT allow non http / https protos (such as javascript:)

Insecure patterns:

  • router.push(route.query.next as string)
  • window.location.href = route.query.redirect

Detection hints:

  • Search for route.query.next, route.query.redirect, return_to, continue, callback.
  • Trace the value into router/window navigation sinks.

Fix:

  • Allow only relative paths starting with / (and reject //host, javascript:, etc.).
  • Prefer redirecting to named routes you control.

Notes:

  • Even Vue’s docs note that sanitized URLs still may not guarantee safe destinations. (Vue.js)

VUE-AUTH-001: Token storage must assume XSS is possible

Severity: Low

Required:

  • MUST assume any token accessible to JavaScript can be stolen via XSS.
  • SHOULD prefer HttpOnly cookies (set by the backend) for session tokens, combined with CSRF protections where relevant. (Vue.js)
  • SHOULD avoid storing long-lived tokens (especially refresh tokens) in localStorage/sessionStorage.

Insecure patterns:

  • localStorage.setItem('token', ...) for long-lived bearer tokens.
  • Storing refresh tokens in JS-accessible storage.

Detection hints:

  • Search: localStorage, sessionStorage, indexedDB, persist, pinia-plugin-persistedstate.
  • Identify whether stored values are auth/session material.

Fix:

  • Prefer backend-managed sessions via HttpOnly cookies.
  • If bearer tokens are unavoidable, keep them short-lived, stored in memory, and rotate frequently; combine with strong XSS mitigations (CSP, Trusted Types, strict sanitization). (OWASP Cheat Sheet Series)

VUE-CSRF-001: Coordinate with the backend for CSRF when using cookies

Severity: High (for cookie-authenticated state-changing requests)

NOTE: If the application is not using cookie based authentication (for example if it passes an Authorization header), then CSRF is not a concern

Required:

  • If API requests include cookies (credentials: 'include' / withCredentials: true) and cookies authenticate the user, MUST include CSRF protections coordinated with the backend (token/header patterns, Origin checks, SameSite cookies as defense-in-depth). (Vue.js)
  • MUST NOT “solve CORS/CSRF errors” by disabling protections on the backend or using mode: 'no-cors' on the frontend.

Insecure patterns:

  • fetch(url, { credentials: 'include', method: 'POST', body: ... }) with no CSRF token/header usage anywhere.
  • Enabling cross-origin credentialed requests without strict origin allowlists (backend-side).

Detection hints:

  • Search: credentials: 'include', withCredentials, xsrf, csrf, X-CSRF-Token, X-XSRF-TOKEN.
  • Look at API wrapper modules for headers and cookie settings.

Fix:

  • Implement backend-issued CSRF tokens and require them on state-changing requests.
  • Keep cookies SameSite=Lax/Strict where compatible and verify Origin/Referer where appropriate (backend-driven). (OWASP Cheat Sheet Series)

Notes:

  • Vue’s docs explicitly say CSRF is primarily backend-addressed but recommends coordinating on CSRF token submission. (Vue.js)

VUE-HTTP-001: Do not put secrets in URLs; avoid leaking sensitive data in navigation/logs

Severity: Medium

Required:

  • MUST NOT place tokens/secrets in query strings or fragments (they leak via logs, referrers, browser history).
  • SHOULD avoid logging sensitive values to console in production.

Insecure patterns:

  • /?token=..., /#access_token=... used beyond short-lived OAuth handoff.
  • console.log(userSession) that includes tokens/PII.

Detection hints:

  • Search for token= in router parsing, auth callback handlers, and analytics logs.
  • Search for console.log( around auth code.

Fix:

  • Use Authorization headers or HttpOnly cookies.
  • Scrub logs; gate debug logs behind dev-only checks.

VUE-HEADERS-001: Require security headers at the deployment layer

Severity: Medium

Required:

  • SHOULD deploy a CSP (Content-Security-Policy) suitable for your Vue app.
  • SHOULD deploy clickjacking defenses (CSP frame-ancestors and/or X-Frame-Options) unless intentional embedding is required.
  • SHOULD deploy X-Content-Type-Options: nosniff, plus other headers as appropriate (Referrer-Policy, Permissions-Policy). (OWASP Cheat Sheet Series)

Insecure patterns:

  • No evidence of headers in server/CDN config for an app with UGC or rich HTML rendering.
  • CSP includes unsafe-inline/unsafe-eval without strong justification.

Detection hints:

  • Look for hosting config: nginx, Netlify/Vercel headers config, CloudFront/Cloudflare rules.
  • If absent in repo, flag as “verify at edge”.

Fix:

  • Set headers at the edge or in the server. Start with a conservative CSP and tighten.

VUE-CSP-001: Use Trusted Types and DOM XSS hardening when feasible

Severity: Low

Required:

  • For apps with significant DOM injection surface (rich text, plugins, v-html), SHOULD consider enabling Trusted Types to reduce DOM XSS risk. (web.dev)
  • SHOULD treat Trusted Types as defense-in-depth, not a replacement for sanitization.

Insecure patterns:

  • Frequent use of innerHTML/v-html without sanitization or CSP hardening.

Detection hints:

  • Search: v-html, innerHTML, insertAdjacentHTML.
  • Check CSP for require-trusted-types-for 'script' usage (if headers are in repo).

Fix:

  • Reduce/centralize HTML injection, sanitize inputs, and add Trusted Types policies where appropriate.

VUE-THIRDPARTY-001: Avoid dynamic third-party script injection; prefer static, vetted loading

Severity: Low

Required:

  • MUST NOT inject <script src="..."> where the URL is user-controlled.
  • SHOULD treat third-party widgets/analytics as supply-chain risk; load only from vetted, pinned sources.

Insecure patterns:

  • const s=document.createElement('script'); s.src = userProvidedUrl; ...
  • “Plugin marketplace” that loads arbitrary remote scripts.

Detection hints:

  • Search: createElement('script'), .src =, appendChild(script).
  • Search for “loadExternalScript”, “injectScript”, “cdnUrl”.

Fix:

  • Bundle dependencies, or allowlist strict origins and enforce integrity (see SRI rule).
  • Consider sandboxed iframes for untrusted third-party UI.

VUE-SRI-001: Use Subresource Integrity for CDN-hosted scripts/styles

Severity: Low

Required:

  • If loading scripts/styles from a CDN, SHOULD use Subresource Integrity (integrity attribute) with appropriate crossorigin configuration. (MDN Web Docs)
  • SHOULD prefer self-hosting or bundling over runtime CDN dependencies for security-critical code.

Insecure patterns:

  • <script src="https://cdn.example/..."> with no integrity.
  • Remote script URLs that can change content without version pinning.

Detection hints:

  • Search index.html and server templates for https:// script/style tags.
  • Check for integrity=.

Fix:

  • Add SRI hashes (and pin versions), or bundle assets with your build.

VUE-SUPPLY-001: Dependency and patch hygiene is mandatory

Severity: Low

Required:

  • SHOULD keep Vue and official companion libraries updated; Vue explicitly recommends using latest versions to remain as secure as possible. (Vue.js)
  • MUST respond to security advisories promptly.
  • SHOULD pin dependencies and keep lockfiles committed (to reduce drift in production artifacts).

Insecure patterns:

  • Outdated major versions with known CVEs.
  • No lockfile in repo; wide semver ranges for critical deps.
  • Ignoring advisories for template/rendering/compiler packages.

Detection hints:

  • Inspect package.json, lockfiles, CI install commands.
  • Search for npm audit disabled, “ignore vulnerabilities” scripts.

Fix:

  • Upgrade dependencies and add regression tests around the impacted behavior.
  • Add dependency scanning in CI.

VUE-SSR-001: SSR adds additional trust boundaries; treat state injection as XSS-sensitive

Severity: Medium

Required:

  • When using SSR, MUST treat anything injected into the HTML document (initial state, serialized data, inline scripts) as XSS-sensitive.
  • MUST keep the “trusted templates only” rule even stricter, because unsafe templates can lead to server-side execution during rendering. (Vue.js)
  • SHOULD follow Vue SSR documentation and best practices for SSR security. (Vue.js)

Insecure patterns:

  • Concatenating untrusted strings into SSR templates.
  • Injecting JSON into <script> blocks without robust escaping/serialization controls.

Detection hints:

  • Search server code for __INITIAL_STATE__, window.__*STATE__, template concatenation, and SSR render pipelines.
  • Trace untrusted data into those sinks.

Fix:

  • Use safe serialization patterns recommended by your SSR stack.
  • Avoid rendering untrusted HTML; sanitize or isolate.

5) Practical scanning heuristics (how to “hunt”)

When actively scanning, use these high-signal patterns:

  • Dev/preview servers in production:

    • npm run dev, vite, vite preview, vue-cli-service serve (vitejs)
  • Secrets exposure:

    • .env, .env.production, .env.*.local, VITE_, import.meta.env, hard-coded API_KEY / SECRET (vitejs)
  • XSS sinks:

    • v-html, innerHTML, insertAdjacentHTML, DOMParser, document.write (Vue.js)
  • Client-side template injection:

    • template: concatenation, compile(, runtime compiler usage, mounting on non-sterile DOM (Vue.js)
  • URL injection / open redirects:

    • :href="..." / :src="..." from user data
    • javascript: occurrences
    • route.query.next / redirect / return_to flowing into router.push or window.location (Vue.js)
  • Style injection:

    • :style="userProvidedStyles" or user-driven theme CSS (Vue.js)
  • Token storage:

    • localStorage.setItem('token'...), persisted auth stores, refresh tokens in JS-accessible storage
  • CSRF integration red flags:

    • credentials: 'include' / withCredentials: true without any CSRF header/token handling (Vue.js)
  • Third-party scripts:

    • dynamic script injection (createElement('script')), CDN scripts without SRI (MDN Web Docs)
  • External links security:

    • target="_blank" without rel="noopener"/noreferrer (still recommended for legacy and explicitness) (MDN Web Docs)

Always try to confirm:

  • data origin (untrusted vs trusted)
  • sink type (HTML/DOM insertion, template compilation, URL navigation, style injection, script injection)
  • protective controls present (sanitization, allowlists, CSP/Trusted Types, backend validation)

6) Sources (accessed 2026-01-27)

Primary Vue documentation:

  • Vue Docs: Security — https://vuejs.org/guide/best-practices/security (Vue.js)
  • Vue Docs: Template Syntax (security warning about in-DOM templates) — https://vuejs.org/guide/essentials/template-syntax (Vue.js)
  • Vue Docs: Production Deployment — https://vuejs.org/guide/best-practices/production-deployment (Vue.js)
  • Vue Docs: Feature Flags — https://link.vuejs.org/feature-flags (Vue.js)

Vite documentation (common Vue tooling):

  • Vite Docs: Env Variables and Modes (VITE_* exposure + security notes) — https://vite.dev/guide/env-and-mode (vitejs)
  • Vite Docs: CLI (vite preview not designed for production) — https://vite.dev/guide/cli (vitejs)
  • Vite Docs: Server Options (server.host can listen on public addresses) — https://vite.dev/config/server-options (vitejs)

OWASP and web platform hardening references:

  • OWASP Cheat Sheet Series: XSS Prevention — https://cheatsheetseries.owasp.org/cheatsheets/Cross_Site_Scripting_Prevention_Cheat_Sheet.html (Vue.js)
  • OWASP Cheat Sheet Series: CSRF Prevention — https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.html (OWASP Cheat Sheet Series)
  • OWASP Cheat Sheet Series: Authorization — https://cheatsheetseries.owasp.org/cheatsheets/Authorization_Cheat_Sheet.html (OWASP Cheat Sheet Series)
  • OWASP Cheat Sheet Series: HTTP Headers — https://cheatsheetseries.owasp.org/cheatsheets/HTTP_Headers_Cheat_Sheet.html (OWASP Cheat Sheet Series)
  • HTML5 Security Cheat Sheet (referenced by Vue) — https://html5sec.org/ (Vue.js)

Browser/platform references:

  • MDN: rel="noopener"https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Attributes/rel/noopener (MDN Web Docs)
  • MDN: Subresource Integrity — https://developer.mozilla.org/en-US/docs/Web/Security/Subresource_Integrity (MDN Web Docs)
  • web.dev: Trusted Types — https://web.dev/trusted-types/ (web.dev)

Reference: Python Django Web Server Security

Django (Python) Web Security Spec (Django 6.0.x, Python 3.x)

This document is designed as a security spec that supports:

  1. Secure-by-default code generation for new Django code.
  2. Security review / vulnerability hunting in existing Django code (passive “notice issues while working” and active “scan the repo and report findings”).

It is intentionally written as a set of normative requirements (“MUST/SHOULD/MAY”) plus audit rules (what bad patterns look like, how to detect them, and how to fix/mitigate them).


0) Safety, boundaries, and anti-abuse constraints (MUST FOLLOW)

  • MUST NOT request, output, log, or commit secrets (API keys, passwords, private keys, session cookies, SECRET_KEY, SECRET_KEY_FALLBACKS, database passwords).
  • MUST NOT “fix” security by disabling protections (e.g., removing CsrfViewMiddleware, sprinkling @csrf_exempt, loosening ALLOWED_HOSTS to ['*'], disabling SecurityMiddleware, disabling template auto-escaping, disabling permission checks).
  • MUST provide evidence-based findings during audits: cite file paths, code snippets, and concrete configuration values that justify the claim.
  • MUST treat uncertainty honestly: if a protection might exist in infrastructure (reverse proxy, WAF, CDN, ingress controller), report it as “not visible in app code; verify at runtime / edge config”.
  • MUST keep fixes compatible with Django’s intended security model: prefer Django’s built-ins (middleware, auth, forms, ORM) over custom security logic whenever possible. Django’s deployment checklist and system checks are part of the intended model. (Django Project)

1) Operating modes

1.1 Generation mode (default)

When asked to write new Django code or modify existing code:

  • MUST follow every MUST requirement in this spec.
  • SHOULD follow every SHOULD requirement unless the user explicitly says otherwise.
  • MUST prefer safe-by-default Django APIs and proven libraries over custom security code.
  • MUST avoid introducing new risky sinks (dynamic template rendering from untrusted strings, unsafe redirects, unsafe file serving, shell execution, raw SQL string formatting, SSRF-capable URL fetchers from untrusted input).

1.2 Passive review mode (always on while editing)

While working anywhere in a Django repo (even if the user did not ask for a security scan):

  • MUST “notice” violations of this spec in touched/nearby code.
  • SHOULD mention issues as they come up, with a brief explanation + safe fix.

1.3 Active audit mode (explicit scan request)

When the user asks to “scan”, “audit”, or “hunt for vulns”:

  • MUST systematically search the codebase for violations of this spec.
  • MUST output findings in a structured format (see §2.3).

Recommended audit order:

  1. Deployment entrypoints (ASGI/WSGI), Dockerfiles, Procfiles, systemd units, platform manifests.
  2. settings.py and environment-specific settings modules.
  3. Middleware ordering and enabled protections.
  4. Authn/authz (login, session management, permissions, admin).
  5. CSRF protections and state-changing endpoints.
  6. Templates and XSS.
  7. File handling (uploads/downloads/static/media) and path traversal.
  8. Injection classes (SQL, command execution, unsafe deserialization).
  9. Outbound requests (SSRF).
  10. Redirect handling (open redirects) + CORS + security headers (CSP, HSTS, etc.).
  11. Dependency/pinning and patch posture.

2) Definitions and review guidance

2.1 Untrusted input (treat as attacker-controlled unless proven otherwise)

Examples include:

  • request.GET, request.POST, request.FILES
  • request.body, JSON bodies (e.g., json.loads(request.body)), DRF request.data
  • URL path parameters (e.g., <int:id>, <slug:...>)
  • request.headers / request.META (including HTTP_HOST, HTTP_ORIGIN, HTTP_REFERER, HTTP_X_FORWARDED_*)
  • request.COOKIES
  • Any data from external systems (webhooks, third-party APIs, message queues)
  • Any persisted content that originated from users (DB rows, cached content, file uploads)

Django explicitly emphasizes “never trust user-controlled data” and recommends using forms/validation. (Django Project)

2.2 State-changing request

A request is state-changing if it can create/update/delete data, change auth/session state, trigger side effects (purchase, email send, webhook send), or initiate privileged actions.

2.3 Required audit finding format

For each issue found, output:

  • Rule ID:
  • Severity: Critical / High / Medium / Low
  • Location: file path + function/class/view name + line(s)
  • Evidence: the exact code/config snippet
  • Impact: what could go wrong, who can exploit it
  • Fix: safe change (prefer minimal diff)
  • Mitigation: defense-in-depth if immediate fix is hard
  • False positive notes: what to verify if uncertain

3) Secure baseline: minimum production configuration (MUST in production)

This is the smallest “production baseline” that prevents common Django misconfigurations. Django provides a “Deployment checklist” and recommends running manage.py check --deploy against production settings. (Django Project)

3.1 Settings management pattern (SHOULD)

  • SHOULD use environment-based configuration (or a secret manager) so production settings are not hard-coded.
  • MUST treat sensitive settings as confidential (e.g., SECRET_KEY, DB passwords) and keep them out of source control. Django’s checklist explicitly recommends loading SECRET_KEY from env or a file rather than hardcoding. (Django Project)
  • SHOULD separate dev vs prod settings modules, with safe defaults for production (fail closed if critical settings are missing). (Django Project)

3.2 Minimum baseline targets (production)

  • MUST NOT use manage.py runserver as the production entrypoint; use a production-ready WSGI or ASGI server. (Django Project)
  • MUST set DEBUG = False in production. (Django Project)
  • MUST set a strong, secret SECRET_KEY and keep it secret; MAY use SECRET_KEY_FALLBACKS for safe rotation. (Django Project)
  • MUST set ALLOWED_HOSTS to expected hosts (no wildcard unless you do your own host validation). (Django Project)
  • MUST enforce HTTPS for authenticated areas (ideally site-wide for any login-capable app) and set CSRF_COOKIE_SECURE=True and SESSION_COOKIE_SECURE=True when HTTPS is used. (Django Project)
  • SHOULD enable key SecurityMiddleware headers/settings: HSTS, Referrer-Policy, COOP, nosniff, SSL redirect (with correct proxy configuration). (Django Project)
  • MUST treat user uploads as untrusted; ensure your web server never interprets them as executable content; keep MEDIA_ROOT separate from STATIC_ROOT. (Django Project)

4) Rules (generation + audit)

Each rule contains: required practice, insecure patterns, detection hints, and remediation.

DJANGO-DEPLOY-001: Do not use Django’s development server in production

Severity: High (if production)

Required:

  • MUST NOT deploy manage.py runserver as the production server.
  • MUST run behind a production-grade WSGI or ASGI server. (Django Project)

Insecure patterns:

  • Production docs/scripts using python manage.py runserver 0.0.0.0:8000.
  • Docker CMD/entrypoint uses runserver.
  • Kubernetes/Procfile/systemd units invoking runserver.

Detection hints:

  • Search for manage.py runserver, runserver 0.0.0.0, --insecure.
  • Check Docker CMD/ENTRYPOINT, Procfile, systemd unit files, Helm charts.

Fix:

  • Use a production server (WSGI/ASGI) as recommended in Django’s deployment checklist. (Django Project)

Note:

  • runserver is fine for local development. Only flag if it’s used as the production entrypoint.

DJANGO-DEPLOY-002: DEBUG MUST be disabled in production

Severity: High

Required:

  • MUST set DEBUG = False in production.
  • MUST treat any mechanism that exposes debug pages/tracebacks to untrusted users as a critical information disclosure risk. Django’s checklist explicitly warns DEBUG=True leaks source excerpts, local variables, settings, and more. (Django Project)

Insecure patterns:

  • DEBUG = True in production settings.
  • Environment defaults to DEBUG=True unless explicitly overridden.

Detection hints:

  • Search DEBUG = True, DEBUG=os.environ.get(..., True), DJANGO_DEBUG, .env files.
  • Look for “production” settings modules that import from dev defaults.

Fix:

  • Set DEBUG=False in prod settings; use explicit environment config.
  • Ensure error reporting is via safe logging/monitoring, not debug pages. (Django Project)

DJANGO-CONFIG-001: SECRET_KEY must be strong, secret, and rotated safely

Severity: High (Critical if missing in production with signing/sessions)

Required:

  • MUST set a large random SECRET_KEY in production and keep it secret. (Django Project)
  • MUST NOT commit it to source control or print/log it. (Django Project)
  • SHOULD load it from env or a file/secret store (not hard-coded). (Django Project)
  • MAY rotate keys using SECRET_KEY_FALLBACKS to avoid instantly invalidating all signed data; MUST remove old keys from fallbacks in a timely manner. (Django Project)

Insecure patterns:

  • Hard-coded SECRET_KEY = "..." in repo for production.
  • SECRET_KEY reused across environments.
  • SECRET_KEY_FALLBACKS contains long-expired keys indefinitely.

Detection hints:

  • Search for SECRET_KEY =, SECRET_KEY_FALLBACKS, .env committed files, print(settings.SECRET_KEY).

Fix:

  • Load from secret manager / environment variable.

  • If rotating:

    • Set new SECRET_KEY
    • Keep old key(s) temporarily in SECRET_KEY_FALLBACKS
    • Remove old key(s) after the rotation window. (Django Project)

DJANGO-HOST-001: Host header must be validated (ALLOWED_HOSTS must be strict)

Severity: Medium

Required:

  • MUST set ALLOWED_HOSTS in production to your expected domains/hosts. (Django Project)
  • MUST NOT set ALLOWED_HOSTS = ['*'] in production unless you also implement your own robust Host validation (Django warns that wildcards require your own validation to avoid CSRF-class attacks). (Django Project)
  • SHOULD configure the fronting web server to reject unknown hosts early (defense-in-depth). (Django Project)

Insecure patterns:

  • ALLOWED_HOSTS = ['*'] (or env expands to *) in production.
  • ALLOWED_HOSTS = [] with DEBUG=False (site won’t run, or misconfigured deployments attempt workarounds).

Detection hints:

  • Search ALLOWED_HOSTS.
  • Check platform environment settings that override ALLOWED_HOSTS.

Fix:

  • Set ALLOWED_HOSTS = ['example.com', 'www.example.com', ...] for prod.
  • Keep dev hosts separate.

Notes:

  • Django uses the Host header for URL construction; fake Host values can lead to CSRF, cache poisoning, and poisoned email links (Django security docs call this out). (Django Project)

Severity: High (Critical for auth-enabled apps)

NOTE: Only enforce this if TLS is enabled, as it will break non-TLS applications

If using TLS:

  • MUST set:

  • SHOULD consider enabling:

    • SECURE_SSL_REDIRECT = True (with correct proxy config) (Django Project)
    • HSTS via SECURE_HSTS_SECONDS (+ includeSubDomains/preload as appropriate). (Django Project)

Insecure patterns:

  • Login pages over HTTP, or mixed HTTP/HTTPS with the same session cookie.
  • CSRF_COOKIE_SECURE=False or SESSION_COOKIE_SECURE=False in production HTTPS.
  • HSTS enabled incorrectly (can break site for the duration).

Detection hints:

  • Inspect settings.py for CSRF_COOKIE_SECURE, SESSION_COOKIE_SECURE, SECURE_SSL_REDIRECT, SECURE_HSTS_SECONDS.
  • Inspect proxy/ingress config for HTTP->HTTPS redirect behavior.

Fix:

  • Enable HTTPS redirect and secure cookies.
  • Add HSTS carefully (start with low value, validate, then increase). Django warns misconfig can break your site for the HSTS duration. (Django Project)

DJANGO-PROXY-001: Reverse proxy trust must be configured correctly (SECURE_PROXY_SSL_HEADER)

Severity: Medium (when behind a TLS proxy)

Required:

  • If behind a reverse proxy that terminates TLS, MUST configure Django so request.is_secure() reflects the external scheme, otherwise CSRF and other logic can break. Django documents using SECURE_PROXY_SSL_HEADER for this. (Django Project)
  • MUST only set SECURE_PROXY_SSL_HEADER if you control the proxy (or have guarantees) and it strips inbound spoofed headers. Django explicitly warns misconfig can compromise security and lists required conditions. (Django Project)

Insecure patterns:

  • SECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTO", "https") in an environment where the proxy does not strip user-supplied X-Forwarded-Proto.
  • Infinite redirect loops after setting SECURE_SSL_REDIRECT=True (often indicates proxy HTTPS detection is wrong). (Django Project)

Detection hints:

  • Search SECURE_PROXY_SSL_HEADER, SECURE_SSL_REDIRECT.
  • Inspect ingress/proxy behavior for stripping forwarded headers.

Fix:

  • Set SECURE_PROXY_SSL_HEADER only if the proxy strips and sets the header correctly (per Django’s documented prerequisites). (Django Project)

DJANGO-SESS-001: Session cookies must use secure attributes in production

Severity: Medium (Only if TLS enabled)

Required (production, HTTPS):

  • MUST set SESSION_COOKIE_SECURE=True (only transmit over HTTPS). (Django Project)
  • MUST keep SESSION_COOKIE_HTTPONLY=True (Django default is True). (Django Project)
  • SHOULD keep SESSION_COOKIE_SAMESITE='Lax' (Django default is Lax) unless a justified cross-site flow requires None. (Django Project)
  • SHOULD avoid setting SESSION_COOKIE_DOMAIN unless you truly need cross-subdomain cookies (subdomain-wide cookies expand attack surface).

Insecure patterns:

  • SESSION_COOKIE_SECURE=False in production HTTPS.

IMPORTANT NOTE: Only set Secure in production environment when TLS is configured. When running in a local dev environment over HTTP, do not set Secure property on cookies. You should do this conditionally based on if the app is running in production mode. You should also include a property like SESSION_COOKIE_SECURE which can be used to disable Secure cookies when testing over HTTP.

  • SESSION_COOKIE_HTTPONLY=False.
  • SESSION_COOKIE_SAMESITE=None combined with cookie-authenticated state-changing endpoints (higher CSRF risk).

Detection hints:

  • Search for SESSION_COOKIE_ settings, response.set_cookie(..., httponly=..., secure=..., samesite=...).

Fix:

  • Set the above explicitly in production settings.
  • Validate compatibility with your auth flows. (Django Project)

Severity: Medium

Required:

  • SHOULD set CSRF_COOKIE_SECURE=True when using HTTPS/TLS. (Django Project)
  • SHOULD keep CSRF_COOKIE_SAMESITE='Lax' unless you have a cross-site requirement. Django default is Lax. (Django Project)
  • MAY set CSRF_COOKIE_HTTPONLY=True (default is False) if your frontend does not need to read the CSRF cookie. If you enable it, your JS must read the CSRF token from the DOM instead (Django documents this). (Django Project)

Insecure patterns:

  • CSRF_COOKIE_SECURE=False in production HTTPS/TLS.
  • Setting CSRF_COOKIE_HTTPONLY=True but still relying on “read csrftoken cookie in JS” patterns (breaks CSRF for AJAX).
  • CSRF_COOKIE_SAMESITE=None without a clear reason.

Detection hints:

  • Search for CSRF_COOKIE_ settings.
  • Search JS for document.cookie usage to fetch csrftoken.

Fix:

  • Align cookie settings with your CSRF token acquisition method (cookie vs DOM) as Django describes. (Django Project)

Severity: High

Required:

  • MUST keep django.middleware.csrf.CsrfViewMiddleware enabled (it is activated by default). (Django Project)
  • MUST include {% csrf_token %} in internal POST forms; MUST NOT include it in forms that POST to external URLs (Django warns this leaks the token). (Django Project)
  • MUST protect all state-changing endpoints (POST/PUT/PATCH/DELETE) that rely on cookies for authentication.
  • For AJAX/SPA calls, MUST send the CSRF token via the X-CSRFToken header (or configured header name) as documented. (Django Project)
  • MUST be very careful with @csrf_exempt and use it only when absolutely necessary; if used, MUST replace CSRF with an appropriate alternative control (e.g., request signing for webhooks). Django explicitly warns about csrf_exempt. (Django Project)

Insecure patterns:

  • Missing CsrfViewMiddleware in MIDDLEWARE.
  • @csrf_exempt on general-purpose authenticated views.
  • POST/PUT/PATCH/DELETE endpoints with session auth and no CSRF tokens.
  • Using GET for state-changing actions (amplifies CSRF risk).

Detection hints:

  • Inspect settings.py MIDDLEWARE for CsrfViewMiddleware and its order (Django notes it should come before middleware that assumes CSRF is handled). (Django Project)
  • Search for csrf_exempt, csrf_protect, ensure_csrf_cookie.
  • Enumerate URL patterns for non-GET methods; confirm CSRF coverage.

Fix:

  • Re-enable CsrfViewMiddleware, add CSRF tokens to forms, and add AJAX header handling.
  • For caching decorators: if you cache a view that needs CSRF tokens, apply @csrf_protect as Django documents to avoid caching a response without CSRF cookie/Vary headers. (Django Project)

Notes:

  • When deployed with HTTPS, Django’s CSRF middleware also checks the Referer header for same-origin (Django security docs mention this). (Django Project)

DJANGO-XSS-001: Prevent reflected/stored XSS in templates and HTML generation

Severity: High

Required:

  • MUST rely on Django template auto-escaping (safe-by-default) for HTML templates. Django security docs highlight that Django templates escape dangerous characters but have limitations. (Django Project)

  • MUST NOT disable auto-escaping broadly ({% autoescape off %}) unless the content is trusted or safely sanitized. (Django Project)

  • MUST NOT mark untrusted content as safe:

    • Avoid mark_safe(...) on user data.
    • Avoid |safe on user-controlled content.
  • MUST be careful about HTML context pitfalls (e.g., unquoted attributes); Django explicitly shows an example where escaping does not protect an unquoted attribute context. (Django Project)

  • SHOULD prefer safe HTML construction helpers (e.g., format_html) rather than manual concatenation that risks missing escapes. (Django Project)

Insecure patterns:

  • {% autoescape off %}{{ user_input }}{% endautoescape %}
  • {{ user_input|safe }}
  • mark_safe(request.GET["q"])
  • Unquoted attribute injections: <style class={{ var }}>... (Django’s own example). (Django Project)

Detection hints:

  • Search templates for |safe, autoescape off, safeseq.
  • Search Python for mark_safe, SafeString, or direct HTML concatenation with request/DB values.
  • Review any code returning HttpResponse(user_value) where user_value contains HTML.

Fix:

  • Remove unsafe marking; sanitize only when strictly necessary (use an allowlist-based HTML sanitizer).
  • Quote attributes and avoid placing untrusted values into dangerous contexts.
  • Add CSP as defense-in-depth (see DJANGO-CSP-001). (Django Project)

DJANGO-TEMPLATE-001: Never render untrusted template source strings

Severity: High to Critical (depends on context and exposure)

Required:

  • MUST NOT render templates where the template source string is influenced by untrusted input (request, user content, DB rows editable by untrusted users).
  • MUST treat “template from string” patterns as dangerous, even if Django templates are more constrained than some other engines: they can still leak data from context, bypass escaping, and create XSS or content injection.

Insecure patterns:

  • Template(request.GET["tmpl"]).render(Context(...))
  • Saving user templates in the DB and rendering them with normal privileges/context.

Detection hints:

  • Search for django.template.Template(, Engine.from_string, .render(Context( with non-constant strings.
  • Trace where the template string comes from (admin panels, DB, uploads, requests).

Fix:

  • Replace with non-executing formatting (e.g., string.Template, explicit placeholders) or a strict allowlisted rendering model.
  • If you must support user-defined templates, isolate heavily (separate service/tenant context, strict allowlists, and assume bypasses are possible).

DJANGO-SQL-001: Prevent SQL injection (use ORM or parameterized raw SQL)

Severity: High

Required:

  • MUST use Django ORM/querysets for normal DB access; Django notes querysets are parameterized and protected from SQL injection under typical use. (Django Project)
  • MUST be very careful with raw SQL; if using raw(), cursor.execute(), extra(), or RawSQL, MUST pass parameters separately (e.g., params=) and MUST NOT string-interpolate untrusted input into SQL. Django’s raw SQL docs warn to escape user-controlled parameters using params. (Django Project)
  • MUST NOT quote placeholders in SQL templates (Django docs explicitly warn that quoting %s placeholders makes it unsafe). (Django Project)
  • SHOULD avoid extra() and RawSQL unless necessary; Django security docs call for caution. (Django Project)

Insecure patterns:

  • cursor.execute(f"SELECT ... WHERE id={request.GET['id']}")
  • Model.objects.raw("... %s" % user_input) (string formatting)
  • extra(where=[f"headline='{q}'"])
  • Quoted placeholders: WHERE othercol = '%s' (explicitly documented as unsafe). (Django Project)

Detection hints:

  • Grep for .raw(, .extra(, RawSQL(, connection.cursor(), .execute(.
  • Grep for SQL keywords (SELECT, UPDATE, DELETE, INSERT) in Python strings.
  • Track untrusted inputs into these call sites.

Fix:

  • Prefer ORM queries.
  • If raw SQL is unavoidable, use parameters (params, DB-API param binding) and do not quote placeholders. (Django Project)

DJANGO-CMD-001: Prevent OS command injection

Severity: Critical to High (depends on exposure)

Required:

  • MUST avoid executing system commands with attacker-influenced input.

  • If subprocess is necessary:

    • MUST pass args as a list (not a shell string).
    • MUST NOT use shell=True with attacker-influenced content.
    • SHOULD use strict allowlists for variable components.
  • SHOULD prefer pure-Python libraries instead of shelling out.

Insecure patterns:

  • os.system(request.GET["cmd"])
  • subprocess.run(f"convert {path}", shell=True) where path is user-controlled.

Detection hints:

  • Search os.system, subprocess, Popen, shell=True.
  • Trace request/DB inputs into those calls.

Fix:

  • Replace with library APIs; if unavoidable, hard-code executable and allowlist validated parameters.

DJANGO-UPLOAD-001: File uploads must be validated, stored safely, and served safely

Severity: High

Required:

  • MUST treat all user uploads as untrusted. Django explicitly warns “Media files are uploaded by your users. They’re untrusted!” (Django Project)
  • MUST ensure the web server never interprets user uploads as executable code (e.g., don’t allow uploaded .php or HTML to execute/inline as active content). (Django Project)
  • MUST enforce size limits (at least at the web server; Django security docs recommend limiting upload size at the server to prevent DoS). (Django Project)
  • SHOULD validate file types using allowlists and content checks (not only extensions).
  • SHOULD store uploads outside the application code directory and outside any static root.
  • SHOULD consider serving uploads from a separate top-level/second-level domain to reduce same-origin impact; Django security docs recommend a distinct domain and note that a subdomain may be insufficient for some protections. (Django Project)
  • MUST be aware of polyglot upload risks: Django documents a case where HTML can be uploaded “as an image” by using a valid PNG header (and may be served as HTML depending on the web server). (Django Project)

Insecure patterns:

  • Serving uploads inline with text/html or without forcing download for potentially active formats.
  • Upload allowlist based only on extension.
  • Upload storage inside static roots or code roots.

Detection hints:

  • Search for request.FILES, FileField, ImageField, upload forms/views.
  • Inspect upload serving paths and Nginx/Apache config (media handlers).
  • Check MEDIA_URL, MEDIA_ROOT, and static config.

Fix:

  • Configure the web server to serve uploads as inert bytes (no execution), and consider forcing Content-Disposition: attachment for risky types.
  • Use a separate domain for user content when warranted. (Django Project)

DJANGO-PATH-001: Prevent path traversal and unsafe file serving (static/media separation)

Severity: High

Required:

  • MUST NOT treat user input as a filesystem path for reads/writes/serving.
  • MUST keep MEDIA_ROOT and STATIC_ROOT distinct; Django settings docs explicitly warn they must have different values to avoid security implications. (Django Project)
  • SHOULD prefer using Django storage APIs keyed by server-side identifiers rather than accepting arbitrary relative paths from users.

Insecure patterns:

  • open(os.path.join(MEDIA_ROOT, request.GET["path"]))
  • Download endpoints that take ?file=../../... style parameters.
  • Misconfigured MEDIA_ROOT == STATIC_ROOT.

Detection hints:

  • Grep for open(, Path(, os.path.join( used with request values.
  • Check MEDIA_ROOT, STATIC_ROOT in settings. (Django Project)

Fix:

  • Use server-side IDs mapped to known files.
  • Keep static and media separated and ensure the web server treats media as untrusted. (Django Project)

DJANGO-REDIRECT-001: Prevent open redirects (next, return_to, redirect)

Severity: Medium (High when combined with auth flows)

Required:

  • MUST validate redirect targets derived from untrusted input (e.g., next, return_to).
  • SHOULD restrict to same-site relative paths or allowlisted hosts/schemes.
  • SHOULD use Django’s safe URL helpers (e.g., django.utils.http.url_has_allowed_host_and_scheme) rather than custom parsing.

Insecure patterns:

  • return redirect(request.GET.get("next")) with no validation.
  • Redirect allowlist implemented with naive string checks.

Detection hints:

  • Search for redirect( and track origin of the target.
  • Search for parameters named next, return_to, redirect, url.

Fix:

  • Validate with allowlists and default to a safe internal path if validation fails.
  • Ensure host validation via ALLOWED_HOSTS remains strict (see DJANGO-HOST-001). (Django Project)

DJANGO-HEADERS-001: Enable essential security headers (SecurityMiddleware + clickjacking protection)

Severity: Medium to High

Required:

  • SHOULD use django.middleware.security.SecurityMiddleware and configure it appropriately (production) for:

    • X-Content-Type-Options: nosniff (Django setting SECURE_CONTENT_TYPE_NOSNIFF, default True). (Django Project)
    • Referrer-Policy (Django setting SECURE_REFERRER_POLICY, default 'same-origin'). (Django Project)
    • COOP (Django setting SECURE_CROSS_ORIGIN_OPENER_POLICY, default 'same-origin'). (Django Project)
    • HTTPS redirects and HSTS as appropriate (see DJANGO-HTTPS-001). (Django Project)
  • SHOULD enable clickjacking protection via X-Frame-Options middleware; Django security docs strongly recommend it for sites that don’t need third-party framing. (Django Project)

Insecure patterns:

  • Missing SecurityMiddleware.
  • Missing clickjacking protection (or disabling it globally) without a clear framing requirement.
  • Over-broad framing allowances for sensitive endpoints.

Detection hints:

  • Inspect MIDDLEWARE for SecurityMiddleware and XFrameOptionsMiddleware.
  • Search for per-view disabling of framing/CSRF protections.

Fix:

  • Add/enable middleware and configure the settings intentionally. (Django Project)

NOTE:

  • Some headers may be set at the edge (CDN/reverse proxy). If not visible in app code, flag as “verify at edge”.

DJANGO-CSP-001: Deploy a Content Security Policy (CSP) as defense-in-depth

Severity: Medium (High for apps rendering untrusted content)

NOTE: It is most important to set the CSP’s script-src. All other directives are not as important and can generally be excluded for the ease of development.

Required:

  • SHOULD deploy a CSP to mitigate XSS and content injection classes; Django’s security docs recommend CSP and note it is new in Django 6.0. (Django Project)

  • MUST understand CSP limitations:

    • Avoid excluding routes from CSP coverage; Django warns that an unprotected page can undermine protected pages due to same-origin policy. (Django Project)
  • MAY start with SECURE_CSP_REPORT_ONLY to iterate safely (Django provides report-only support). (Django Project)

Insecure patterns:

  • No CSP on apps that render user-controlled content.
  • CSP excludes “just a couple pages” (weakens overall protection), especially pages with any injection surface. (Django Project)
  • CSP uses overly permissive directives (e.g., widespread unsafe-inline) without justification.

Detection hints:

  • Search SECURE_CSP, SECURE_CSP_REPORT_ONLY, and CSP middleware configuration.
  • Inspect reverse proxy/CDN config for CSP headers.

Fix:

  • Implement a realistic CSP, ideally report-only first, then enforce. (Django Project)

DJANGO-AUTH-001: Password storage must use Django’s secure hashers; password policy must be configured

Severity: High

Required:

  • MUST use Django’s built-in password hashing (never store plaintext or reversible encrypted passwords).
  • SHOULD prefer modern hashers and keep defaults updated; Django documents PASSWORD_HASHERS and includes modern options (Argon2, bcrypt, scrypt, PBKDF2 variants). (Django Project)
  • SHOULD configure AUTH_PASSWORD_VALIDATORS (default is empty) for production password policy. (Django Project)

Insecure patterns:

  • Custom password storage or hashing.
  • Plaintext passwords stored in DB fields.
  • No password validation on consumer-facing apps.

Detection hints:

  • Search for .set_password( usage vs manual hashing.
  • Inspect settings for PASSWORD_HASHERS and AUTH_PASSWORD_VALIDATORS. (Django Project)

Fix:

  • Use Django auth user model APIs.
  • Enable password validators appropriate to the product’s risk profile. (Django Project)

DJANGO-AUTHZ-001: Authorization must be explicit and consistent

Severity: High

Required:

  • MUST enforce authorization checks on every privileged action (view, modify, admin-like operations).
  • MUST NOT rely on UI-only restrictions (e.g., hiding buttons) without server-side permission checks.
  • SHOULD use Django’s permissions/groups and per-object authorization patterns where applicable.

Insecure patterns:

  • Views that assume “user is logged in” implies “user may do action”.
  • Missing authorization checks on update/delete endpoints.

Detection hints:

  • Enumerate views that modify state; ensure they validate ownership/permission.
  • Look for use of only is_authenticated or only is_staff without checking object-level access.

Fix:

  • Add explicit permission checks and tests for unauthorized access.

DJANGO-ADMIN-001: Django admin must be treated as a high-value target

Severity: High

Required:

  • MUST ensure admin is protected by strong authentication and HTTPS-only transport (see DJANGO-HTTPS-001). (Django Project)
  • SHOULD restrict admin exposure (network allowlists, VPN, SSO, or additional authentication controls) when possible.
  • SHOULD audit installed admin extensions and third-party apps for XSS/CSRF exposure.

Insecure patterns:

  • Admin exposed to the internet with weak authentication.
  • Admin served over HTTP.

Detection hints:

  • Search urlpatterns for admin.site.urls.
  • Check deployment config for IP allowlisting or auth gateways.

Fix:

  • Add network controls and enforce HTTPS.

DJANGO-LOG-001: Logging and error reporting must not leak secrets

Severity: Medium to High

Required:

  • MUST NOT log secrets (including SECRET_KEY, session cookies, auth headers, password reset tokens).
  • MUST configure production logging deliberately; Django’s deployment checklist explicitly calls out reviewing logging before production. (Django Project)
  • MUST ensure DEBUG=False in production so exceptions aren’t rendered with sensitive context. (Django Project)

Insecure patterns:

  • Logging full request headers or cookies in production.
  • Printing settings dictionaries.
  • Debug error pages.

Detection hints:

  • Inspect LOGGING config; search for middleware that logs request headers/cookies.
  • Grep for print(settings / logging.info(request.META) patterns.

Fix:

  • Redact sensitive values; log IDs not secrets.
  • Use structured logging and a safe error monitoring tool. (Django Project)

DJANGO-SUPPLY-001: Dependency and patch hygiene (Django + security-critical deps)

Severity: Medium (High if known vulnerable versions)

Required:

  • SHOULD pin and regularly update Django and security-critical dependencies.
  • MUST respond to Django security releases promptly.

Detection hints:

  • Check requirements.txt, lockfiles, build images.
  • Identify Django version; compare against latest supported release (Django’s download page publishes current stable and supported branches). (Django Project)

Fix:

  • Upgrade to patched versions; add regression tests for previously vulnerable classes.

5) Practical scanning heuristics (how to “hunt”)

When actively scanning, use these high-signal patterns:

  • Deployment/dev server:

  • Debug / settings:

  • Host validation:

  • HTTPS and proxy:

    • SECURE_SSL_REDIRECT, SECURE_HSTS_SECONDS, SECURE_PROXY_SSL_HEADER (Django Project)
  • Cookies / sessions:

    • SESSION_COOKIE_SECURE, SESSION_COOKIE_HTTPONLY, SESSION_COOKIE_SAMESITE (Django Project)
    • CSRF_COOKIE_SECURE, CSRF_COOKIE_HTTPONLY, CSRF_COOKIE_SAMESITE (Django Project)
  • CSRF bypasses:

    • csrf_exempt, missing CsrfViewMiddleware, POST forms without {% csrf_token %} (Django Project)
  • XSS:

    • |safe, autoescape off, mark_safe(, HTML string concatenation (Django Project)
  • SQL injection:

    • .raw(, .extra(, RawSQL(, cursor.execute( with formatted SQL strings (Django Project)
  • User uploads / media:

    • request.FILES, MEDIA_ROOT, MEDIA_URL, serving media inline; MEDIA_ROOT == STATIC_ROOT (Django Project)
  • Redirects:

    • redirect(request.GET.get("next")) patterns; missing allowlist validation
  • Security headers and CSP:

    • Missing SecurityMiddleware, missing X-Frame-Options protection, missing SECURE_CSP adoption (where appropriate) (Django Project)

Always try to confirm:

  • data origin (untrusted vs trusted)
  • sink type (template/SQL/subprocess/files/redirect/http)
  • protective controls present (middleware, validation, allowlists, authz checks)
  • whether security headers/controls are set in-app vs at the edge

6) Sources (accessed 2026-01-27)

Primary Django documentation:

- Django Downloads (current stable & supported branches): https://www.djangoproject.com/download/
- Django 6.0 Release Notes: https://docs.djangoproject.com/en/6.0/releases/6.0/
- Django: Deployment checklist (incl. check --deploy, runserver warning, HTTPS/cookies guidance): https://docs.djangoproject.com/en/6.0/howto/deployment/checklist/
- Django: Settings reference (SecurityMiddleware settings, cookies, SECRET_KEY_FALLBACKS, CSP settings): https://docs.djangoproject.com/en/6.0/ref/settings/
- Django: Security in Django (XSS/CSRF/SQLi/clickjacking/HTTPS/host header validation/uploads/CSP): https://docs.djangoproject.com/en/6.0/topics/security/
- Django: CSRF how-to (middleware, csrf_token usage, AJAX header patterns, csrf_exempt cautions): https://docs.djangoproject.com/en/6.0/howto/csrf/
- Django: Performing raw SQL queries (parameterization guidance): https://docs.djangoproject.com/en/6.0/topics/db/sql/
- Django: QuerySet API reference (extra() cautions; “do not quote placeholders” guidance): https://docs.djangoproject.com/en/6.0/ref/models/querysets/
- Django: Template built-ins (autoescape tag): https://docs.djangoproject.com/en/6.0/ref/templates/builtins/
- Django: Template language reference (turning off autoescape & risks): https://docs.djangoproject.com/en/6.0/ref/templates/language/
- Django: Utilities reference (e.g., format_html): https://docs.djangoproject.com/en/6.0/ref/utils/

OWASP:

- OWASP Cheat Sheet Series: Django Security Cheat Sheet: https://cheatsheetseries.owasp.org/cheatsheets/Django_Security_Cheat_Sheet.html

Reference: Python Fastapi Web Server Security

FastAPI (Python) Web Security Spec (FastAPI 0.128.x, Python 3.x) (PyPI)

This document is designed as a security spec that supports:

  1. Secure-by-default code generation for new FastAPI code.
  2. Security review / vulnerability hunting in existing FastAPI code (passive “notice issues while working” and active “scan the repo and report findings”).

It is intentionally written as a set of normative requirements (“MUST/SHOULD/MAY”) plus audit rules (what bad patterns look like, how to detect them, and how to fix/mitigate them).

FastAPI is commonly deployed with an ASGI server (e.g., Uvicorn) and is built on Starlette + Pydantic, so this spec covers those layers where they affect security. (PyPI)


0) Safety, boundaries, and anti-abuse constraints (MUST FOLLOW)

  • MUST NOT request, output, log, or commit secrets (API keys, passwords, private keys, session cookies, signing keys, database URLs with credentials).

  • MUST NOT “fix” security by disabling protections (e.g., weakening auth, making CORS permissive, skipping signature checks, disabling validation, turning off TLS verification, adding allow_origins=["*"] with credentials).

  • MUST provide evidence-based findings during audits: cite file paths, code snippets, and configuration values that justify the claim.

  • MUST treat uncertainty honestly: if a protection might exist in infrastructure (reverse proxy, WAF, CDN, service mesh), report it as “not visible in app code; verify at runtime/config”.

  • MUST treat browser controls correctly:

    • CORS is not an auth mechanism; it only affects browsers.
    • CSRF defenses apply when the browser automatically attaches credentials (cookies); they are usually not relevant for purely header-token APIs. (OWASP Cheat Sheet Series)

1) Operating modes

1.1 Generation mode (default)

When asked to write new FastAPI code or modify existing code:

  • MUST follow every MUST requirement in this spec.
  • SHOULD follow every SHOULD requirement unless the user explicitly says otherwise.
  • MUST prefer safe-by-default APIs and proven libraries over custom security code.
  • MUST avoid introducing new risky sinks (shell execution, unsafe deserialization, dynamic eval, untrusted template rendering, unsafe file serving, unsafe redirects, arbitrary outbound fetching).

1.2 Passive review mode (always on while editing)

While working anywhere in a FastAPI repo (even if the user did not ask for a security scan):

  • MUST “notice” violations of this spec in touched/nearby code.
  • SHOULD mention issues as they come up, with a brief explanation + safe fix.

1.3 Active audit mode (explicit scan request)

When the user asks to “scan”, “audit”, or “hunt for vulns”:

  • MUST systematically search the codebase for violations of this spec.
  • MUST output findings in a structured format (see §2.3).

Recommended audit order:

  1. App entrypoints / deployment scripts / Dockerfiles / Procfiles / Helm/terraform.
  2. ASGI server configuration (Uvicorn/Gunicorn), proxy settings, debug/reload settings.
  3. FastAPI app configuration (docs exposure, middleware, trusted hosts, CORS).
  4. Authn/Authz design (dependencies, JWT/session handling, password storage).
  5. Cookie/session usage + CSRF (if cookies are used).
  6. Input validation and output shaping (Pydantic models, mass assignment, excessive data exposure).
  7. Template rendering and XSS/SSTI (if HTML is served).
  8. File handling (uploads + downloads), StaticFiles, Range support.
  9. Injection classes (SQL, command execution, unsafe deserialization).
  10. Outbound requests (SSRF), redirect handling, WebSockets security.

2) Definitions and review guidance

2.1 Untrusted input (treat as attacker-controlled unless proven otherwise)

Examples include:

  • Query parameters / path parameters
  • JSON bodies (including nested fields)
  • Headers (including Host, Origin, X-Forwarded-*)
  • Cookies (including session cookies)
  • File uploads (multipart parts)
  • WebSocket messages, query params, and headers during handshake (Starlette)
  • Any data from external systems (webhooks, third-party APIs, message queues)
  • Any persisted user content (DB rows) that originated from users

2.2 State-changing request

A request is state-changing if it can create/update/delete data, change auth/session state, trigger side effects (purchase, email send, webhook send), or initiate privileged actions.

2.3 Required audit finding format

For each issue found, output:

  • Rule ID:
  • Severity: Critical / High / Medium / Low
  • Location: file path + function/route name + line(s)
  • Evidence: the exact code/config snippet
  • Impact: what could go wrong, who can exploit it
  • Fix: safe change (prefer minimal diff)
  • Mitigation: defense-in-depth if immediate fix is hard
  • False positive notes: what to verify if uncertain

3) Secure baseline: minimum production configuration (MUST in production)

This is the smallest “production baseline” that prevents common FastAPI/ASGI misconfigurations.

Baseline goals:

  • No debug tracebacks or auto-reload in production. (PyPI)
  • Run under a production ASGI server configuration (workers, timeouts, resource controls). (PyPI)
  • Host header validation enabled (TrustedHostMiddleware or equivalent). (PyPI)
  • CORS disabled unless explicitly needed; if enabled, it is strict and least-privilege. (OWASP Cheat Sheet Series)
  • Auth is enforced consistently via dependencies (no “oops, forgot auth on this route”). (FastAPI)
  • If cookies/sessions are used, cookie flags are secure and CSRF is addressed. (OWASP Cheat Sheet Series)
  • Request size limits and multipart limits exist at the edge and are validated in app as needed (to mitigate memory/CPU DoS). (advisories.gitlab.com)
  • Dependencies are patched promptly, especially Starlette/python-multipart (multiple DoS and traversal advisories exist historically). (advisories.gitlab.com)

4) Rules (generation + audit)

Each rule contains: required practice, insecure patterns, detection hints, and remediation.

FASTAPI-DEPLOY-001: Do not use auto-reload / dev-only server modes in production

Severity: High (if production)

Required:

  • MUST NOT run production using auto-reload/watch mode (e.g., Uvicorn reload).
  • MUST run with a production process model (e.g., multiple workers where appropriate) and stable server settings. (PyPI)

Insecure patterns:

  • uvicorn ... --reload (or equivalent “reload=True” configs) in production entrypoints.
  • Docker/Procfile/systemd commands that run with --reload in production.

Detection hints:

  • Search for --reload, reload=True, watchfiles, fastapi dev, “development” run scripts.
  • Check Docker CMD/ENTRYPOINT, Procfile, systemd units, shell scripts.

Fix:

  • Remove reload in production; run Uvicorn/Gunicorn with stable settings and explicit worker configuration. (PyPI)

Note:

  • Reload is fine for local development. Only flag when it is clearly used as a production entrypoint.

FASTAPI-DEPLOY-002: Debug mode MUST be disabled in production

Severity: Critical

Required:

  • MUST NOT enable debug tracebacks in production (FastAPI/Starlette debug mode can expose sensitive internals and make some exploit chains easier). (PyPI)
  • MUST treat any configuration that returns detailed stack traces to clients as sensitive.

Insecure patterns:

  • app = FastAPI(debug=True) (or Starlette debug=True), or equivalent environment toggles enabling debug in production. (PyPI)
  • Server/log config that exposes tracebacks to end users.

Detection hints:

  • Search for debug=True, DEBUG = True, environment flags mapped to debug.
  • Review exception middleware and error handler setup.

Fix:

  • Ensure debug is only enabled in local dev/test.
  • Return generic error responses to clients; log details internally.

FASTAPI-OPENAPI-001: OpenAPI and interactive docs MUST be disabled or protected in production

Severity: Medium (can be High in sensitive/internal apps)

Required:

  • SHOULD disable /docs, /redoc, and /openapi.json in production for public-facing services unless there is an explicit business need.
  • If enabled, MUST protect them (e.g., auth, network allowlists, or internal-only routing).
  • MUST NOT assume “security through obscurity”; treat docs exposure as an information disclosure amplifier.

Insecure patterns:

  • Publicly reachable /docs and /openapi.json for internal/admin APIs.
  • Docs enabled on the same hostname as production without access control.

Detection hints:

  • Look for FastAPI(docs_url=..., redoc_url=..., openapi_url=...) or defaults.
  • Check reverse proxy routing and allowlists.

Fix:

  • Disable docs endpoints in prod (docs_url=None, redoc_url=None, openapi_url=None) or restrict access at the edge.

FASTAPI-AUTH-001: Authentication MUST be explicit and consistently enforced via dependencies

Severity: High

Required:

  • MUST implement authentication as a dependency (or router-level dependency) so that protected endpoints cannot “forget” auth.
  • MUST default to “deny” for privileged routers/endpoints; explicitly mark truly public routes.
  • SHOULD centralize auth enforcement at router boundaries (e.g., protected APIRouter for authenticated endpoints). (FastAPI)

Insecure patterns:

  • Per-route ad-hoc auth checks scattered through handlers (easy to miss).
  • A mix of protected/unprotected endpoints with no clear policy.

Detection hints:

  • Identify routers and endpoints; check whether protected ones include Depends(...)/Security(...).
  • Search for patterns like if user is None: raise ... inside handlers (instead of dependencies).

Fix:

  • Move authentication into a dependency and attach it to the router/endpoint consistently using Depends()/Security(). (FastAPI)

FASTAPI-AUTH-002: Use standard auth transports; avoid secrets in URLs

Severity: High

Required:

  • SHOULD use the Authorization: Bearer <token> header for token auth, not query parameters. (FastAPI)
  • MUST NOT place secrets (tokens, reset links containing long-lived secrets, API keys) in query strings when avoidable.

Insecure patterns:

  • ?token=..., ?api_key=..., ?auth=... used for primary auth.
  • Long-lived access tokens embedded in URLs (leak via logs, referrers, caches).

Detection hints:

  • Search for parameter names like token, api_key, key, secret, password.
  • Look for security schemes that use query API keys without justification.

Fix:

  • Move tokens to Authorization headers; rotate/shorten lifetimes; use POST bodies for sensitive values.

FASTAPI-AUTH-003: Password storage MUST be strongly hashed; never store plaintext passwords

Severity: Critical

Required:

  • MUST store passwords using a strong, slow password hashing scheme (e.g., Argon2id, bcrypt).
  • MUST NOT store plaintext passwords, or reversible encryption as the primary protection.
  • SHOULD use established libraries for hashing and verification (do not roll your own).

Insecure patterns:

  • Storing plaintext passwords in DB.
  • Using fast hashes (e.g., SHA256) without a proper password hashing KDF.
  • Returning password hashes in API responses.

Detection hints:

  • Search for password= persisted fields, and look for hashlib.md5/sha1/sha256 usage on passwords.
  • Inspect response models for password/hash fields.

Fix:

  • Migrate to a proper password hashing library; add a re-hash-on-login upgrade path.

FASTAPI-AUTH-004: JWT validation MUST be strict; JWTs MUST NOT carry secrets

Severity: High

Required:

  • MUST validate JWT signature and enforce an algorithm allowlist.
  • MUST validate standard claims appropriate to your system (at least exp; typically also iss/aud if multi-service or multi-tenant).
  • MUST treat JWT contents as readable by the client; do not put secrets in JWT payloads. (FastAPI)

Insecure patterns:

  • jwt.decode(..., options={"verify_signature": False}) or equivalent.
  • Accepting alg=none / algorithm confusion.
  • Using JWT payload to store sensitive secrets (API keys, passwords).

Detection hints:

  • Search for jwt.decode, python-jose, PyJWT, verify_signature.
  • Check for missing exp validation or long expirations.

Fix:

  • Enforce strict validation (signature, allowed algorithms, exp, and any required issuer/audience constraints).
  • Store only identifiers/claims you are comfortable exposing to the client. (FastAPI)

FASTAPI-AUTHZ-001: Authorization MUST be enforced per-object and per-property

Severity: High

Required:

  • MUST perform object-level authorization whenever accessing a resource by user-controlled identifier (ID in path/query/body).
  • MUST perform property-level authorization and response shaping to prevent “excessive data exposure” (e.g., admin-only fields). (OWASP Foundation)

Insecure patterns:

  • GET /users/{id} returns user record without verifying caller can access that id.
  • Response models include internal fields (roles, permissions, billing data, password hashes).

Detection hints:

  • Enumerate endpoints that accept IDs; trace whether an authz check is performed.
  • Compare response models for public vs internal fields.

Fix:

  • Add object-level checks (ownership, ACLs, tenant boundaries).
  • Use dedicated response models that include only allowed fields.

Severity: High (only if TLS is enabled)

Required (production, HTTPS):

  • MUST set session cookies to be sent only over HTTPS (secure). IMPORTANT NOTE: Only set Secure in production environment when TLS is configured. When running in a local dev environment over HTTP, do not set Secure property on cookies. You should do this conditionally based on if the app is running in production mode. You should also include a property like SESSION_COOKIE_SECURE which can be used to disable Secure cookies when testing over HTTP.
  • MUST set HttpOnly for session cookies (not accessible to JS).
  • SHOULD use SameSite=Lax (or Strict if UX allows); if you require cross-site cookies, document the CSRF implications and add compensating controls. (OWASP Cheat Sheet Series)
  • If using Starlette SessionMiddleware, MUST set https_only=True in production and choose an appropriate same_site. (PyPI)

Insecure patterns:

  • Session cookies without Secure/HttpOnly.
  • SameSite=None cookies used for authenticated state-changing endpoints without CSRF protections.

Detection hints:

  • Search for SessionMiddleware( and inspect parameters like https_only, same_site.
  • Search for set_cookie( usage and cookie flags.

Fix:


FASTAPI-SESS-002: Do not store sensitive secrets in signed session cookies

Severity: High

Required:

  • MUST assume cookie-based session data is readable by the client (signed ≠ encrypted); do not store secrets/PII unless encrypted server-side.
  • Store only opaque identifiers (e.g., session ID) or non-sensitive state in the cookie; store sensitive session state server-side. (OWASP Cheat Sheet Series)

Insecure patterns:

  • Storing access tokens, refresh tokens, or PII directly in cookie session payloads.
  • Treating “signed cookies” as confidential storage.

Detection hints:

  • Search for request.session[...] = or session[...] =-equivalent patterns; identify what is stored.
  • Identify use of SessionMiddleware or other cookie session mechanisms.

Fix:

  • Move sensitive values to server-side storage; keep cookie minimal.

Severity: High

Note: This only applies if using cookie based auth. If the application uses header or token based auth such as Authorization header, then CSRF is not an issue.

Required:

  • MUST protect all state-changing endpoints (POST/PUT/PATCH/DELETE) that rely on cookies for authentication.
  • SHOULD use a proven CSRF approach (synchronizer token pattern, or well-reviewed middleware) rather than rolling your own. (OWASP Cheat Sheet Series)
  • MAY add defense-in-depth (Origin/Referer checks, SameSite cookies, Fetch Metadata), but tokens are the primary defense for cookie-authenticated apps. (OWASP Cheat Sheet Series)
  • IMPORTANT NOTE: If cookies are not used for auth (auth is via Authorization header), CSRF is usually not applicable. (FastAPI)

Insecure patterns:

  • Cookie-authenticated endpoints that change state with no CSRF validation.
  • Using GET for state-changing actions (amplifies CSRF risk).

Detection hints:

  • Enumerate routes with methods other than GET; identify whether cookies are used for auth.
  • Look for CSRF token generation/verification or middleware.

Fix:


FASTAPI-VALID-001: Request parsing and validation MUST be schema-driven; prevent mass assignment

Severity: Medium (especially for APIs that write to DB)

Required:

  • SHOULD use Pydantic models for request bodies instead of accepting arbitrary dict/Any.
  • SHOULD configure models to reject unexpected fields where appropriate (prevents “mass assignment” style bugs).
  • MUST validate and normalize identifiers (IDs, email, URLs) before using them for access control or side effects. (OWASP Cheat Sheet Series)

Insecure patterns:

  • payload = await request.json() followed by Model(**payload) or direct DB writes with payload (no allowlist).
  • Models that silently accept unknown fields for write endpoints.

Detection hints:

  • Search for await request.json(), request.body(), dict-typed bodies, Any-typed bodies.
  • Look for endpoints that do db.update(**payload) or Model(**payload) with unfiltered input.

Fix:


FASTAPI-RESP-001: Prevent excessive data exposure via response models and explicit serialization

Severity: Medium

Required:

  • MUST define response models that include only intended fields (especially for user objects, auth-related objects, billing objects).
  • SHOULD use separate models for “create input”, “db/internal”, and “public output” to avoid leaking sensitive fields. (FastAPI)

Insecure patterns:

  • Returning ORM objects or dicts that include internal columns.
  • Reusing “DB model” as the response model (includes password_hash, is_admin, etc).

Detection hints:

  • Look for endpoints that return user where user is an ORM instance.
  • Check for response_model omissions on endpoints that return sensitive resources.

Fix:

  • Add explicit response models; create “public” schemas that exclude sensitive fields. (FastAPI)

FASTAPI-XSS-001: Prevent reflected/stored XSS in HTML responses and templates

Severity: High (if the service serves HTML)

Required:

  • MUST use templating with auto-escaping enabled for HTML.
  • MUST NOT mark untrusted content as safe (no unsafe “raw HTML” rendering of user-controlled data).
  • SHOULD deploy a CSP when serving HTML that includes any user content. (OWASP Cheat Sheet Series)

Insecure patterns:

  • Rendering user content directly into HTML without escaping/sanitization.
  • Disabling auto-escaping or using “raw HTML” features without sanitization.

Detection hints:

  • Search for template rendering and string concatenation that builds HTML.
  • Review templates for “unsafe” filters/constructs and unquoted attributes.

Fix:

  • Keep auto-escaping on; sanitize user HTML only if absolutely required using a trusted sanitizer; add CSP. (OWASP Cheat Sheet Series)

Note:

  • If the app is a pure JSON API, XSS is usually a client/app concern, but error pages/docs pages might still render HTML.

FASTAPI-SSTI-001: Never render untrusted templates (Server-Side Template Injection)

Severity: Critical

Required:

  • MUST NOT render templates that contain user-controlled template syntax.

  • MUST treat “template-from-string” rendering as dangerous if influenced by untrusted input.

  • If untrusted templates are absolutely required (rare, high-risk):

    • MUST use a sandboxed templating approach and restrict capabilities.
    • MUST assume sandbox escapes are possible; add isolation and strict allowlists. (OWASP Foundation)

Insecure patterns:

  • Rendering templates loaded from user input or DB via a normal Jinja environment.
  • Building templates dynamically using user-controlled strings.

Detection hints:

  • Grep for Jinja Environment.from_string, Template(...), or similar.
  • Trace origin of template string (request, DB, uploads, admin panels).

Fix:

  • Replace with non-executable templating (simple string substitution).
  • If truly needed, use Jinja’s sandbox environment plus strong isolation. (jinja.palletsprojects.com)

FASTAPI-HEADERS-001: Set essential security headers (in app or at the edge)

Severity: Medium

Required (typical API/web app):

  • SHOULD set:

    • X-Content-Type-Options: nosniff
    • Clickjacking protection (X-Frame-Options and/or CSP frame-ancestors) if HTML is served
    • Referrer-Policy and Permissions-Policy as appropriate

NOTE:

  • Headers may be set by a proxy/CDN. If not visible in app code, flag as “verify at edge”. (OWASP Cheat Sheet Series)

Insecure patterns:

  • No security headers anywhere (app or edge) for apps serving HTML or sensitive APIs.

Detection hints:

  • Search for middleware that sets headers; check reverse proxy config.

Fix:

  • Set headers centrally (middleware) or via reverse proxy/CDN.

FASTAPI-CORS-001: CORS MUST be explicit and least-privilege

Severity: Medium (High if misconfigured with credentials)

Required:

  • If CORS is not needed, MUST keep it disabled.

  • If CORS is needed:

    • MUST allowlist trusted origins (do not reflect arbitrary origins).
    • MUST NOT combine credentialed requests with wildcard origins (this is unsafe and commonly rejected by compliant middleware). (OWASP Cheat Sheet Series)
    • SHOULD restrict allowed methods and headers.

Insecure patterns:

  • allow_origins=["*"] together with allow_credentials=True.
  • Reflecting Origin without validation.
  • allow_origin_regex=".*" used broadly.

Detection hints:

  • Search for CORSMiddleware configuration.
  • Look for allow_origins=["*"], allow_credentials=True, allow_origin_regex.

Fix:

  • Use an explicit origin allowlist and minimal methods/headers; keep credentials off unless required. (OWASP Cheat Sheet Series)

FASTAPI-HOST-001: Host header MUST be validated in production

Severity: Low

Required:

  • SHOULD use TrustedHostMiddleware (or equivalent at edge) to restrict accepted Host values. (PyPI)
  • MUST NOT trust the Host header for security-sensitive decisions without validation.

Insecure patterns:

  • No Host validation while generating external URLs (password reset links, callback URLs) from request host.
  • Allowing arbitrary Host headers in apps behind permissive proxies.

Detection hints:

  • Search for TrustedHostMiddleware usage.
  • Search for logic that uses request.url, request.base_url, or host-derived values to build external URLs.

Fix:

  • Configure a strict allowed-hosts list in production; enforce at edge too if possible.

FASTAPI-PROXY-001: Reverse proxy trust MUST be configured correctly

Severity: High (when behind a proxy)

Required:

  • If behind a reverse proxy, MUST configure forwarded-header trust correctly.
  • MUST NOT blindly trust X-Forwarded-* headers from the open internet.
  • If using Uvicorn proxy header support, MUST restrict which IPs are allowed to provide forwarded headers. (PyPI)

Insecure patterns:

  • Enabling proxy headers broadly without restricting trusted proxy IPs.
  • Using forwarded headers to decide “is secure” / “is internal” / “client IP” without proper trust boundaries.

Detection hints:

  • Search for --proxy-headers, --forwarded-allow-ips, or equivalent config.
  • Search for security-sensitive use of request.client.host, request.url.scheme, request.headers["x-forwarded-for"].

Fix:

  • Configure Uvicorn with proxy headers only when behind a known proxy, and restrict forwarded_allow_ips to that proxy. (PyPI)
  • Keep Host allowlisting in place even behind proxies.

FASTAPI-LIMITS-001: Request and multipart limits MUST be enforced to prevent DoS

Severity: Low

Required:

  • MUST enforce request size limits at the edge (reverse proxy/load balancer) and validate in app where needed.
  • MUST apply special scrutiny to multipart/form-data handling; historical vulnerabilities include unbounded buffering and DoS vectors. (advisories.gitlab.com)
  • SHOULD rate limit and/or add per-IP/per-user throttles for expensive endpoints.

Insecure patterns:

  • Accepting arbitrarily large JSON bodies or multipart forms.
  • Parsing multipart forms without size/field-count controls.

Detection hints:

  • Identify file upload endpoints and multipart/form-data usage.
  • Look for missing proxy-level limits (nginx client_max_body_size, ALB limits, etc.) and missing app-level checks.

Fix:

  • Enforce strict body limits and multipart constraints; keep Starlette and python-multipart updated to patched versions. (advisories.gitlab.com)

FASTAPI-FILES-001: Prevent path traversal and unsafe static file exposure

Severity: High

Required:

  • MUST NOT pass user-controlled file paths to FileResponse/filesystem calls without strict validation and safe base directories.
  • If using StaticFiles, MUST keep Starlette updated and understand the security history (path traversal advisory exists for older versions). (advisories.gitlab.com)
  • MUST NOT serve user uploads as executable/active content (especially HTML/JS) from a static root without safe handling.

Insecure patterns:

  • FileResponse(request.query_params["path"])
  • Mounting StaticFiles(directory="uploads") where uploads include HTML/JS/SVG and are served inline.

Detection hints:

  • Search for FileResponse(, StaticFiles(, open( in routes.
  • Trace whether the path originates from untrusted input.

Fix:

  • Use opaque IDs for files; map IDs to server-side stored paths.
  • Serve untrusted content as attachment downloads where appropriate.

FASTAPI-FILES-002: Mitigate Range-header DoS on file-serving endpoints

Severity: Low (if affected versions and file serving is enabled)

Required:

  • MUST keep Starlette patched against known file-serving DoS issues if using FileResponse/StaticFiles.
  • MUST treat unusual Range header handling and file serving as a DoS surface. (advisories.gitlab.com)

Insecure patterns:

  • Serving large files with vulnerable Starlette versions.
  • No rate limiting / CDN shielding for file endpoints.

Detection hints:

  • Identify Starlette version; if in affected range, flag.
  • Find uses of FileResponse and StaticFiles.

Fix:

  • Upgrade Starlette to a fixed version per advisory guidance. (advisories.gitlab.com)
  • Add edge caching/rate limiting for file endpoints where appropriate.

FASTAPI-UPLOAD-001: File uploads MUST be validated, stored safely, and served safely

Severity: Medium

Required:

  • MUST enforce upload size limits (app + edge).
  • MUST validate file type using allowlists and content checks (not only extension). (OWASP Cheat Sheet Series)
  • SHOULD generate server-side filenames (random IDs) and avoid trusting original names.
  • MUST serve potentially active formats safely (download attachment) unless explicitly intended.

Insecure patterns:

  • Accepting arbitrary file types and serving them back inline.
  • Using user-supplied filename as storage path.

Detection hints:

  • Look for upload handlers and where/how files are written.
  • Look for direct exposure of upload directories.

Fix:

  • Implement allowlist validation + safe storage + safe serving; add scanning/quarantine if applicable. (OWASP Cheat Sheet Series)

FASTAPI-INJECT-001: Prevent SQL injection (use parameterized queries / ORM)

Severity: High

Required:

  • MUST use parameterized queries or an ORM that parameterizes under the hood.
  • MUST NOT build SQL by string concatenation / f-strings with untrusted input. (OWASP Cheat Sheet Series)

Insecure patterns:

  • f"SELECT ... WHERE id={user_id}"
  • "... WHERE name = '%s'" % user_input

Detection hints:

  • Grep for SQL keywords in Python strings near .execute(...).
  • Trace untrusted data into DB calls.

Fix:


FASTAPI-INJECT-002: Prevent OS command injection

Severity: Critical to High (depends on exposure)

Required:

  • MUST avoid executing shell commands with untrusted input.

  • If subprocess is necessary:

    • MUST pass args as a list (not a string)
    • MUST NOT use shell=True with attacker-influenced strings
    • SHOULD use strict allowlists for any variable component (OWASP Cheat Sheet Series)

Insecure patterns:

  • os.system(user_input)
  • subprocess.run(f"cmd {user}", shell=True)
  • Passing user strings into bash -c, sh -c, PowerShell, etc.

Detection hints:

  • Search for os.system, subprocess, Popen, shell=True.
  • Trace data from request/DB into these calls.

Fix:

  • Use library APIs instead of shell commands.
  • If unavoidable, hard-code the command and allowlist validated parameters; use -- separator where supported. (OWASP Cheat Sheet Series)

FASTAPI-SSRF-001: Prevent server-side request forgery (SSRF) in outbound HTTP

Severity: Medium (can be High in cloud/VPC environments)

  • Note: For small stand alone projects this is less important. It is most important when deploying into an LAN or with other services listening on the same server.

Required:

  • MUST treat outbound requests to user-provided URLs as high risk.
  • SHOULD validate and restrict destinations (allowlist hosts/domains) for any user-influenced URL fetch.
  • SHOULD block access to localhost/private IP ranges/link-local and cloud metadata endpoints.
  • MUST restrict protocols to http/https.
  • SHOULD set timeouts and carefully control redirects. (OWASP Cheat Sheet Series)

Insecure patterns:

  • httpx.get(request.query_params["url"])
  • “URL preview/import/webhook tester” features that accept arbitrary URLs.

Detection hints:

  • Search for requests, httpx, urllib, aiohttp calls with URLs derived from requests/DB.
  • Identify endpoints named fetch, preview, proxy, webhook, import.

Fix:

  • Implement strict URL parsing + allowlists; add egress controls; set short timeouts; disable redirects if not required. (OWASP Cheat Sheet Series)

FASTAPI-REDIRECT-001: Prevent open redirects

Severity: Low

Required:

  • MUST validate redirect targets derived from untrusted input (next, redirect, return_to).
  • SHOULD prefer redirecting only to same-site relative paths or an allowlist of domains. (OWASP Cheat Sheet Series)

Insecure patterns:

  • Returning RedirectResponse(next) where next is user-controlled with no validation.

Detection hints:

  • Search for RedirectResponse( or redirect logic and examine the source of the target.

Fix:


FASTAPI-WS-001: WebSocket endpoints MUST be authenticated and protected against cross-site abuse

Severity: Medium to High (depends on data/privilege)

Required:

  • MUST authenticate WebSocket connections for any non-public channel (WebSockets don’t inherently provide auth). (OWASP Cheat Sheet Series)
  • SHOULD enforce origin/CSRF-like protections appropriate for browser-based WebSocket clients (Origin validation is a common control).
  • SHOULD rate limit message frequency and connection attempts; close idle/abusive connections.

Insecure patterns:

  • @app.websocket(...) accepts and trusts the connection with no auth check.
  • Using query-string tokens for auth without considering leakage/rotation.

Detection hints:

  • Search for @app.websocket / websocket_endpoint and inspect whether auth is performed before accepting sensitive operations.
  • Review origin checks, token parsing, and per-connection authorization.

Fix:

  • Require authentication during handshake (e.g., a token or session) and enforce authorization for actions/messages.
  • Validate Origin for browser-based clients where appropriate; apply rate limits and timeouts. (OWASP Cheat Sheet Series)

FASTAPI-SUPPLY-001: Dependency and patch hygiene (focus on security-relevant deps)

Severity: Low

Required:

  • SHOULD pin and regularly update security-critical dependencies (FastAPI, Starlette, Uvicorn, Pydantic, python-multipart, auth/JWT libs).
  • MUST respond to known security advisories promptly.
  • MUST treat file serving and multipart parsing dependencies as security-sensitive due to historical CVEs. (advisories.gitlab.com)

Audit focus examples (historical):

Detection hints:

  • Check requirements.txt, lockfiles, container images, and runtime environments for actual installed versions.
  • Map file upload/file serving features to dependency versions.

Fix:

  • Upgrade to patched versions per advisories; add regression tests around affected behavior.

5) Practical scanning heuristics (how to “hunt”)

When actively scanning, use these high-signal patterns:

  • Dev server / debug:

    • --reload, reload=True, debug=True, FastAPI(debug=True) (PyPI)
  • OpenAPI/docs exposure:

    • /docs, /redoc, /openapi.json, docs_url=, openapi_url=
  • Auth enforcement gaps:

    • Endpoints missing Depends()/Security() where expected; routers without a consistent dependency boundary (FastAPI)
    • Tokens in query params (token=, api_key=, key=) (FastAPI)
  • Session/cookies + CSRF:

    • SessionMiddleware( and cookie flags (https_only, same_site) (PyPI)
    • POST/PUT/PATCH/DELETE handlers using cookie auth with no CSRF checks (OWASP Cheat Sheet Series)
  • Input validation & mass assignment:

  • Excessive data exposure:

    • Returning ORM objects or dicts without response_model; responses containing password/role/internal fields (FastAPI)
  • CORS:

  • Files:

  • Uploads / multipart:

    • multipart/form-data endpoints with no size/field constraints; outdated Starlette/python-multipart (advisories.gitlab.com)
  • Injection:

  • SSRF:

  • Redirect:

  • WebSockets:

    • @app.websocket handlers without auth/origin checks; use of ws:// in prod configs (FastAPI)

Always try to confirm:

  • data origin (untrusted vs trusted)
  • sink type (SQL/subprocess/files/template/http/redirect/ws)
  • protective controls present (validation, allowlists, middleware, edge controls)
  • installed dependency versions vs vulnerable ranges (advisories.gitlab.com)

6) Sources (accessed 2026-01-27)

Primary framework documentation:

  • FastAPI (PyPI metadata, versioning) — https://pypi.org/project/fastapi/ (PyPI)
  • FastAPI docs: Security “First Steps” (Authorization Bearer header conventions) — https://fastapi.tiangolo.com/tutorial/security/first-steps/ (FastAPI)
  • FastAPI reference: Dependencies (Depends, Security) — https://fastapi.tiangolo.com/reference/dependencies/ (FastAPI)
  • FastAPI reference: APIRouter (router-level dependencies) — https://fastapi.tiangolo.com/reference/apirouter/ (FastAPI)
  • FastAPI docs: WebSockets — https://fastapi.tiangolo.com/advanced/websockets/ (FastAPI)

ASGI/server stack documentation:

  • Starlette (PyPI, general capabilities) — https://pypi.org/project/starlette/ (PyPI)
  • Starlette docs: WebSockets — https://starlette.dev/websockets/ (Starlette)
  • Uvicorn (PyPI metadata) — https://pypi.org/project/uvicorn/ (PyPI)
  • Pydantic docs (v2.12.x) — https://docs.pydantic.dev/latest/ (Pydantic)

Security standards and cheat sheets:

  • OWASP Cheat Sheet Series: Session Management — https://cheatsheetseries.owasp.org/cheatsheets/Session_Management_Cheat_Sheet.html (OWASP Cheat Sheet Series)
  • OWASP Cheat Sheet Series: CSRF Prevention — https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.html (OWASP Cheat Sheet Series)
  • OWASP Cheat Sheet Series: XSS Prevention — https://cheatsheetseries.owasp.org/cheatsheets/Cross_Site_Scripting_Prevention_Cheat_Sheet.html (OWASP Cheat Sheet Series)
  • OWASP Cheat Sheet Series: Mass Assignment — https://cheatsheetseries.owasp.org/cheatsheets/Mass_Assignment_Cheat_Sheet.html (OWASP Cheat Sheet Series)
  • OWASP API Security Top 10 (2023) — https://owasp.org/API-Security/editions/2023/en/0x11-t10/ (OWASP Foundation)
  • OWASP Cheat Sheet Series: SQL Injection Prevention — https://cheatsheetseries.owasp.org/cheatsheets/SQL_Injection_Prevention_Cheat_Sheet.html (OWASP Cheat Sheet Series)
  • OWASP Cheat Sheet Series: OS Command Injection Defense — https://cheatsheetseries.owasp.org/cheatsheets/OS_Command_Injection_Defense_Cheat_Sheet.html (OWASP Cheat Sheet Series)
  • OWASP Cheat Sheet Series: SSRF Prevention — https://cheatsheetseries.owasp.org/cheatsheets/Server_Side_Request_Forgery_Prevention_Cheat_Sheet.html (OWASP Cheat Sheet Series)
  • OWASP Cheat Sheet Series: File Upload — https://cheatsheetseries.owasp.org/cheatsheets/File_Upload_Cheat_Sheet.html (OWASP Cheat Sheet Series)
  • OWASP Cheat Sheet Series: Unvalidated Redirects and Forwards — https://cheatsheetseries.owasp.org/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.html (OWASP Cheat Sheet Series)
  • OWASP Cheat Sheet Series: HTTP Security Response Headers — https://cheatsheetseries.owasp.org/cheatsheets/HTTP_Headers_Cheat_Sheet.html (OWASP Cheat Sheet Series)
  • OWASP Cheat Sheet Series: WebSocket Security — https://cheatsheetseries.owasp.org/cheatsheets/WebSocket_Security_Cheat_Sheet.html (OWASP Cheat Sheet Series)
  • OWASP WSTG: Testing for Server-Side Template Injection — https://owasp.org/www-project-web-security-testing-guide/v41/4-Web_Application_Security_Testing/07-Input_Validation_Testing/18-Testing_for_Server_Side_Template_Injection (OWASP Foundation)
  • OWASP WSTG: Testing WebSockets — https://owasp.org/www-project-web-security-testing-guide/latest/4-Web_Application_Security_Testing/11-Client-side_Testing/10-Testing_WebSockets (OWASP Foundation)

Template safety references:

Selected supply-chain/advisory references (Starlette examples):

  • CVE-2023-29159 (StaticFiles path traversal; fixed 0.27.0) — https://advisories.gitlab.com/pkg/pypi/starlette/CVE-2023-29159/ (advisories.gitlab.com)
  • CVE-2024-47874 (multipart/form-data DoS; fixed 0.40.0) — https://advisories.gitlab.com/pkg/pypi/starlette/CVE-2024-47874/ (advisories.gitlab.com)
  • CVE-2025-62727 (FileResponse Range header DoS; fixed 0.49.1) — https://advisories.gitlab.com/pkg/pypi/starlette/CVE-2025-62727/ (advisories.gitlab.com)

Reference: Python Flask Web Server Security

Flask (Python) Web Security Spec (Flask 3.1.x, Python 3.x)

This document is designed as a security spec that supports:

  1. Secure-by-default code generation for new Flask code.
  2. Security review / vulnerability hunting in existing Flask code (passive “notice issues while working” and active “scan the repo and report findings”).

It is intentionally written as a set of normative requirements (“MUST/SHOULD/MAY”) plus audit rules (what bad patterns look like, how to detect them, and how to fix/mitigate them).


0) Safety, boundaries, and anti-abuse constraints (MUST FOLLOW)

  • MUST NOT request, output, log, or commit secrets (API keys, passwords, private keys, session cookies, SECRET_KEY).
  • MUST NOT “fix” security by disabling protections (e.g., turning off CSRF, relaxing CORS, disabling escaping, disabling auth checks).
  • MUST provide evidence-based findings during audits: cite file paths, code snippets, and configuration values that justify the claim.
  • MUST treat uncertainty honestly: if a protection might exist in infrastructure (reverse proxy, WAF, CDN), report it as “not visible in app code; verify at runtime/config”.

1) Operating modes

1.1 Generation mode (default)

When asked to write new Flask code or modify existing code:

  • MUST follow every MUST requirement in this spec.
  • SHOULD follow every SHOULD requirement unless the user explicitly says otherwise.
  • MUST prefer safe-by-default APIs and proven libraries over custom security code.
  • MUST avoid introducing new risky sinks (template rendering from strings, shell execution, dynamic imports, unsafe redirects, serving user files as HTML, etc.).

1.2 Passive review mode (always on while editing)

While working anywhere in a Flask repo (even if the user did not ask for a security scan):

  • MUST “notice” violations of this spec in touched/nearby code.
  • SHOULD mention issues as they come up, with a brief explanation + safe fix.

1.3 Active audit mode (explicit scan request)

When the user asks to “scan”, “audit”, or “hunt for vulns”:

  • MUST systematically search the codebase for violations of this spec.
  • MUST output findings in a structured format (see §2.3).

Recommended audit order:

  1. App entrypoints / deployment scripts / Dockerfiles / Procfiles.
  2. Flask configuration and environment handling.
  3. Auth + sessions + cookies.
  4. CSRF protections and state-changing routes.
  5. Template rendering and XSS/SSTI.
  6. File handling (uploads + downloads) and path traversal.
  7. Injection classes (SQL, command execution, unsafe deserialization).
  8. Outbound requests (SSRF).
  9. Redirect handling (open redirects).
  10. CORS and security headers.

2) Definitions and review guidance

2.1 Untrusted input (treat as attacker-controlled unless proven otherwise)

Examples include:

  • request.args, request.form, request.values
  • request.get_json(), request.json, request.data
  • request.headers, request.cookies
  • URL path parameters (e.g., /user/<id>)
  • Any data from external systems (webhooks, third-party APIs, message queues)
  • Any persisted user content (DB rows) that originated from users

2.2 State-changing request

A request is state-changing if it can create/update/delete data, change auth/session state, trigger side effects (purchase, email send, webhook send), or initiate privileged actions.

2.3 Required audit finding format

For each issue found, output:

  • Rule ID:
  • Severity: Critical / High / Medium / Low
  • Location: file path + function/route name + line(s)
  • Evidence: the exact code/config snippet
  • Impact: what could go wrong, who can exploit it
  • Fix: safe change (prefer minimal diff)
  • Mitigation: defense-in-depth if immediate fix is hard
  • False positive notes: what to verify if uncertain

3) Secure baseline: minimum production configuration (MUST in production)

This is the smallest “production baseline” that prevents common Flask misconfigurations.

3.1 App initialization pattern (SHOULD)

SHOULD use an app factory and environment-based config so production config is not hard-coded.

Example skeleton (illustrative; adjust to your project):

  • Load config from environment / secret store.
  • Fail closed if critical settings are missing in production.

Key baseline config targets:

  • SECRET_KEY set and not committed
  • SESSION_COOKIE_SECURE=True (when HTTPS) IMPORTANT NOTE: Only set Secure in production environment when TLS is configured. When running in a local dev environment over HTTP, do not set Secure property on cookies. You should do this conditionally based on if the app is running in production mode. You should also include a property like SESSION_COOKIE_SECURE which can be used to disable Secure cookies when testing over HTTP.
  • SESSION_COOKIE_HTTPONLY=True
  • SESSION_COOKIE_SAMESITE='Lax' (or 'Strict' if compatible)
  • TRUSTED_HOSTS set in production
  • Security headers set (CSP, etc.) either in app or at the edge

4) Rules (generation + audit)

Each rule contains: required practice, insecure patterns, detection hints, and remediation.

FLASK-DEPLOY-001: Do not use Flask’s development server in production

Severity: High (if production)

Required:

  • MUST NOT deploy the built-in development server as the production server.
  • MUST run behind a production-grade WSGI server or managed platform (such as gunicorn)

Insecure patterns:

  • app.run(...) in a production entrypoint.
  • Deployment docs/scripts that use flask run in production.

Detection hints:

  • Search for app.run(, flask run, --debug, FLASK_DEBUG, FLASK_ENV=development.
  • Check Docker CMD/ENTRYPOINT, Procfile, systemd units, shell scripts.

Fix:

  • Use a production WSGI server (and keep Flask as the app object).
  • Ensure the dev server is only used for local development.

Note:

  • These are often used in dev mode or local testing. This is allowed. Only flag if it is clear that it is being used as the production entrypoint

FLASK-DEPLOY-002: Debug mode MUST be disabled in production

Severity: Critical

Required:

  • MUST NOT enable debug mode in production.
  • MUST treat the interactive debugger as equivalent to remote code execution if exposed.

Insecure patterns:

  • app.run(debug=True)
  • flask run --debug in production.
  • DEBUG=True via env/config in production.

Detection hints:

  • Look for debug=True, FLASK_DEBUG=1, DEBUG = True, app.debug = True.
  • Look for TRAP_HTTP_EXCEPTIONS/debugger settings enabled in non-test contexts.

Fix:

  • Ensure debug is only enabled in local dev/test.
  • Prefer environment-based toggles and safe defaults.

Note:

  • These are often used in dev mode or local testing. This is allowed. Only flag if it is clear that it is being used as the production entrypoint

FLASK-CONFIG-001: SECRET_KEY must be strong, secret, and rotated safely

Severity: High (Critical if missing in production with sessions or signing)

Required:

  • MUST set a strong random SECRET_KEY in production.
  • MUST keep SECRET_KEY out of source control and out of logs.
  • MAY rotate keys periodically; MAY use SECRET_KEY_FALLBACKS to support rotation without instantly invalidating existing sessions, then remove old keys after the rotation window. This likely is not needed for smaller applications but is good practice for larger applications. As this may complicate deployment, suggest that it be implemented rather than implementing it by default.

Insecure patterns:

  • Missing SECRET_KEY in production.
  • Hard-coded SECRET_KEY in repo (including test keys accidentally used in prod).
  • Logging or printing SECRET_KEY.

Detection hints:

  • Search for SECRET_KEY =, app.secret_key =, SECRET_KEY_FALLBACKS =.
  • Check .env files committed to repo.
  • Check config modules for constants.

Fix:

  • Load from secret manager or environment variable.
  • Add a rotation process:
    • Set new SECRET_KEY
    • Keep old key(s) temporarily in SECRET_KEY_FALLBACKS
    • Remove old key(s) after the safe window.

Notes:

  • If the application uses Flask sessions (cookie-based by default), SECRET_KEY is directly security-critical.

FLASK-SESS-001: Session cookies must use secure attributes in production

Severity: Medium

Required (production, HTTPS):

  • MUST set SESSION_COOKIE_SECURE=True (cookies only over HTTPS). NOTE: Only set Secure in production environment when TLS is configured. When running in a local dev environment over HTTP, do not set Secure property on cookies. You should do this conditionally based on if the app is running in production mode. You should also include a property like SESSION_COOKIE_SECURE which can be used to disable Secure cookies when testing over HTTP.
  • MUST ensure SESSION_COOKIE_HTTPONLY=True (protect from JS access).
  • SHOULD set SESSION_COOKIE_SAMESITE='Lax' (recommended) or 'Strict' if compatible with UX.
  • SHOULD keep SESSION_COOKIE_DOMAIN=None unless you explicitly need subdomain-wide cookies.
  • If you need embedded/iframe third-party usage, MAY consider SESSION_COOKIE_PARTITIONED=True (requires HTTPS).

Insecure patterns:

  • SESSION_COOKIE_SECURE=False in production.
  • SESSION_COOKIE_HTTPONLY=False.
  • SESSION_COOKIE_SAMESITE=None with cookie-authenticated state-changing endpoints (higher CSRF risk).

Detection hints:

  • Inspect app.config.update(...) blocks and config classes.
  • Look for set_cookie(..., secure=..., httponly=..., samesite=...) usage on non-session cookies too.

Fix:

  • Set these config values explicitly in production config.

Notes:

  • SameSite is defense-in-depth; do not treat it as a full replacement for CSRF tokens.

FLASK-SESS-002: Sessions must be bounded and resistant to fixation/replay

Severity: Medium

Required:

  • SHOULD set a bounded session lifetime appropriate to the app.
  • SHOULD set session.permanent = True only when you intend persistent sessions, and set PERMANENT_SESSION_LIFETIME to a justified value.
  • SHOULD clear the session on login and privilege changes to reduce session fixation risk.
  • MUST NOT store sensitive secrets in the default Flask session cookie. The default session is signed, not encrypted.

Insecure patterns:

  • Extremely long or unlimited lifetimes for privileged sessions.
  • No session clearing on login.
  • Storing secrets (passwords, access tokens, PII) directly in session[...] when using default cookie sessions.

Detection hints:

  • Search for PERMANENT_SESSION_LIFETIME, session.permanent, session[...] =.
  • Identify whether server-side session storage is used; if not, assume default cookie sessions.

Fix:

  • Set appropriate lifetimes.
  • Clear/rotate session on login.
  • Store sensitive data server-side; store only identifiers in the session cookie.

Severity: High

  • IMPORTANT NOTE: If cookies are not being used for auth (ie auth is via Authentication header or other passed token), then there is no CSRF risk.

Required:

  • MUST protect all state-changing endpoints (POST/PUT/PATCH/DELETE) that rely on cookies for authentication.
  • MAY use a well-tested CSRF library/integration (form framework or middleware) rather than rolling your own.
  • MAY use additional defenses (Origin/Referer checking, SameSite cookies, Fetch Metadata headers, custom headers for AJAX/API), but tokens remain the primary defense for cookie-authenticated apps. If tokens are impractical, or for small applications:
  • MUST at a minimum require a custom header to be set and set the session cookie SESSION_COOKIE_SAMESITE=lax, as this is the strongest method besides requiring a form token, and may be much easier to implement.

Insecure patterns:

  • Cookie-authenticated endpoints that change state with no CSRF protection.
  • Using GET for state-changing actions (amplifies CSRF risk).

Detection hints:

  • Enumerate routes with methods other than GET and identify auth mechanism.
  • Look for CSRF integrations (e.g., Flask-WTF, global CSRF middleware). If absent, treat as suspicious.
  • Check JSON API endpoints too, not only HTML forms.

Fix:

  • Add CSRF protection to all state-changing requests.
  • If the app is a pure API and uses Authorization headers (bearer tokens) rather than cookies, document that choice and ensure cookies aren’t used for auth. If cookies are not used for auth, there is no CSRF risk.

Notes:

  • XSS can defeat CSRF protections; CSRF defenses do not replace XSS prevention.

FLASK-XSS-001: Prevent reflected/stored XSS in templates and HTML generation

Severity: High

Required:

  • MUST rely on Jinja auto-escaping for HTML templates.
  • MUST NOT mark untrusted content as safe:
    • Avoid Markup(...) on user data.
    • Avoid Jinja |safe on user-controlled content.
  • MUST quote HTML attributes containing Jinja expressions (value="{{ x }}" not value={{ x }}).
  • MUST NOT serve uploaded HTML as active HTML; serve as download (Content-Disposition: attachment) or transform to a safe format. Note: This is only relevant if it is possible to upload document content such as html, js, css, etc. If it purely is image files, there is no concern.
  • SHOULD deploy a Content Security Policy (CSP) to mitigate XSS classes (including javascript: in href).

Insecure patterns:

  • Markup(request.args.get(...))
  • Template filters: {{ user_html|safe }}
  • Unquoted attributes in templates
  • Serving user-uploaded content directly with text/html or inline rendering

Detection hints:

  • Search for Markup( and investigate origin of the data.
  • Search template files for |safe, |tojson misuse, and unquoted attributes.
  • Review file-serving routes that might return user uploads without as_attachment=True. Note: This is only relevant if it is possible to upload document content such as html, js, css, etc. If it purely is image files, there is no concern.

Fix:

  • Remove unsafe marking; sanitize only when strictly necessary using a trusted HTML sanitizer.
  • Always quote attributes.
  • Add CSP and reduce inline scripts.

FLASK-SSTI-001: Never render untrusted templates (Server-Side Template Injection)

Severity: Critical

Required:

  • MUST NOT render templates that contain user-controlled template syntax.
  • MUST treat render_template_string and Environment.from_string(...).render(...) as dangerous if the template string is influenced by untrusted input.
  • MUST NOT use use .format() on user controlled strings
  • If untrusted templates are absolutely required, treat it as a special high-risk design:
    • MUST use a sandboxed templating approach and restrict capabilities.
    • MUST keep Jinja updated and assume sandbox escapes are possible; isolate further.

Insecure patterns:

  • render_template_string(request.args["tmpl"], ...)
  • Storing user templates in DB and rendering them with the normal Jinja environment.
  • request.args["tmpl"].format(...)

Detection hints:

  • Grep for render_template_string, from_string, .render( with dynamic strings.
  • Trace the origin of the template string (DB, request, uploads, admin panels).

Fix:

  • Replace with safe templating alternatives that do not evaluate code (e.g., string.Template, str.replace).
  • If templates must be user-defined, use a sandbox plus strict allowlists and heavy isolation.

FLASK-HEADERS-001: Set essential security headers (in app or at the edge)

Severity: Medium

Required (typical web app):

  • SHOULD set:
    • CSP (Content-Security-Policy)
    • X-Content-Type-Options: nosniff
    • Clickjacking protection (X-Frame-Options: SAMEORIGIN and/or CSP frame-ancestors) (there may be cases where the user wants to iframe their site elsewhere. If that is the case, work with them to safely allow it)
  • SHOULD consider additional hardening headers depending on app (Referrer-Policy, Permissions-Policy).
  • MUST ensure cookies are set with secure attributes (see FLASK-SESS-001).

NOTE: Security headers may be set via a proxy or other cloud provider. Check to see if there is evidence of that.

Insecure patterns:

  • No security headers anywhere (app or edge).
  • CSP missing on apps that display untrusted content.

Detection hints:

  • Search for after_request hooks, Flask-Talisman usage, reverse proxy config.
  • If not visible in app code, flag as “verify at edge”.

Fix:

  • Set headers centrally (middleware / after_request) or via reverse proxy/CDN.
  • Keep CSP realistic and compatible; avoid unsafe-inline where possible.

FLASK-LIMITS-001: Request size and form parsing limits MUST be set appropriately

Severity: Low (Medium if file uploads / large bodies are possible)

Required:

  • SHOULD set and justify:
    • MAX_CONTENT_LENGTH (global maximum request bytes)
    • MAX_FORM_MEMORY_SIZE (max per non-file form field in multipart)
    • MAX_FORM_PARTS (max number of multipart fields)
  • MUST enforce additional limits at the reverse proxy / WSGI / platform level where possible.

Insecure patterns:

  • Unlimited request body sizes when handling uploads or user content.
  • Accepting arbitrarily large multipart forms or many fields.

Detection hints:

  • Inspect Flask config for these keys.
  • Inspect upload routes and APIs that accept large JSON.

Fix:

  • Set conservative defaults, override per-route only when needed.
  • Ensure large uploads use dedicated upload mechanisms.

FLASK-HOST-001: Host header must be validated in production

Severity: Low (depends on app’s use of external URLs)

Required:

  • MUST set TRUSTED_HOSTS in production to restrict accepted Host values.
  • MUST NOT rely on SERVER_NAME as a host restriction mechanism.

Insecure patterns:

  • TRUSTED_HOSTS unset in production.
  • Code that generates external URLs for emails/password resets without host validation.

Detection hints:

  • Find TRUSTED_HOSTS config usage.
  • Find url_for(..., _external=True) and check how host is determined.

Fix:

  • Set TRUSTED_HOSTS to your expected domains (and required subdomains).
  • Ensure external URL generation uses trusted host/scheme.

FLASK-PROXY-001: Reverse proxy trust must be configured correctly

Severity: Medium (High if relying on IPs for auth)

Required:

  • If behind a reverse proxy, MUST configure Flask/Werkzeug to trust forwarded headers only from the intended proxy.
  • MUST NOT blindly trust X-Forwarded-* headers from the open internet.

Insecure patterns:

  • ProxyFix applied with overly broad trust settings, or applied without understanding how many proxies are in front.
  • Relying on forwarded headers for scheme/host without validation.

Detection hints:

  • Search for ProxyFix.
  • Search for usage of request.remote_addr, request.scheme, request.host in security-sensitive logic.

Fix:

  • Configure ProxyFix (or platform-specific settings) with correct hop counts.
  • Keep TRUSTED_HOSTS in place even behind proxies.

FLASK-PATH-001: Prevent path traversal and unsafe file serving

Severity: High

Required:

  • MUST NOT pass user-controlled file paths to send_file or to direct file I/O.
  • MUST use safe file serving patterns:
    • send_from_directory for user-specified paths under a trusted base directory
    • safe_join for joining a trusted base directory with untrusted path components
    • secure_filename for uploaded filenames (and still generate your own unique storage name)
  • MUST ensure user uploads are not served as executable/active content (especially HTML).
  • SHOULD in general use safe_join over os.path.join for almost any filesystem path computations.

Insecure patterns:

  • send_file(request.args["path"])
  • open(os.path.join(base_dir, user_path)) where user_path is untrusted
  • Serving uploads from within a static web root without restrictions

Detection hints:

  • Search for send_file(, open(, os.path.join(, pathlib.Path(...)/... in file routes.
  • Identify where filenames come from (request args, DB, headers).

Fix:

  • Serve only from a non-user-controlled directory base.
  • Store uploads outside static roots; serve through controlled routes.
  • Always validate and normalize file identifiers.

Note: safe_join is imported from werkzeug.security


FLASK-UPLOAD-001: File uploads must be validated, stored safely, and served safely

Severity: High

Required:

  • MUST enforce upload size limits (app + edge).
  • MUST validate file type using allowlists and content checks (not only extension).
  • MUST store uploads outside executable/static roots when possible.
  • SHOULD generate server-side filenames (random IDs) and avoid trusting original names.
  • MUST serve potentially active formats safely (download attachment) unless explicitly intended.

Insecure patterns:

  • Accepting arbitrary file types and serving them back inline.
  • Using user-supplied filename as storage path.
  • Missing size/type validation.

Detection hints:

  • Look for request.files[...] handlers.
  • Check for secure_filename usage (and whether it’s combined with uniqueness).
  • Check where files are stored and how they are served.

Fix:

  • Implement allowlist validation + safe storage + safe serving.
  • Add scanning / quarantine if applicable.

FLASK-INJECT-001: Prevent SQL injection (use parameterized queries / ORM)

Severity: High

Required:

  • MUST use parameterized queries or an ORM that parameterizes under the hood.
  • MUST NOT build SQL by string concatenation / f-strings with untrusted input.

Insecure patterns:

  • f"SELECT ... WHERE id={request.args['id']}"
  • "... WHERE name = '%s'" % user_input

Detection hints:

  • Grep for SELECT, INSERT, UPDATE, DELETE strings in Python code.
  • Track untrusted data into DB execute calls.

Fix:

  • Replace with parameterized queries or ORM query APIs.
  • Validate types (e.g., int IDs) before querying.

FLASK-INJECT-002: Prevent OS command injection

Severity: Critical to High (depends on exposure)

Required:

  • MUST avoid executing shell commands with untrusted input.
  • If subprocess is necessary:
    • MUST pass args as a list (not a string)
    • MUST NOT use shell=True with attacker-influenced strings
    • SHOULD use strict allowlists for any variable component
  • If possible, use pure python or a python library rather than using a subprocess or system command
  • Do not assume that arguments to commands will be inherently safe even in shell=False. Commands may incorrectly process these arguments as command line flags or other trusted values.

Insecure patterns:

  • os.system(user_input)
  • subprocess.run(f"cmd {user}", shell=True)
  • Passing user strings into bash -c, sh -c, PowerShell, etc.

Detection hints:

  • Search for os.system, subprocess, Popen, shell=True.
  • Trace data from request/DB into these calls.

Fix:

  • Use library APIs instead of shell commands.
  • If unavoidable, hard-code the command and allowlist validated parameters. If supported by the subcommand, try to keep user values after -- to prevent them being processed as command line flags.

FLASK-SSRF-001: Prevent server-side request forgery (SSRF) in outbound HTTP

Severity: Medium

  • Note: For small stand alone projects this is less important. It is most important when deploying into an LAN or with other services listening on the same server.

Required:

  • MUST treat outbound requests to user-provided URLs as high risk.
  • SHOULD validate and restrict destinations (allowlist hosts/domains) for any user-influenced URL fetch.
  • SHOULD block access to:
    • localhost / private IP ranges / link-local addresses
    • cloud metadata endpoints
  • MUST NOT allow non http/https protocols (ie file: etc)
  • SHOULD set timeouts and restrict redirects.

Insecure patterns:

  • requests.get(request.args["url"])
  • Webhooks/preview/fetch endpoints that accept arbitrary URLs.

Detection hints:

  • Search for requests.get/post, httpx, urllib, aiohttp usage with untrusted URL sources.
  • Identify URL fetch features (preview, import, webhook tester).

Fix:

  • Ensure URLs are http or https (disallow file: or other protocols)
  • Enforce allowlists and network egress controls.
  • Add strict parsing and IP resolution checks; set timeouts; disable redirects if not needed.

FLASK-REDIRECT-001: Prevent open redirects

Severity: Low

Required:

  • MUST validate redirect targets derived from untrusted input (e.g., next, redirect, return_to).
  • SHOULD use allowlists of internal paths or known domains.
  • SHOULD prefer redirecting only to same-site relative paths.

Insecure patterns:

  • redirect(request.args.get("next")) with no validation.

Detection hints:

  • Search for redirect( and examine where location comes from.

Fix:

  • Only allow relative paths or allowlisted domains.
  • Fall back to a safe default if validation fails.

FLASK-HTTP-001: Use HTTP methods safely; do not change state via GET; avoid secrets in URLs

Severity: Medium

Required:

  • MUST NOT perform state-changing actions over GET.
  • MUST NOT put secrets in URLs (query strings are commonly logged and leaked via referrers).
  • SHOULD require POST/PUT/PATCH/DELETE for state change and apply CSRF protections when cookie-authenticated.

Insecure patterns:

  • /delete?id=... implemented as GET
  • Password reset tokens or API keys in query params

Detection hints:

  • Enumerate GET routes and inspect whether they mutate state.
  • Look for URL parameters named token, key, secret, password, etc.

Fix:

  • Move state changes to non-GET methods.
  • Move sensitive values to secure channels (POST bodies, headers) and protect them.

FLASK-CORS-001: CORS must be explicit and least-privilege

Severity: Medium (High if misconfigured with credentials)

Required:

  • If CORS is not needed, MUST keep it disabled.
  • If CORS is needed:
    • MUST allowlist trusted origins (do not reflect arbitrary origins).
    • MUST be careful with credentialed requests; do not combine broad origins with cookies.
    • SHOULD restrict allowed methods and headers.

Insecure patterns:

  • Access-Control-Allow-Origin: * paired with credentialed cookies or overly broad access.
  • Reflecting Origin without validation.
  • flask_cors.CORS(app) with permissive defaults.

Detection hints:

  • Search for flask_cors, CORS(, Access-Control-Allow-Origin.
  • Check for supports_credentials=True and wildcard origins.

Fix:

  • Use a strict origin allowlist and minimal methods/headers.
  • Ensure cookie-authenticated endpoints are not exposed cross-origin unless necessary.

FLASK-SUPPLY-001: Dependency and patch hygiene (focus on security-relevant deps)

Severity: Low

Required:

  • SHOULD pin and regularly update security-critical dependencies (Flask, Werkzeug, Jinja2, itsdangerous).
  • MUST respond to known security advisories promptly.

Audit focus example:

  • If running on Windows and using file serving with untrusted paths, ensure Werkzeug’s safe_join behavior is not vulnerable to Windows device-name edge cases.

Detection hints:

  • Check requirements.txt, lockfiles, and runtime environments.
  • Identify where security helpers are used (safe_join, send_from_directory).

Fix:

  • Upgrade to patched versions and add regression tests for the impacted behavior.

5) Practical scanning heuristics (how to “hunt”)

When actively scanning, use these high-signal patterns:

  • Dev server / debug:
    • app.run(, flask run, --debug, DEBUG=True, FLASK_DEBUG
  • Secrets:
    • SECRET_KEY, secret_key, .env committed, print(config)
  • Cookies / sessions:
    • SESSION_COOKIE_SECURE, SESSION_COOKIE_HTTPONLY, SESSION_COOKIE_SAMESITE
    • session[...] = with sensitive values
  • CSRF:
    • POST/PUT/PATCH/DELETE handlers without CSRF checks in cookie-authenticated apps
  • XSS/SSTI:
    • Markup(, |safe, unquoted attributes, render_template_string
  • Files:
    • send_file( with user-controlled path; open( on user path; os.path.join with untrusted
    • upload handlers using user filename for path
  • Injection:
    • SQL strings + string formatting into .execute(...)
    • subprocess.*, shell=True, os.system
  • SSRF:
    • requests.get/post or httpx with URL from request/DB
  • Redirect:
    • redirect(request.args.get("next"))
  • CORS:
    • flask_cors.CORS permissive configs; wildcard origins with credentials

Always try to confirm:

  • data origin (untrusted vs trusted)
  • sink type (template/SQL/subprocess/files/redirect/http)
  • protective controls present (validation, allowlists, middleware)

6) Sources (accessed 2026-01-26)

Primary framework documentation:

Werkzeug documentation & advisories:

OWASP Cheat Sheet Series:

Template safety references:

HTTP semantics:

#security

数据统计

总访客 -- 总访问 --
ESC
输入关键词开始搜索