Files
tm-landing/USAGE_EXAMPLES.md
T
AmirReza Jamali 3e5e135b47 feat(security): Add XSS protection with DOMPurify sanitization
- Add sanitize.ts utility module with sanitizeText(), sanitizeUrl(), and sanitizeHtml() functions
- Implement server-side HTML entity escaping and client-side DOMPurify sanitization
- Add comprehensive test suite for sanitization functions in lib/__tests__/sanitize.test.ts
- Secure marketing pages by sanitizing all CMS data (hero, features, challenges, advantages, footer)
- Secure inquiries form by sanitizing field labels, placeholders, and button text
- Add SECURITY.md documentation outlining XSS protection measures and best practices
- Add USAGE_EXAMPLES.md with detailed examples of sanitization function usage
- Add XSS_PROTECTION_SUMMARY.md with implementation overview
- Update package.json to include dompurify (^3.3.0) dependency
- Protects against common XSS attack vectors including script injection, event handlers, and malicious URLs
2025-11-30 11:14:42 +03:30

5.4 KiB

XSS Protection Usage Examples

Basic Usage

Sanitizing Text Content

import { sanitizeText } from "@/lib/sanitize";

// In a component
function MyComponent({ cmsData }) {
  return (
    <div>
      <h1>{sanitizeText(cmsData.title)}</h1>
      <p>{sanitizeText(cmsData.description)}</p>
    </div>
  );
}

Sanitizing URLs

import { sanitizeUrl } from "@/lib/sanitize";
import Link from "next/link";
import Image from "next/image";

function MyComponent({ cmsData }) {
  return (
    <div>
      {/* For links */}
      <Link href={sanitizeUrl(cmsData.link) || "#"}>Click here</Link>

      {/* For images */}
      <Image
        src={sanitizeUrl(cmsData.imageUrl) || "/fallback.svg"}
        alt={sanitizeText(cmsData.imageAlt)}
        width={100}
        height={100}
      />
    </div>
  );
}

Sanitizing HTML Content

import { sanitizeHtml } from "@/lib/sanitize";

function MyComponent({ cmsData }) {
  return (
    <div>
      {/* With default allowed tags (b, i, em, strong, a, br, p) */}
      <div
        dangerouslySetInnerHTML={{
          __html: sanitizeHtml(cmsData.richContent),
        }}
      />

      {/* With custom allowed tags */}
      <div
        dangerouslySetInnerHTML={{
          __html: sanitizeHtml(cmsData.richContent, ["p", "br", "strong"]),
        }}
      />
    </div>
  );
}

Real-World Examples

Example 1: Dynamic Marketing Page

import { sanitizeText, sanitizeUrl } from "@/lib/sanitize";

export default function MarketingPage({ cmsData }) {
  return (
    <main>
      <section>
        <h1>{sanitizeText(cmsData.hero.title)}</h1>
        <p>{sanitizeText(cmsData.hero.description)}</p>
        <Link href={sanitizeUrl(cmsData.hero.buttonLink) || "#"}>
          {sanitizeText(cmsData.hero.buttonText)}
        </Link>
      </section>

      <section>
        {cmsData.features.map((feature) => (
          <article key={feature.id}>
            <Image
              src={sanitizeUrl(feature.icon) || "/default-icon.svg"}
              alt={sanitizeText(feature.title)}
              width={48}
              height={48}
            />
            <h3>{sanitizeText(feature.title)}</h3>
            <p>{sanitizeText(feature.description)}</p>
          </article>
        ))}
      </section>
    </main>
  );
}

Example 2: Dynamic Form Fields

import { sanitizeText } from "@/lib/sanitize";
import { useForm } from "react-hook-form";

export default function DynamicForm({ contactData }) {
  const { register, handleSubmit } = useForm();

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      {contactData.fields.map((field) => (
        <div key={field.name}>
          <label htmlFor={field.name}>{sanitizeText(field.label)}</label>
          <input
            id={field.name}
            placeholder={sanitizeText(field.placeholder)}
            {...register(field.name)}
          />
        </div>
      ))}
      <button type="submit">
        {sanitizeText(contactData.submitButtonText)}
      </button>
    </form>
  );
}

Example 3: User-Generated Content

import { sanitizeHtml } from "@/lib/sanitize";

export default function CommentSection({ comments }) {
  return (
    <div>
      {comments.map((comment) => (
        <article key={comment.id}>
          <h4>{sanitizeText(comment.author)}</h4>
          {/* Allow only basic formatting tags */}
          <div
            dangerouslySetInnerHTML={{
              __html: sanitizeHtml(comment.content, [
                "p",
                "br",
                "em",
                "strong",
              ]),
            }}
          />
        </article>
      ))}
    </div>
  );
}

Common XSS Attack Vectors (All Blocked)

// These malicious inputs will be sanitized:

// 1. Script injection
const xss1 = "<script>alert('XSS')</script>";
sanitizeText(xss1); // Safe: &lt;script&gt;alert('XSS')&lt;/script&gt;

// 2. Event handler injection
const xss2 = "<img src=x onerror=alert('XSS')>";
sanitizeText(xss2); // Safe: &lt;img src=x onerror=alert('XSS')&gt;

// 3. JavaScript URL
const xss3 = "javascript:alert('XSS')";
sanitizeUrl(xss3); // Returns: ""

// 4. Data URL with script
const xss4 = "data:text/html,<script>alert('XSS')</script>";
sanitizeUrl(xss4); // Returns: ""

// 5. Iframe injection
const xss5 = "<iframe src='javascript:alert(1)'></iframe>";
sanitizeText(xss5); // Safe: &lt;iframe src='javascript:alert(1)'&gt;&lt;/iframe&gt;

Best Practices

  1. Always sanitize external data before rendering
  2. Use the appropriate function for the content type
  3. Provide fallback values for URLs and optional content
  4. Never trust user input or external API data
  5. Keep dependencies updated to get latest security patches

When NOT to Sanitize

  • Static content defined in your codebase
  • Environment variables (but validate them)
  • Data from trusted internal APIs (but still validate)
  • Constants and configuration values

Testing Your Sanitization

// Test with malicious input
const testData = {
  title: "<script>alert('XSS')</script>Hello",
  link: "javascript:alert('XSS')",
  description: "<img src=x onerror=alert('XSS')>",
};

// All should be safe
console.log(sanitizeText(testData.title)); // No script execution
console.log(sanitizeUrl(testData.link)); // Empty string
console.log(sanitizeText(testData.description)); // No image/script