- DataTable: add overflowX auto to TableContainer + minWidth 600
- WAF events: tighten column widths (Time 150, Host 150, IP 140,
Method 60), add ellipsis+tooltip on Host column, let Rule Message
expand to fill remaining space
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Coraza does not write matched rules to the audit log (known upstream
bug). Rule details are logged by Caddy's http.handlers.waf logger.
Two changes:
1. caddy.ts: Always configure a dedicated Caddy log sink that writes
http.handlers.waf logger output to /logs/waf-rules.log as JSON.
2. waf-log-parser.ts: Before parsing the audit log, read the new
waf-rules.log to build a Map<unique_id, RuleInfo>. Each audit log
entry joins against this map via transaction.id to populate
ruleId, ruleMessage, and severity fields. Skips anomaly evaluation
rules (949110/980130) to show the actual detection rule instead.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Coraza does not populate the messages array in the audit log JSON
(known bug — matched rules appear in Caddy's error log but not in
the audit log's messages field). The transaction.is_interrupted flag
IS correctly set to true for blocked/detected requests.
Changes:
- Filter on tx.is_interrupted instead of entry.messages.length
- Check both top-level and tx.messages for rule info (future compat)
- Fall back to tx.highest_severity when messages missing
- Document the Coraza messages bug in interface comments
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Coraza's RelevantOnly mode does not write audit log entries for requests
blocked by the WAF itself (403 responses), so the waf-log-parser had
nothing to parse. Reverting to On so all transactions are logged, and
relying on the parser-side messages[] filter to skip clean requests.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Change SecAuditEngine from On to RelevantOnly so Coraza only writes
audit log entries for transactions that triggered at least one rule.
Previously all requests were logged regardless of matches.
- Add parser-side guard to skip entries with empty messages array as
belt-and-suspenders against any pre-existing clean entries in log.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
load_owasp_crs: true only merges the embedded coraza-coreruleset
filesystem - it does NOT auto-include rule files. The correct way to
load CRS rules is to explicitly Include them using the @ prefix which
references the embedded FS:
Include @coraza.conf-recommended
Include @crs-setup.conf.example
Include @owasp_crs/*.conf
Without these includes, SecRuleEngine On had no rules to apply and
all requests passed through unblocked (rulesets: null in audit log).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The Caddyfile adapter test confirms: load_owasp_crs loads all CRS rules
internally without any Include directives. Include @owasp_crs/... was
wrong — that path is not accessible from SecLang directives.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
load_owasp_crs:true mounts the embedded CRS filesystem (@owasp_crs prefix),
but Include @owasp_crs/... directives are still needed to actually load the
rules. Previously we had one or the other — now both are set together.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The coraza-caddy Go struct defines directives as type string, not []string.
Revert to joined string but keep the Include @owasp_crs/... fix for CRS loading.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The enabled switch state was never submitted to the form, so the host
WAF config was always saved as enabled: false regardless of the toggle.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- buildWafHandler: directives must be string[] not a joined string (coraza-caddy
JSON API requirement); load_owasp_crs is Caddyfile-only and silently ignored in
JSON config — replaced with Include @owasp_crs/... directives
- waf-log-parser: use unix_timestamp (nanoseconds) for precise ts; host header is
headers.host[] (lowercase array); messages[].data.{id,msg,severity} not rule.*
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Previously only counted rows in the certificates table (0 for most setups).
Now counts: proxy hosts with certificate_id=NULL (each gets one Caddy ACME cert
via HTTP-01/DNS-01) + imported certs with actual PEM data.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
SwapHorizIcon→Proxy Hosts, VpnKeyIcon→Access Lists, SecurityIcon→Certificates,
HistoryIcon→Audit Log. Nav now uses the exact same icons as the overview stat
cards so identical sections share identical icons.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
DnsIcon→RouterIcon, SecurityIcon→LockIcon, ShieldIcon→WorkspacePremiumIcon,
HistoryIcon→AssignmentIcon — all now match the visual weight of Dashboard,
BarChart, and Settings icons.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replace DeleteOutline→Delete, CheckCircleOutline→CheckCircle, ErrorOutline→Error,
RemoveCircleOutline→RemoveCircle, InfoOutlined→Info across all dashboard components.
Replace custom SVG bar chart in OverviewClient with BarChartIcon.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Revert 2-column squircle grid back to original list layout —
same space usage as before, just with squircle-style border radius
on each ListItemButton instead of sharp rectangle edges.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replace full-width ListItemButton rows with a 2-column grid of
square tiles (borderRadius: 22%, aspectRatio: 1) containing a
centred icon and small label. Active tile uses primary.main fill.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replace minWidth/maxWidth with a fixed width:260 + flexShrink:0 so the
Autocomplete never resizes the header row. Chip labels also truncate
with ellipsis instead of wrapping.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
renderTags now shows individual chips for 1-2 selections, and a single
'N hosts' chip (with clear-all × button) for 3+. Prevents the input
from growing vertically when many hosts are selected.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- PaperComponent header with "Select all" / "Clear" buttons; onMouseDown
preventDefault keeps the popup open when clicking them
- ListboxProps overscrollBehavior: contain prevents the dropdown list's
scroll from propagating to the page when reaching top/bottom
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>