- Move WAF config (enable, CRS, custom directives, templates) from
Settings page into a new Settings tab on the WAF page
- WAF page now has three tabs: Events | Suppressed Rules | Settings
- Rename nav item from "WAF Events" to "WAF", route /waf-events → /waf
- Fix excluded_rule_ids preservation: no longer wiped when form field
is absent (Settings tab omits the hidden field intentionally)
- Allow pre-adding suppressed rules even when WAF is disabled
- Reorder sidebar: Overview, Proxy Hosts, Access Lists, Certificates,
WAF, Analytics, Audit Log, Settings
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add lookupWafRuleMessageAction server action — queries WAF event
history for a known message for any rule ID
- Suppressed Rules tab: type a rule ID, look it up to see its
description (or a "not triggered yet" note), confirm to suppress
- Duplicate-guard: looking up an already-suppressed rule shows an error
- Search field filters the suppressed list by rule ID or message text
- Newly added rules show their message immediately without page reload
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- IssuedCertsPanel preview: only show active (non-revoked) certs
- ManageIssuedClientCertsDialog: filter out revoked by default; show
"Show revoked (N)" toggle when revoked certs exist
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Split ACME / Imported / CA-mTLS into tabs with count badges
- Add clickable status summary bar (expired / expiring soon / healthy)
- Per-tab search filter by name and domain
- Replace accordion cards with DataTable for imported certs
- Slide-in Drawers (480 px) for add/edit imported and CA certs
- File upload + show/hide toggle for private key in ImportCertDrawer
- CaCertDrawer: Generate / Import PEM tabs for add, simple form for edit
- CA tab: expandable rows showing issued client certs inline
- RelativeTime component: "in 45 days" / "EXPIRED 3 days ago" with date tooltip
- Remove CreateCaCertDialog and EditCaCertDialog (replaced by CaCertDrawer)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Two bugs fixed:
1. buildClientAuthentication was placing issued leaf cert PEMs into
trusted_ca_certs. Caddy uses that field for CA chain validation, not
leaf pinning — putting leaf certs there made chain verification fail
for every presented client cert, causing the browser to be asked
repeatedly. Fixed by using trusted_leaf_certs for managed CAs.
2. If all issued certs for a CA were revoked, the active cert map would
be empty and the code fell back to trusting the CA cert directly,
effectively un-revoking everything. Fixed by tracking which CAs have
ever had issued certs (including revoked) and keeping them in
trusted_leaf_certs mode permanently (empty list = no one trusted).
Also fix CA certificate delete action not surfacing the error message
to the user in production (Next.js strips thrown error messages in
server actions). Changed to return { success, error } and updated the
client dialog to check the result instead of using try/catch.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
With DetectionOnly removed, the global WAF had two redundant controls:
an Enable toggle and an Off/On radio, both doing the same thing. Collapse
them into a single labelled switch. Mode is now derived from the enabled
state in the action rather than being a separate form field.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
DetectionOnly was fundamentally broken in coraza-caddy (actually blocks
requests via anomaly scoring), caused massive audit log flooding, and the
threshold workaround had several issues:
- t:none is meaningless in a SecAction (no target to transform)
- SecRuleEngine directive ordering relative to SecAction is implementation-
defined, making the override fragile
- host.mode ?? 'DetectionOnly' fallbacks silently gave any host without an
explicit mode the broken DetectionOnly behaviour
Changes:
- Remove DetectionOnly from UI (global settings radio, per-host engine mode)
- Coerce legacy DB values of 'DetectionOnly' to 'On' in buildWafHandler
- Fix fallback defaults: host.mode ?? 'DetectionOnly' → host.mode ?? 'On'
- Fix action parsers: unknown mode defaults to 'On' (was 'DetectionOnly')
- Fix global settings defaultValue: ?? 'DetectionOnly' → ?? 'On' (or 'Off')
- Remove the fragile threshold SecAction workaround
- Update types: mode is now 'Off' | 'On' throughout
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- DetectionOnly mode: add SecAction to set anomaly score thresholds to
9999999 so rule 949110/980130 never fires; works around coraza-caddy
bug where is_interrupted=true still causes a 403 in detection mode
- Switch SecAuditEngine back to On (from RelevantOnly) so DetectionOnly
hits are captured, now safe because body parts are excluded
- SecAuditLogParts: ABIJDEFHZ → ABFHZ, dropping request body (I),
multipart files (J), intermediate response headers (D), and response
body (E) — prevents multi-MB payloads being written to audit log
- Parser: store both blocked and detected events; filter on rule matched
OR is_interrupted instead of is_interrupted only
- Add blocked column to waf_events (migration 0014); existing rows
default to blocked=true
- WAF Events UI: Blocked/Detected chip in table and drawer header
- Fix misleading help text that said to use Detection Only to observe
traffic before blocking
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- New `ca_certificates` table for reusable CA certs (migration 0011)
- CA cert CRUD model, server actions, and UI dialogs
- Proxy host create/edit dialogs include mTLS toggle + CA cert selection
- Caddy config generates `client_authentication` TLS policy blocks with
`require_and_verify` mode for hosts with mTLS enabled
- CA certs sync to slave instances via instance-sync payload
- Certificates page shows CA Certificates section
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds checkSameOrigin() helper in auth.ts that validates the Origin header
against the Host header. If Origin is present and mismatched, returns 403.
Applied to all 5 custom POST routes flagged in CPM-003 (NEXT-CSRF-001):
- change-password, link-oauth-start, unlink-oauth, update-avatar, logout
SameSite=Lax (NextAuth default) already blocks standard cross-site CSRF;
this adds defense-in-depth against subdomain and misconfiguration scenarios.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add getTopWafRulesWithHosts() and getWafEventCountries() model queries
- WAF stats API now returns topRules with per-host breakdown and byCountry
- Analytics: replace WAF rules table with bar chart + host chip details
- Analytics: add WAF column (amber) to Top Countries table
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- WafEvent model: expose rawData field from DB
- DataTable: add optional onRowClick prop with hover cursor
- WafEventsClient: clicking a row opens a right-side drawer showing
all event fields plus the raw Coraza audit JSON (pretty-printed)
Safety: rawData is rendered via JSON.stringify into a <pre> element,
never via dangerouslySetInnerHTML, so attack payloads are displayed
as inert text.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- 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>
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>
- Replace native datetime-local inputs with @mui/x-date-pickers DateTimePicker
(proper dark-themed calendar popover with time picker, DD/MM/YYYY HH:mm format,
min/max constraints between pickers, 24h clock)
- Replace single-host Select with Autocomplete (multiple, disableCloseOnSelect):
checkbox per option, chip display with limitTags=2, built-in search/filter
- getAnalyticsHosts() now unions traffic event hosts WITH all configured proxy host
domains (parsed from proxyHosts.domains JSON), so every proxy appears in the list
- analytics-db: buildWhere accepts hosts: string[] (empty = all); uses inArray for
multi-host filtering via drizzle-orm
- All 6 API routes updated: accept hosts param (comma-separated) instead of host
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Analytics default interval changed to 1h
- Add 'Custom' toggle option with datetime-local pickers (pre-filled to last 24h)
- Refactor analytics-db: buildWhere now takes from/to unix timestamps instead of Interval
- Export INTERVAL_SECONDS from analytics-db for route reuse
- All 6 API routes accept from/to params (fallback to interval if absent)
- Timeline bucket size computed from duration rather than hardcoded per interval
- Fix map country click highlight: bake isSelected into GeoJSON features (data-driven)
instead of relying on Layer filter prop updates (unreliable in react-map-gl v8)
- Split highlight into countries-selected (data-driven) and countries-hover (filter-driven)
- Show tooltip at country centroid when selected via table, hover takes precedence
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>