Skip to content

feat: firewall (WAF) management page#128

Merged
acoshift merged 12 commits into
mainfrom
feat/waf
May 26, 2026
Merged

feat: firewall (WAF) management page#128
acoshift merged 12 commits into
mainfrom
feat/waf

Conversation

@acoshift
Copy link
Copy Markdown
Member

Summary

  • Adds a per-location Firewall (WAF) editor at /waf, modeled on the existing location-scoped route/pull-secret pages.
  • One zone per project per location. A Select location picker at the top loads that location's ruleset via waf.get; a missing zone is treated as the normal "not configured yet" state (empty editor, not an error screen).
  • The editor has a description field and an editable rules table: id, description, expression (wide CEL input), action (log/allow/block via Select), priority (number), and HTTP status + message inputs that appear only when action === 'block' (defaults 403 / "Forbidden"). Add-row and remove-row buttons use Svelte 5 $state arrays.
  • Save posts the whole ruleset with waf.set; Disable firewall (in a DangerZone, behind a confirm modal) calls waf.delete. Errors surface through the standard modal.error handler.
  • Adds the Firewall entry to the project sidebar (shield icon, between Routes and Workload Identities), Api.WafZone / Api.WafRule types, and waf.get/waf.set/waf.delete mock fixtures for offline dev.

Notes

  • status/message are sent as flat rule fields (and only when blocking), matching the API contract; rule priority defaults to 0.
  • npm run lint, npm run check, and npm run build all pass with zero errors/warnings.

Test plan

  • npm run lint — clean
  • npm run check (svelte-check) — 0 errors / 0 warnings
  • npm run build — succeeds
  • Rendered via npm run dev:mock + Playwright: page renders in light and dark mode, location selector loads the zone, Add Rule appends a row, switching a row's action to "Block" reveals the status/message inputs, no console/page errors.
  • Verify against a live backend once the waf.* endpoints are deployed.

🤖 Generated with Claude Code

Adds a per-location firewall editor under /waf: a location selector that
loads that zone's ruleset via waf.get, a description field, and an editable
rules table (id, description, CEL expression, action, priority, plus HTTP
status + message inputs shown only when the action is block). Saves the
whole ruleset with waf.set and disables the firewall via waf.delete behind a
confirm in a danger zone. Includes the Firewall sidebar entry, Api.WafZone /
Api.WafRule types, and mock fixtures for offline dev.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@cloudflare-workers-and-pages
Copy link
Copy Markdown

cloudflare-workers-and-pages Bot commented May 26, 2026

Deploying deploys-app--console with  Cloudflare Pages  Cloudflare Pages

Latest commit: 161293f
Status: ✅  Deploy successful!
Preview URL: https://8d6a6b8c.deploys-app--console.pages.dev
Branch Preview URL: https://feat-waf.deploys-app--console.pages.dev

View logs

acoshift and others added 11 commits May 26, 2026 15:50
Add a "Build…" helper next to each rule's expression that opens an in-page
modal to compose one condition from form controls (field, operator, value,
case-insensitive/url-decode/negate), shows a live CEL preview, and inserts
the generated snippet into the raw expression — combining with AND/OR or
replacing when the rule already has an expression.

CEL is generated only from parapet's portable helper functions
(containsAny, hasPrefixAny, regexMatch, ipInCidr, lower, urlDecode) rather
than the non-portable .startsWith/.contains/.matches string methods. The
generation lives in a pure, testable module (src/lib/waf/expression.js).
The raw expression field stays the source of truth and is now a monospace
textarea.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The expression is now a readonly field set exclusively via the Build…
wizard (plus a Clear action), so rules can only be composed from the
portable CEL helpers. Rule ids are auto-generated and stable across
reorders, shown read-only. Replace the manual priority input with
move up/down controls — row order is the execution order (priority is
derived from position on save).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Replace the detail-heavy firewall table with a compact summary list
(ID, Description, Action + row actions) and edit each rule in a working-copy
modal that embeds the CEL condition builder. Retires the standalone
WafExpressionModal in favor of the in-modal builder.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Replace the WAF rule modal with a list + detail page flow. The list page
reflects server state for the selected location and persists move/remove
optimistically (reloading on error), with an explicit Save for the zone
description; the new edit page hosts the rule fields and condition builder
and writes the whole zone on Save. The location is carried in the URL so
context survives navigating to the edit page and back.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…ression mode

Reorder the edit page to read top-to-bottom like Cloudflare's custom-rule
editor: Description, then "When incoming requests match" (the expression),
then "Then take action" (Action + block-only response fields) above Save.

Add a Visual/Expression toggle over the same expression string — Visual keeps
the condition builder, Expression exposes an editable monospace CEL textarea so
raw rules can be typed or pasted. Switching modes preserves the expression, and
a shared Clear stays available in both. The builder gains a `showPreview` prop
(and exposes `clearExpression`) so the page can render its own raw editor.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Split the single WAF management page into an index that lists configured
firewalls, a create page that picks an unconfigured location, and a manage
page where the location is read-only. The index and create pages fan out
waf.get per location (no list-zones endpoint exists) to discover which
locations are configured. The mock now returns the seed zone only for
gke.cluster-rcf2 and not-found elsewhere, and remembers locations created
in-session so the create -> manage flow is coherent.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…ion builder

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Drive the WAF condition builder's value input from the selected field and
operator: a TLS on/off toggle for the scheme field (https/http), a free-text
datalist combobox for method equals/not_equals, and a chip-based TagInput for
the contains-any/starts-with-any operators. Drop the Body field.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Add an optional `editable` mode to Select.svelte so a free-text combobox
renders with the same trigger/chevron/menu styling as the standard dropdown.
When `editable` is false the component behaves exactly as before, so all
existing usages are untouched. Use it for the WAF Method value control,
replacing the native `<input list>` + `<datalist>` that looked inconsistent
with the other dropdowns while still allowing custom methods (e.g. PURGE).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Add parseExpression() as the conservative inverse of the CEL generator so the
visual builder can reflect an existing expression. Rebuild WafConditionBuilder
as a two-way-bound, rows-based editor (one row per condition + AND/OR
combinator) and gate the Visual tab on the expression being representable —
complex expressions (mixed &&/||, grouping, unknown functions, .startsWith,
malformed) keep the editor in raw mode with a hint.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Add a small "Preview" badge next to the Firewall nav item to signal the
feature is still in preview.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@acoshift acoshift merged commit 174560e into main May 26, 2026
5 checks passed
@acoshift acoshift deleted the feat/waf branch May 26, 2026 16:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant