feat(waf): CRS-App-Exclusion-Plugins (Nextcloud/WordPress/Drupal) pro Domain — v1.3.12

Statt manueller SecRuleRemoveById-IDs kann man pro Domain offizielle OWASP-CRS-
Exclusion-Plugins aktivieren — pfad-genaue, upstream-gepflegte App-Ausnahmen.

- Migration 0046: waf_configs.crs_plugins text[].
- Engine (engine.go): je gewähltem Plugin werden config/before VOR den CRS-Rules
  und after DANACH inkludiert (exakt nach OWASP-CRS-Plugin-Spec); nur die für
  DIESE Domain gewählten, nur wenn die Datei existiert. Whitelist KnownCRSPlugins.
- Handler: crs_plugins im Upsert-Body + Whitelist-Validierung (Include-Pfad-
  Injection-Schutz).
- Packaging (postinst): lädt die Plugins (coreruleset/<name>-plugin) nach
  <crs>/plugins/ — self-healing auf jedem configure, nur fehlende.
- UI: Multi-Select „App-Profile (CRS-Plugins)" im WAF-Config-Drawer.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Debian
2026-08-03 10:28:03 +02:00
parent 088910ee19
commit 37f729381d
11 changed files with 220 additions and 19 deletions

View File

@@ -1934,6 +1934,9 @@
"enabled": "Aktiviert",
"mode": "Modus",
"paranoia": "Paranoia-Level",
"crsPlugins": "App-Profile (CRS-Plugins)",
"crsPluginsHint": "Offizielle OWASP-CRS-Exclusion-Plugins für bekannte Apps — deaktivieren automatisch die typischen False-Positive-Regeln pfad-genau (z.B. Nextcloud-WebDAV, WordPress-Editor). Sauberer als manuelle Regel-IDs.",
"crsPluginsPlaceholder": "App-Profile wählen (optional)",
"exclusions": "Regel-Ausnahmen",
"exclusionsHint": "Kommagetrennte Regel-IDs die deaktiviert werden (z.B. 920350, 941130).",
"trustedProxies": "Vertrauenswürdige Proxys",

View File

@@ -1934,6 +1934,9 @@
"enabled": "Enabled",
"mode": "Mode",
"paranoia": "Paranoia Level",
"crsPlugins": "App profiles (CRS plugins)",
"crsPluginsHint": "Official OWASP CRS exclusion plugins for well-known apps — automatically disable the typical false-positive rules in a path-scoped way (e.g. Nextcloud WebDAV, WordPress editor). Cleaner than manual rule IDs.",
"crsPluginsPlaceholder": "Select app profiles (optional)",
"exclusions": "Rule Exclusions",
"exclusionsHint": "Comma-separated rule IDs to disable (e.g. 920350, 941130).",
"trustedProxies": "Trusted Proxies",

View File

@@ -33,11 +33,19 @@ interface WafConfig {
mode: 'detection' | 'blocking'
paranoia_level: number
rule_exclusions: string[]
crs_plugins: string[]
exclusion_notes: Record<string, string>
trusted_proxies: string[]
custom_rules: string
}
// CRS-App-Exclusion-Plugins — muss zur Backend-Whitelist (KnownCRSPlugins) passen.
const CRS_PLUGIN_OPTIONS = [
{ value: 'nextcloud', label: 'Nextcloud' },
{ value: 'wordpress', label: 'WordPress' },
{ value: 'drupal', label: 'Drupal' },
]
// ---------- API helpers -----------------------------------------------------
async function fetchDomains(): Promise<Domain[]> {
@@ -65,6 +73,7 @@ function defaultConfig(domainId: number): WafConfig {
mode: 'detection',
paranoia_level: 1,
rule_exclusions: [],
crs_plugins: [],
exclusion_notes: {},
trusted_proxies: [],
custom_rules: '',
@@ -75,6 +84,7 @@ interface WafFormValues {
enabled: boolean
mode: 'detection' | 'blocking'
paranoia_level: number
crs_plugins: string[]
trusted_proxies_str: string
custom_rules: string
}
@@ -147,6 +157,7 @@ function ConfigDrawer({ domainName, domainId, onClose }: ConfigDrawerProps) {
mode: vals.mode,
paranoia_level: vals.paranoia_level,
rule_exclusions: cfg?.rule_exclusions ?? [],
crs_plugins: vals.crs_plugins ?? [],
exclusion_notes: cfg?.exclusion_notes ?? {},
trusted_proxies: proxies,
custom_rules: vals.custom_rules ?? '',
@@ -190,6 +201,20 @@ function ConfigDrawer({ domainName, domainId, onClose }: ConfigDrawerProps) {
</Select>
</Form.Item>
<Form.Item
label={t('waf.config.crsPlugins')}
name="crs_plugins"
help={t('waf.config.crsPluginsHint')}
>
<Select
mode="multiple"
allowClear
disabled={isViewer}
placeholder={t('waf.config.crsPluginsPlaceholder')}
options={CRS_PLUGIN_OPTIONS}
/>
</Form.Item>
{/* Exclusions list — shows existing exclusions with notes + remove button */}
<Form.Item label={t('waf.config.exclusions')}>
{(cfg?.rule_exclusions ?? []).length === 0 ? (
@@ -224,6 +249,7 @@ function ConfigDrawer({ domainName, domainId, onClose }: ConfigDrawerProps) {
mode: cfg?.mode ?? 'detection',
paranoia_level: cfg?.paranoia_level ?? 1,
rule_exclusions: newExclusions,
crs_plugins: cfg?.crs_plugins ?? [],
exclusion_notes: newNotes,
trusted_proxies: cfg?.trusted_proxies ?? [],
custom_rules: cfg?.custom_rules ?? '',