Skip to content

fix(coderd/notifications): skip markdown escaping for control-flow labels - #27978

Open
jdomeracki-coder wants to merge 2 commits into
mainfrom
fix/notification-control-flow-label-escaping
Open

fix(coderd/notifications): skip markdown escaping for control-flow labels#27978
jdomeracki-coder wants to merge 2 commits into
mainfrom
fix/notification-control-flow-label-escaping

Conversation

@jdomeracki-coder

Copy link
Copy Markdown
Contributor

Problem

Commit 07f79af ("fix: markdown rendering improvements") made notifier.prepare render notification titles and bodies from a SanitizedPayload that Markdown-escapes every label value.

The AI budget admin templates (added concurrently in #27415, migration 000553) gate a line on a control-flow comparison:

{{- if eq .Labels.limit_source "user_override"}}
This limit is a per-user override.
{{- end}}

limit_source is a machine-set enum (codersdk.AIBudgetLimitSource: user_override / group), not user input. Markdown escaping turns user_override into user\_override, so the eq comparison fails and the conditional line silently disappears. This broke TestNotificationTemplates_Golden on main for the AI budget templates.

Fix

  • Add an explicit skip-list of control-flow label keys (controlFlowLabels, currently just limit_source) that are passed through verbatim. All other user-controlled values (display names, other labels) remain Markdown-escaped, preserving the injection hardening from 07f79af.
  • SanitizedPayload now accepts optional skipLabels ...string.
  • Regenerate the two AI budget user golden files for the legitimate <title>/<h1> HTML escaping (You're -> You&#39;re) introduced by 07f79af. The admin HTML and webhook goldens are unchanged because the conditional line is restored.

Why not just make gen the goldens? Blindly accepting them would ship a real behavioral regression: admins would stop seeing the "per-user override" line. The skip-list restores correct behavior; the goldens only change where escaping is genuinely correct (HTML contexts).

Tests

  • New unit coverage in sanitize_test.go: skipped control-flow labels stay verbatim while user-controlled labels are still escaped, and an end-to-end render test asserting the eq .Labels.limit_source "user_override" conditional still renders. These lock the behavior so the goldens can't be silently regenerated away again.
  • go test ./coderd/notifications/... passes, including TestNotificationTemplates_Golden.
Verification
  • Confirmed limit_source is the only eq .Labels.* literal comparison across all notification template bodies.
  • Confirmed the value is always assigned from the codersdk.AIBudgetLimitSource enum in coderd/aibridgedserver/notifications.go, never from request input.
  • Regenerated goldens diff is confined to HTML <title>/<h1>; no &#39; leaked into plaintext bodies or webhook JSON.

Generated by Coder Agents on behalf of @jdomeracki-coder.

…bels

Commit 07f79af rendered notification titles and bodies from a
SanitizedPayload that Markdown-escapes every label value. That broke the
AI budget admin templates, which gate a line on
{{if eq .Labels.limit_source "user_override"}}: escaping turned the
machine-set value into "user\\_override", so the comparison failed and
the "This limit is a per-user override." line silently disappeared,
causing the golden tests to fail.

Add an explicit skip-list of control-flow label keys (currently just
limit_source, set from the codersdk.AIBudgetLimitSource enum) that are
left verbatim while all other user-controlled values remain escaped.
Regenerate the AI budget user golden files for the legitimate title HTML
escaping introduced by 07f79af.

Generated by Coder Agents.
Apply gci/goimports ordering to smtp_internal_test.go so make fmt is a
no-op and the format lint check passes.

Generated by Coder Agents.
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