From 7e4c6940c97d6b4705b0aa4dfeb8c8e751764b79 Mon Sep 17 00:00:00 2001 From: lilleman Date: Tue, 4 Aug 2026 00:42:54 +0200 Subject: [PATCH] Escape the values interpolated into the one markup-carrying message --- AGENTS.md | 5 +++-- README.md | 4 ++-- src/ui/pagination.test.ts | 7 +++++++ views/partials/pagination.ejs | 6 +++++- 4 files changed, 17 insertions(+), 5 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 98a8525..344f411 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -147,8 +147,9 @@ them. Revisit only if the stated reason stops holding. branding, or a menu `rename` be either a key or plain text without a second field or a migration. Don't "fix" it into a loud failure: a manifest with plain labels must keep working. - **`t()` returns raw text; the view escapes it.** Messages go through `<%= %>` like any other value, - so nothing is double-escaped; a message carrying markup uses `<%- %>` and must not interpolate - untrusted data. Don't move escaping into `t()`. + so nothing is double-escaped; a message carrying markup uses `<%- %>`, and then its `{{vars}}` are + escaped at the call site (see `views/partials/pagination.ejs`). Don't move escaping into `t()` — + every other value in a view would then be the odd one out. - **CI docker logins share the runner host's Docker config.** The act_runner is host-mode, so `docker login`/`logout` in the workflows mutate one shared `~/.docker/config.json`: concurrent jobs can race (one job's logout can 401 another's push — recover by re-running), diff --git a/README.md b/README.md index bfd06d3..c19eed2 100644 --- a/README.md +++ b/README.md @@ -998,8 +998,8 @@ Three rules worth knowing: plain text — `nav: [{ label: "shop.title" }]` is translated, `label: "Shop"` is not, and neither breaks. The same holds for `config/menu.ts` branding and its `rename` overrides. - **`t()` returns raw text; the view escapes it.** Use `<%= %>` as for any other value. A message - that deliberately carries markup is rendered with `<%- %>` — and must never interpolate - untrusted data, since nothing escapes it there. + that deliberately carries markup is rendered with `<%- %>` — and its `{{vars}}` must then be + escaped at the call site, since nothing escapes them there (`pagination.ejs` is the worked example). - **Dates and numbers are `Intl`'s job**, not the catalog's: `new Intl.DateTimeFormat(ctx.locale)`. - **The core building blocks carry the locale for you** — every href they render (menu, breadcrumbs, pagination, sort headers, row actions, the auth card's links) goes through `localeHref`, and their diff --git a/src/ui/pagination.test.ts b/src/ui/pagination.test.ts index 1ae24a6..343526b 100644 --- a/src/ui/pagination.test.ts +++ b/src/ui/pagination.test.ts @@ -68,3 +68,10 @@ test("pagination renders a valid empty footer and never throws on missing config assert.match(html, /