Skip to content
Prompt of the Day — Part 29 of 30

Prompt of the Day: Implement Input Sanitization for User Content

Written by claude-sonnet-4 · Edited by claude-sonnet-4
input-sanitizationxsssecuritydompurifyzodprompt-engineering

Part 29 of 30 -- Prompt of the Day

Every input field in your application is a door. Most users walk through it normally. A few will try to kick it open. Input sanitization is the lock -- it ensures that whatever comes through that door cannot execute as code, rewrite your page, or compromise your users. XSS (cross-site scripting) and injection attacks remain among the most exploited vulnerability classes in production software: CVE-2025-67991, disclosed in early 2025, demonstrated a reflected XSS flaw in a widely used WordPress plugin caused entirely by unsanitized URL parameters. Sanitizing user content is not optional -- it is the baseline.


The Prompt

You are a security-focused software engineer auditing this application for input sanitization vulnerabilities.

Step 1 -- Map every user input surface:
- Identify all form fields, text areas, search inputs, and comment boxes.
- Identify all URL parameters and query strings that get read and rendered anywhere in the UI.
- Identify file upload fields and any metadata (filename, alt text, description) that gets stored or displayed.
- Produce a list of each input point, the component or route that handles it, and whether its output is rendered as plain text, markdown, or raw HTML.

Step 2 -- Implement server-side validation with Zod:
- Define a Zod schema for every API route that accepts user input. Validate req.body, req.query, and req.params before any business logic runs.
- Use allowlist patterns: restrict string fields to expected character sets (e.g., /^[a-zA-Z0-9 _-]+$/ for usernames), enforce .min() and .max() lengths on all text fields, and use .url() with a protocol refine check for any URL inputs.
- Return a structured 400 response on validation failure -- never leak schema details in the error message.

Step 3 -- Add HTML sanitization for rendered content:
- For any user content rendered as HTML on the client, wrap it with DOMPurify.sanitize() before passing it to dangerouslySetInnerHTML. Never render raw user HTML without this step.
- For server-side rendering or email templates, use the sanitize-html package with an explicit allowlist of tags and attributes. Strip script tags, on* event handlers, and data: URIs by default.
- Treat content type separately: plain text fields should be escaped (not sanitized); markdown fields should be converted to HTML first, then sanitized with DOMPurify; rich HTML fields need a fully configured allowlist that preserves formatting tags while removing anything executable.

Step 4 -- Handle file upload metadata safely:
- Sanitize filenames: strip path traversal characters (../, ..\), remove null bytes, and normalize to a safe slug before storage.
- Validate MIME type on the server using the file's actual bytes (magic numbers), not the Content-Type header.
- Never render a raw filename in an HTML context without escaping it first.

Step 5 -- Add Content Security Policy headers:
- Set a Content-Security-Policy header that restricts script-src to 'self' (plus any known CDN hashes). Disallow inline scripts with 'unsafe-inline' removed.
- Add X-Content-Type-Options: nosniff and X-Frame-Options: DENY.
- Configure a CSP report-uri or report-to endpoint so violations surface in your monitoring.

After each step, show me the code changes and confirm that legitimate content (formatted text, safe links, images from known domains) is preserved while script tags, event handlers, and data URIs are stripped.

Why It Works

This prompt forces the AI to think in layers rather than patching one field at a time. By starting with an input surface audit, it prevents the most common failure mode: sanitizing the obvious fields while missing URL parameters, file metadata, or a secondary rendering path. Separating validation (Zod) from sanitization (DOMPurify / sanitize-html) is deliberate -- validation rejects malformed structure before it ever reaches the sanitizer, reducing attack surface at two independent checkpoints.

The instruction to treat content types differently -- plain text, markdown, and rich HTML -- is what separates a thorough implementation from a false-security one. Applying DOMPurify to a plain text field is unnecessary; failing to apply it before rendering markdown-converted HTML is a real vulnerability. The prompt names both libraries with known, battle-tested track records: DOMPurify, developed by Cure53 and relied on by thousands of production applications, uses the browser's own DOM parser to sanitize HTML with context-awareness that regex-based filters cannot replicate. The final CSP step provides defense-in-depth -- even if a sanitizer is misconfigured, a strict policy can block script execution at the browser level.


The Anti-Prompt

Do not use this:

Make my app safe from XSS. Sanitize the inputs.

Why it fails: This tells the AI nothing about where inputs exist, how they are rendered, or which library to use. The AI will likely add a single sanitization call to one obvious form and consider the task complete, leaving URL parameters, file upload metadata, and server-side rendering paths completely unprotected. It also gives no guidance on content type distinctions, so the AI may strip formatting from a rich text editor or, worse, skip sanitization on markdown fields because they "look like plain text." Vague security prompts produce vague security implementations.


Variations

Variation 1 -- Rich text editor with DOMPurify allowlist:

For the rich text editor component, configure DOMPurify with an explicit ALLOWED_TAGS list limited to block and inline formatting elements (p, br, strong, em, h1-h3, ul, ol, li, blockquote, a, pre, code). Set ALLOWED_ATTR to href, target, and rel only. Add a DOMPurify hook that enforces rel="noopener noreferrer" on all anchor tags and rejects any href that does not begin with https:// or mailto:.

Variation 2 -- Markdown content pipeline:

For fields that store user-authored markdown, implement a two-step pipeline: first convert markdown to HTML using a library like marked or remark, then immediately pass the resulting HTML string through DOMPurify.sanitize() with a restricted allowlist before any rendering. Never render the raw markdown-converted HTML without the sanitization step.

Variation 3 -- Next.js API route validation middleware:

Create a reusable validateRequest(schema) middleware for Next.js API routes that accepts a Zod schema, runs schema.safeParse() against { body, query, params }, replaces req.body and req.query with the parsed (and transformed) values on success, and returns a 400 with structured field-level errors on failure. Apply this middleware to every route that touches user-supplied data.

Real-World Context

The OWASP Top 10 for 2025 lists injection -- the family that includes XSS -- as a top-five web application risk, noting that applications are vulnerable whenever user-supplied data is not validated, filtered, or sanitized before use. In December 2025, a stored XSS vulnerability was disclosed in the widely used Tiptap rich text editor extension (CVE-2025-14284), caused by unsanitized user input in link handling -- exactly the kind of edge case that a thorough, content-type-aware sanitization pass catches. The pattern is consistent: vulnerabilities appear not where developers remember to sanitize, but in the places they forgot to think about.


Ask The Guild

Where has input sanitization bitten you in the wild -- or where have you found a sanitization gap in a codebase you inherited? Share your war story (or your narrowest near-miss) in the comments.

Copy A Prompt Next

Review and debug

If this article changed how you think about the problem, copy a prompt that turns that judgment into one safe, reviewable next step.

Matching public prompts

23

Keep the task scoped, copy the prompt, then inspect one reviewable diff before the agent continues.

Need the safest first move instead? Open the curated sample prompts before you browse the broader library.

Working With AI ToolsWorking With AI Tools

System Prompts — .cursorrules and CLAUDE.md Explained

Write system prompts that give AI persistent context about your project and preferences.

Preview
**Use this when you want the agent to draft your persistent project instructions:**
"Help me write a system prompt file for this project.
Tool target: [Cursor / Claude Code / both]
Project summary: [what the app does]
Stack: [frameworks, languages, key services]
Prompt Engineering

Turn this workflow advice into a durable operating system

Prompt and workflow posts are the quick win. The learning paths turn them into a durable operating model for tools, prompts, and agent supervision.

Best Next Path

Working With AI Tools

Explorer · Free

Turn ad hoc prompting into a repeatable workflow with better tool choice, stronger prompting, and safer day-to-day AI habits.

23 lessonsIncluded in the free Explorer plan

Need the free route first?

Start with Foundations for AI-Assisted Builders if you want the workflow and vocabulary before you dive into the deeper path above.

T

About Tom Hundley

Tom Hundley writes for builders who need stronger technical judgment around AI-assisted software work. The Guild turns production experience into public articles, copy-paste prompts, and structured learning paths that help non-software developers supervise AI agents more safely.

Do this next

Leave this article with one concrete move. Copy the matching prompt, or start with the path that teaches the safest next skill in sequence.