feat(waf): ausgeschlossene Regeln grün + Bereits-Ausnahme-Tag — v1.2.84
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1833,7 +1833,8 @@
|
|||||||
"noDomain": "Domain nicht gefunden — Ausnahme manuell konfigurieren.",
|
"noDomain": "Domain nicht gefunden — Ausnahme manuell konfigurieren.",
|
||||||
"exceptionModalTitle": "Ausnahme für Regel {{rule}} hinzufügen",
|
"exceptionModalTitle": "Ausnahme für Regel {{rule}} hinzufügen",
|
||||||
"exceptionModalHint": "Optional: Begründung warum diese Regel ein False Positive für diese Domain ist.",
|
"exceptionModalHint": "Optional: Begründung warum diese Regel ein False Positive für diese Domain ist.",
|
||||||
"exceptionNotePlaceholder": "z.B. Unsere API verwendet nicht-standardisierte Header die diese Regel auslösen."
|
"exceptionNotePlaceholder": "z.B. Unsere API verwendet nicht-standardisierte Header die diese Regel auslösen.",
|
||||||
|
"alreadyExcluded": "Bereits Ausnahme"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1833,7 +1833,8 @@
|
|||||||
"noDomain": "Domain not found — configure exception manually.",
|
"noDomain": "Domain not found — configure exception manually.",
|
||||||
"exceptionModalTitle": "Add exception for rule {{rule}}",
|
"exceptionModalTitle": "Add exception for rule {{rule}}",
|
||||||
"exceptionModalHint": "Optional: describe why this rule is a false positive for this domain.",
|
"exceptionModalHint": "Optional: describe why this rule is a false positive for this domain.",
|
||||||
"exceptionNotePlaceholder": "e.g. Our custom API uses non-standard headers that trigger this rule."
|
"exceptionNotePlaceholder": "e.g. Our custom API uses non-standard headers that trigger this rule.",
|
||||||
|
"alreadyExcluded": "Already excluded"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -292,7 +292,7 @@ async function fetchAlerts(domainId?: number): Promise<WafAlert[]> {
|
|||||||
return (r.data.data as { alerts?: WafAlert[] }).alerts ?? []
|
return (r.data.data as { alerts?: WafAlert[] }).alerts ?? []
|
||||||
}
|
}
|
||||||
|
|
||||||
function AlertsTab({ domainId }: { domainId?: number }) {
|
function AlertsTab({ domainId, configMap }: { domainId?: number; configMap: Map<number, WafConfig> }) {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const qc = useQueryClient()
|
const qc = useQueryClient()
|
||||||
const isViewer = useAuthStore((s) => s.user?.role) === 'viewer'
|
const isViewer = useAuthStore((s) => s.user?.role) === 'viewer'
|
||||||
@@ -388,6 +388,15 @@ function AlertsTab({ domainId }: { domainId?: number }) {
|
|||||||
key: 'exception',
|
key: 'exception',
|
||||||
width: 130,
|
width: 130,
|
||||||
render: (_: unknown, row: WafAlert) => {
|
render: (_: unknown, row: WafAlert) => {
|
||||||
|
const isExcluded = !!row.domain_id &&
|
||||||
|
(configMap.get(row.domain_id)?.rule_exclusions ?? []).includes(String(row.rule_id))
|
||||||
|
if (isExcluded) {
|
||||||
|
return (
|
||||||
|
<Tag color="success" icon={<CheckCircleOutlined />}>
|
||||||
|
{t('waf.alerts.alreadyExcluded')}
|
||||||
|
</Tag>
|
||||||
|
)
|
||||||
|
}
|
||||||
const canExclude = !!row.domain_id && row.rule_id > 0 && !isViewer
|
const canExclude = !!row.domain_id && row.rule_id > 0 && !isViewer
|
||||||
return (
|
return (
|
||||||
<Tooltip title={!row.domain_id ? t('waf.alerts.noDomain') : undefined}>
|
<Tooltip title={!row.domain_id ? t('waf.alerts.noDomain') : undefined}>
|
||||||
@@ -432,9 +441,13 @@ function AlertsTab({ domainId }: { domainId?: number }) {
|
|||||||
loading={isLoading}
|
loading={isLoading}
|
||||||
dataSource={alerts ?? []}
|
dataSource={alerts ?? []}
|
||||||
columns={columns}
|
columns={columns}
|
||||||
rowClassName={(row: WafAlert) =>
|
rowClassName={(row: WafAlert) => {
|
||||||
row.action === 'blocked' ? 'fw-rule-row--zero-hit' : ''
|
const isExcluded = !!row.domain_id &&
|
||||||
}
|
(configMap.get(row.domain_id)?.rule_exclusions ?? []).includes(String(row.rule_id))
|
||||||
|
if (isExcluded) return 'waf-alert-row--excluded'
|
||||||
|
if (row.action === 'blocked') return 'fw-rule-row--zero-hit'
|
||||||
|
return ''
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
@@ -642,7 +655,7 @@ export default function WAFPage() {
|
|||||||
{
|
{
|
||||||
key: 'alerts',
|
key: 'alerts',
|
||||||
label: t('waf.tabs.alerts'),
|
label: t('waf.tabs.alerts'),
|
||||||
children: <AlertsTab />,
|
children: <AlertsTab configMap={configMap} />,
|
||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -3057,6 +3057,15 @@ h1, h2, h3, h4, h5, h6 {
|
|||||||
border-radius: 0 !important;
|
border-radius: 0 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ── WAF Alert rows ──────────────────────────────────────────────────── */
|
||||||
|
.waf-alert-row--excluded > td {
|
||||||
|
background: #F0FDF4 !important;
|
||||||
|
color: #15803D !important;
|
||||||
|
}
|
||||||
|
.waf-alert-row--excluded:hover > td {
|
||||||
|
background: #DCFCE7 !important;
|
||||||
|
}
|
||||||
|
|
||||||
/* ── Firewall Filter-Bar ─────────────────────────────────────────────── */
|
/* ── Firewall Filter-Bar ─────────────────────────────────────────────── */
|
||||||
.fw-filter-bar {
|
.fw-filter-bar {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|||||||
Reference in New Issue
Block a user