From aab35f0679350011c6d6fce384d8a1bd6e685f94 Mon Sep 17 00:00:00 2001 From: Mikhail Yenuchenko Date: Thu, 13 Aug 2026 22:54:31 +0300 Subject: [PATCH] =?UTF-8?q?fix(panel):=20P3=20optional=20items=20=E2=80=94?= =?UTF-8?q?=20urlquery,=20aria-live,=20confirm-without-JS=20note?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Deliveries fragment/pagination links urlquery-encode the domain/app filters instead of splicing them into the query string raw. - The four polled regions (deliveries rows, status, mail queue, system log) carry aria-live="polite" so a screen reader announces refreshes. - security.md accepted risks note that data-confirm prompts are JavaScript-only and why that is acceptable. docs/plans/code-review.md P3 fully checked off; CHANGELOG updated. Co-authored-by: Cursor --- CHANGELOG.md | 12 ++++++++++++ docs/plans/code-review.md | 2 +- docs/security.md | 13 +++++++++++++ internal/web/view/templates/deliveries_rows.html | 9 +++++---- internal/web/view/templates/mail_queue_body.html | 2 +- internal/web/view/templates/status_body.html | 2 +- internal/web/view/templates/system_log_body.html | 2 +- 7 files changed, 34 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7cd31fe..56662b2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -62,6 +62,18 @@ Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/); version `field_values`, `credentials_fields`) instead of copy-pasted blocks. No behaviour or visible change. +- panel (GUI, accessibility): the Deliveries fragment's `hx-get` and pagination + links now `urlquery`-encode the `domain`/`app` filters instead of splicing + them into the query string raw. The four polled regions (deliveries rows, + status, mail queue, system log) carry `aria-live="polite"` so a screen + reader announces the refreshed content. + +- docs: [security.md](docs/security.md) accepted risks now note that + `data-confirm` prompts on destructive forms are JavaScript-only — with + JavaScript disabled the form submits immediately, the same as before the + prompts existed — and why that is acceptable (the prompt is a mis-click + guard, not an authorization boundary). + - docs: full-tree review plan ([docs/plans/code-review.md](docs/plans/code-review.md)) — architecture, quality, GUI, tests, licence; P0 is domain-admin send-log authorization. Roadmap queues that plan ahead of inbound-relay and records diff --git a/docs/plans/code-review.md b/docs/plans/code-review.md index 0a1fb23..04d3f9c 100644 --- a/docs/plans/code-review.md +++ b/docs/plans/code-review.md @@ -551,7 +551,7 @@ roles, and security.md no longer calls the panel single-user. domain delete. Done as a confirm page (`GET/POST /users/{uid}/delete`), matching `domain_delete.html`. - [x] DNS field partial; settings credentials partial. -- [ ] Optional: `urlquery` on deliveries fragment params; `aria-live` on +- [x] Optional: `urlquery` on deliveries fragment params; `aria-live` on polled regions; confirm-without-JS note next to the CSRF accepted risks. **Done when:** a rate-limit validation error is visually an error; user delete diff --git a/docs/security.md b/docs/security.md index c204095..a8d11e4 100644 --- a/docs/security.md +++ b/docs/security.md @@ -127,6 +127,19 @@ deferred item from the roadmap. either: code executing in the panel's origin sends the request itself — against that, `html/template` auto-escaping and CSP do the work, which is why templates must contain no inline scripts and no inline styles. +- **Destructive-action confirmation (`data-confirm`) is JavaScript-only.** + Delete, regenerate-password, and clear-rate-limit forms carry a + `data-confirm` prompt handled entirely in + [panel.js](../internal/web/view/static/panel.js); with JavaScript disabled + or blocked the form submits immediately, exactly as it did before the + prompts existed. Accepted deliberately: the prompt is a mis-click guard, + not an authorization boundary — the same origin check and session/RBAC + gate every one of these `POST`s whether or not JavaScript ran. Progressive + enhancement means the panel must work with JavaScript off; a + server-rendered confirmation step would need a second page (or a `?confirm=1` + round trip) for every one of these forms, which is what + [`user_delete.html`](../internal/web/view/templates/user_delete.html) and + `domain_delete.html` already do for the two highest-blast-radius deletes. - **Encrypting backups and exports is an option, not the default.** With the checkbox cleared the file downloads in the clear, as in 1.0. Otherwise an operator with nowhere to keep a password would lose the ability to take a diff --git a/internal/web/view/templates/deliveries_rows.html b/internal/web/view/templates/deliveries_rows.html index b14610f..eb16817 100644 --- a/internal/web/view/templates/deliveries_rows.html +++ b/internal/web/view/templates/deliveries_rows.html @@ -1,5 +1,6 @@ {{define "deliveries_rows"}} -
{{if .Rows}} @@ -14,15 +15,15 @@ - + {{end}}
{{.To}} {{.Subject}} {{.Status}}DetailsDetails

Page {{.Page}}{{if .LastPage}} of {{.LastPage}}{{end}} - {{if .HasPrev}} · ← Newer{{end}} - {{if .HasNext}} · Older →{{end}} + {{if .HasPrev}} · ← Newer{{end}} + {{if .HasNext}} · Older →{{end}}

{{else}}

No messages logged yet.

diff --git a/internal/web/view/templates/mail_queue_body.html b/internal/web/view/templates/mail_queue_body.html index 34b7caf..a2162e0 100644 --- a/internal/web/view/templates/mail_queue_body.html +++ b/internal/web/view/templates/mail_queue_body.html @@ -1,5 +1,5 @@ {{define "mail_queue_body"}} -
+
{{if .Error}}

{{.Error}}

{{end}} {{if .Output}}{{.Output}}{{else}}Queue is empty.{{end}}
diff --git a/internal/web/view/templates/status_body.html b/internal/web/view/templates/status_body.html index 2d901e0..366801b 100644 --- a/internal/web/view/templates/status_body.html +++ b/internal/web/view/templates/status_body.html @@ -1,5 +1,5 @@ {{define "status_body"}} -
+

Overall {{.OverallStatus}}

{{.OverallHeading}}

diff --git a/internal/web/view/templates/system_log_body.html b/internal/web/view/templates/system_log_body.html index 1c99e02..4e48438 100644 --- a/internal/web/view/templates/system_log_body.html +++ b/internal/web/view/templates/system_log_body.html @@ -1,5 +1,5 @@ {{define "system_log_body"}} -
+
{{if .Error}}

{{.Error}}

{{end}} {{if .Lines}}{{range .Lines}}{{.}} {{end}}{{else}}No log lines yet.{{end}}