"use client"; import { Button } from "@/components/ui/button"; import { Checkbox } from "@/components/ui/checkbox"; import { Switch } from "@/components/ui/switch"; import { Textarea } from "@/components/ui/textarea"; import { cn } from "@/lib/utils"; import { ChevronDown, ClipboardCopy, ShieldOff } from "lucide-react"; import { useState } from "react"; import { type WafHostConfig } from "@/lib/models/proxy-hosts"; import { WafRuleExclusions } from "./WafRuleExclusions"; type WafMode = "merge" | "override"; type EngineMode = "Off" | "On" | "inherit"; const QUICK_TEMPLATES = [ { label: "Allow IP", snippet: `SecRule REMOTE_ADDR "@ipMatch 1.2.3.4" "id:9000,phase:1,allow,nolog,msg:'Allow IP'"` }, { label: "Disable WAF for path", snippet: `SecRule REQUEST_URI "@beginsWith /api/" "id:9001,phase:1,ctl:ruleEngine=Off,nolog"` }, { label: "Remove XSS rules", snippet: `SecRuleRemoveByTag "attack-xss"` }, { label: "Block User-Agent", snippet: `SecRule REQUEST_HEADERS:User-Agent "@contains badbot" "id:9002,phase:1,deny,status:403,log"` }, ]; type Props = { value?: WafHostConfig | null; showModeSelector?: boolean; }; export function WafFields({ value, showModeSelector = true }: Props) { const [enabled, setEnabled] = useState(value?.enabled ?? false); const [wafMode, setWafMode] = useState(value?.waf_mode ?? "merge"); const [engineMode, setEngineMode] = useState( value?.mode === "Off" || value?.mode === "On" ? value.mode : "inherit" ); const [loadCrs, setLoadCrs] = useState(value?.load_owasp_crs ?? true); const [customDirectives, setCustomDirectives] = useState(value?.custom_directives ?? ""); const [showTemplates, setShowTemplates] = useState(false); return (
{/* Header */}

Web Application Firewall

Inspect and block malicious requests via Coraza / OWASP CRS

{/* Expanded content */}
{/* Override mode selector */} {showModeSelector && ( <>
{(["merge", "override"] as WafMode[]).map((v) => (
setWafMode(v)} className={cn( "flex-1 py-2 px-3 rounded-xl border-[1.5px] cursor-pointer text-center transition-all duration-150 select-none", wafMode === v ? "border-destructive bg-destructive/10" : "border-border hover:border-muted-foreground" )} >

{v === "merge" ? "Merge with global" : "Override global"}

))}
)} {!showModeSelector &&
} {/* Engine mode */} Engine Mode
{(["inherit", "Off", "On"] as EngineMode[]).map((v) => (
setEngineMode(v)} className={cn( "flex-1 py-2 px-2 rounded-xl border-[1.5px] cursor-pointer text-center transition-all duration-150 select-none", engineMode === v ? "border-destructive bg-destructive/10" : "border-border hover:border-muted-foreground" )} >

{v === "inherit" ? "Global default" : v}

))}
{/* OWASP CRS */}
setLoadCrs(!!checked)} />
{/* Excluded rule IDs */}
{/* Custom directives */}