{"version":3,"file":"useSanitizedContent.mjs","sources":["../../../../src/lib/security/hooks/useSanitizedContent.ts"],"sourcesContent":["/**\n * @fileoverview useSanitizedContent Hook\n * @module @/lib/security/hooks/useSanitizedContent\n *\n * React hook for XSS-safe content handling with automatic\n * sanitization, threat detection, and context-aware encoding.\n *\n * @author Harbor Security Team\n * @version 1.0.0\n */\n\nimport { useState, useEffect, useCallback, useMemo } from 'react';\nimport type {\n  HTMLEncodingContext,\n  SanitizationOptions,\n  ThreatDetail,\n  SafeHTMLResult,\n  UseSanitizedContentResult,\n} from '../types';\nimport {\n  sanitizeHTML,\n  encodeForContext,\n  detectDangerousContent,\n  isDangerous,\n  createSafeHTMLProps,\n} from '../xss-prevention';\n\n/**\n * Options for the useSanitizedContent hook\n */\nexport interface UseSanitizedContentOptions extends SanitizationOptions {\n  /** Whether to auto-sanitize on content change */\n  autoSanitize?: boolean;\n  /** Callback when threats are detected */\n  onThreatsDetected?: (threats: readonly ThreatDetail[]) => void;\n}\n\n/**\n * Hook for XSS-safe content handling\n *\n * Provides sanitized HTML content, threat detection, and\n * context-aware encoding utilities.\n *\n * @param content - The content to sanitize\n * @param options - Sanitization options\n * @returns Sanitized content and utilities\n *\n * @example\n * ```tsx\n * function UserContent({ html }: { html: string }) {\n *   const {\n *     sanitizedHTML,\n *     wasModified,\n *     threats,\n *     isSafe,\n *   } = useSanitizedContent(html);\n *\n *   if (!isSafe) {\n *     console.warn('Threats detected:', threats);\n *   }\n *\n *   return (\n *     <div>\n *       {wasModified && (\n *         <div className=\"warning\">Content was sanitized for safety</div>\n *       )}\n *       <div dangerouslySetInnerHTML={{ __html: sanitizedHTML }} />\n *     </div>\n *   );\n * }\n * ```\n */\nexport function useSanitizedContent(\n  content: string,\n  options: UseSanitizedContentOptions = {}\n): UseSanitizedContentResult {\n  const { autoSanitize = true, onThreatsDetected, ...sanitizationOptions } = options;\n\n  // State\n  const [sanitizedHTML, setSanitizedHTML] = useState<string>('');\n  const [wasModified, setWasModified] = useState(false);\n  const [threats, setThreats] = useState<readonly ThreatDetail[]>([]);\n\n  // Memoized sanitization options\n  const memoizedOptions = useMemo(\n    () => sanitizationOptions,\n    // eslint-disable-next-line react-hooks/exhaustive-deps\n    [JSON.stringify(sanitizationOptions)]\n  );\n\n  // Sanitize content effect\n  useEffect(() => {\n    if (!autoSanitize || !content) {\n      setSanitizedHTML('');\n      setWasModified(false);\n      setThreats([]);\n      return;\n    }\n\n    // Detect threats\n    const detectionResult = detectDangerousContent(content);\n    setThreats(detectionResult.details);\n\n    // Notify about threats\n    if (detectionResult.isDangerous && onThreatsDetected) {\n      onThreatsDetected(detectionResult.details);\n    }\n\n    // Sanitize\n    const result = sanitizeHTML(content, memoizedOptions);\n    setSanitizedHTML(result.html);\n    setWasModified(result.wasModified);\n  }, [content, autoSanitize, memoizedOptions, onThreatsDetected]);\n\n  // Manual sanitize function\n  const sanitize = useCallback(\n    (newContent: string, overrideOptions?: SanitizationOptions): SafeHTMLResult => {\n      const opts = { ...memoizedOptions, ...overrideOptions };\n      return sanitizeHTML(newContent, opts);\n    },\n    [memoizedOptions]\n  );\n\n  // Context-aware encode function\n  const encode = useCallback((text: string, context: HTMLEncodingContext): string => {\n    return encodeForContext(text, context);\n  }, []);\n\n  // Computed isSafe\n  const isSafe = threats.length === 0;\n\n  return {\n    sanitizedHTML,\n    wasModified,\n    threats,\n    isSafe,\n    sanitize,\n    encode,\n  };\n}\n\n/**\n * Hook for creating safe innerHTML props\n *\n * Convenience hook that returns props ready for React elements.\n *\n * @example\n * ```tsx\n * function RichText({ content }: { content: string }) {\n *   const { props, isSafe, warnings } = useSafeInnerHTML(content);\n *\n *   return (\n *     <div {...props} className=\"rich-text\" />\n *   );\n * }\n * ```\n */\nexport function useSafeInnerHTML(\n  content: string,\n  options?: SanitizationOptions\n): {\n  props: { dangerouslySetInnerHTML: { __html: string } };\n  isSafe: boolean;\n  warnings: readonly string[];\n} {\n  const [result, setResult] = useState<SafeHTMLResult>({\n    html: '',\n    wasModified: false,\n    removedItems: [],\n    warnings: [],\n  });\n\n  useEffect(() => {\n    if (!content) {\n      // Schedule state update to avoid cascading renders\n      Promise.resolve()\n        .then(() => {\n          setResult({\n            html: '',\n            wasModified: false,\n            removedItems: [],\n            warnings: [],\n          });\n        })\n        .catch(() => {\n          // Ignore errors\n        });\n      return;\n    }\n\n    const sanitized = sanitizeHTML(content, options);\n    // Schedule state update to avoid cascading renders\n    Promise.resolve()\n      .then(() => {\n        setResult(sanitized);\n      })\n      .catch(() => {\n        // Ignore errors\n      });\n  }, [content, options]);\n\n  const props = useMemo(\n    () => ({\n      dangerouslySetInnerHTML: { __html: result.html },\n    }),\n    [result.html]\n  );\n\n  return {\n    props,\n    isSafe: !isDangerous(content),\n    warnings: result.warnings,\n  };\n}\n\n/**\n * Hook for validating user input in real-time\n *\n * @example\n * ```tsx\n * function CommentInput() {\n *   const [value, setValue] = useState('');\n *   const { isValid, threats, preview } = useValidatedInput(value);\n *\n *   return (\n *     <div>\n *       <textarea\n *         value={value}\n *         onChange={(e) => setValue(e.target.value)}\n *         className={isValid ? '' : 'error'}\n *       />\n *       {!isValid && (\n *         <p className=\"warning\">\n *           Content contains potentially dangerous patterns\n *         </p>\n *       )}\n *       <div className=\"preview\">\n *         <strong>Preview:</strong>\n *         <div dangerouslySetInnerHTML={{ __html: preview }} />\n *       </div>\n *     </div>\n *   );\n * }\n * ```\n */\nexport function useValidatedInput(\n  input: string,\n  options?: SanitizationOptions\n): {\n  isValid: boolean;\n  threats: readonly ThreatDetail[];\n  preview: string;\n  sanitized: string;\n} {\n  const [state, setState] = useState({\n    isValid: true,\n    threats: [] as readonly ThreatDetail[],\n    preview: '',\n    sanitized: '',\n  });\n\n  useEffect(() => {\n    if (!input) {\n      // Schedule state update to avoid cascading renders\n      Promise.resolve()\n        .then(() => {\n          setState({\n            isValid: true,\n            threats: [],\n            preview: '',\n            sanitized: '',\n          });\n        })\n        .catch(() => {\n          // Ignore errors\n        });\n      return;\n    }\n\n    // Detect threats\n    const detection = detectDangerousContent(input);\n\n    // Sanitize for preview\n    const result = sanitizeHTML(input, options);\n\n    // Schedule state update to avoid cascading renders\n    Promise.resolve()\n      .then(() => {\n        setState({\n          isValid: !detection.isDangerous,\n          threats: detection.details,\n          preview: result.html,\n          sanitized: result.html,\n        });\n      })\n      .catch(() => {\n        // Ignore errors\n      });\n  }, [input, options]);\n\n  return state;\n}\n\n/**\n * Hook for safe text rendering (no HTML)\n *\n * Strips all HTML and encodes for safe display.\n *\n * @example\n * ```tsx\n * function PlainText({ content }: { content: string }) {\n *   const safeText = useSafeText(content);\n *\n *   return <p>{safeText}</p>;\n * }\n * ```\n */\nexport function useSafeText(content: string): string {\n  return useMemo(() => {\n    if (!content) return '';\n\n    const result = sanitizeHTML(content, { stripAllHtml: true });\n    return result.html;\n  }, [content]);\n}\n\n/**\n * Hook for context-aware encoding\n *\n * @example\n * ```tsx\n * function SearchHighlight({ term }: { term: string }) {\n *   const encode = useContextEncoder();\n *\n *   // Safe for HTML content\n *   const htmlSafe = encode(term, 'html-content');\n *\n *   // Safe for URL parameter\n *   const urlSafe = encode(term, 'url-param');\n *\n *   return (\n *     <a href={`/search?q=${urlSafe}`}>\n *       Search for: {htmlSafe}\n *     </a>\n *   );\n * }\n * ```\n */\nexport function useContextEncoder(): (content: string, context: HTMLEncodingContext) => string {\n  return useCallback((content: string, context: HTMLEncodingContext) => {\n    return encodeForContext(content, context);\n  }, []);\n}\n\n/**\n * Hook for creating safe HTML props with threat reporting\n *\n * @example\n * ```tsx\n * function ModeratedContent({ html }: { html: string }) {\n *   const { props, report } = useSafeHTMLWithReport(html, {\n *     onReport: (report) => analytics.track('xss_attempt', report),\n *   });\n *\n *   return <div {...props} />;\n * }\n * ```\n */\nexport function useSafeHTMLWithReport(\n  content: string,\n  options: {\n    sanitizationOptions?: SanitizationOptions;\n    onReport?: (report: {\n      originalContent: string;\n      threats: readonly ThreatDetail[];\n      sanitizedContent: string;\n      timestamp: number;\n    }) => void;\n  } = {}\n): {\n  props: ReturnType<typeof createSafeHTMLProps>;\n  report: {\n    hadThreats: boolean;\n    threatCount: number;\n    threatTypes: readonly string[];\n  };\n} {\n  const { sanitizationOptions, onReport } = options;\n\n  const [report, setReport] = useState({\n    hadThreats: false,\n    threatCount: 0,\n    threatTypes: [] as readonly string[],\n  });\n\n  const props = useMemo(() => {\n    if (!content) {\n      return { dangerouslySetInnerHTML: { __html: '' } };\n    }\n\n    // Detect threats\n    const detection = detectDangerousContent(content);\n\n    // Get sanitized props\n    const safeProps = createSafeHTMLProps(content, sanitizationOptions);\n\n    // Update report\n    const newReport = {\n      hadThreats: detection.isDangerous,\n      threatCount: detection.details.length,\n      threatTypes: [...new Set(detection.threats)],\n    };\n    setReport(newReport);\n\n    // Call report callback if threats detected (timestamp captured lazily)\n    if (detection.isDangerous && onReport != null) {\n      // Use a function to defer timestamp capture to avoid render-time side effects\n      queueMicrotask(() => {\n        onReport({\n          originalContent: content,\n          threats: detection.details,\n          sanitizedContent: safeProps.dangerouslySetInnerHTML.__html,\n          /* eslint-disable-next-line react-hooks/purity */\n          timestamp: Date.now(),\n        });\n      });\n    }\n\n    return safeProps;\n  }, [content, sanitizationOptions, onReport]);\n\n  return { props, report };\n}\n"],"names":["useSanitizedContent","content","options","autoSanitize","onThreatsDetected","sanitizationOptions","sanitizedHTML","setSanitizedHTML","useState","wasModified","setWasModified","threats","setThreats","memoizedOptions","useMemo","useEffect","detectionResult","detectDangerousContent","result","sanitizeHTML","sanitize","useCallback","newContent","overrideOptions","opts","encode","text","context","encodeForContext","isSafe","useSafeInnerHTML","setResult","sanitized","isDangerous","useValidatedInput","input","state","setState","detection","useSafeText","useContextEncoder","useSafeHTMLWithReport","onReport","report","setReport","safeProps","createSafeHTMLProps","newReport"],"mappings":"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"}