Skip to content

Fix HTML-escaping of check error messages - #222

Merged
cinar merged 3 commits into
mainfrom
fix-html-template-escaping
Sep 5, 2026
Merged

Fix HTML-escaping of check error messages#222
cinar merged 3 commits into
mainfrom
fix-html-template-escaping

Conversation

@cinar

@cinar cinar commented Sep 5, 2026

Copy link
Copy Markdown
Owner

Problem

check_error.go renders localized error messages with html/template, which contextually escapes <, >, &, and " in template data. That's the wrong behavior for a validation library whose primary consumer is a plain-text or JSON API error response.

Verified against main before writing this fix: no built-in checker triggers visible corruption today — the template data they pass via NewCheckErrorWithData is either numeric (gte/lte's n) or a developer-chosen name (eq-field's field name, hash's algorithm), never raw user input. But a custom checker registered via RegisterMaker — a documented extension point — that echoes a checked value back into Data (e.g. for a "must not contain X" style message) gets corrupted output:

Got: <script>&"'</script>

renders as

Got: &lt;script&gt;&amp;&#34;&#39;&lt;/script&gt;

Fix

Switch the import from html/template to text/template, which performs no such escaping. No other code changes needed — the two packages share the same Parse/Execute API.

Testing

  • Added TestCheckErrorWithDataDoesNotHTMLEscape, asserting a message with <, >, &, ", ' in its template data renders unescaped.
  • Full suite passes, 100% coverage maintained.
  • go vet, gosec clean.

Fixes #197

🤖 Generated with Claude Code

https://claude.ai/code/session_01FLdVmP5daHiknrTW4Geh2i

CheckError rendered its localized error message with html/template,
which contextually escapes <, >, &, and " in template data for safe
HTML embedding. That's the wrong behavior for a library whose primary
consumer is a plain-text error or JSON API response body: any error
whose template data contains those characters (a value echoed back by
a custom checker via NewCheckErrorWithData, which is a documented
extension point) comes out corrupted, e.g. `<script>&"'` renders as
`&lt;script&gt;&amp;&#34;&#39;`.

No built-in checker triggers this today -- their template data is
either numeric (gte/lte's "n") or a developer-chosen name (eq-field's
field name, hash's algorithm), not raw user input -- so this was a
latent bug for the built-ins, but a live one for any custom checker
using this exact pattern.

Switch to text/template, which performs no such escaping.

Fixes #197

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FLdVmP5daHiknrTW4Geh2i
@codecov

codecov Bot commented Sep 5, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 100.00%. Comparing base (ce3aaf2) to head (e0daa3a).
⚠️ Report is 1 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff            @@
##              main      #222   +/-   ##
=========================================
  Coverage   100.00%   100.00%           
=========================================
  Files           52        52           
  Lines          896       896           
=========================================
  Hits           896       896           

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@cinar
cinar enabled auto-merge (squash) September 5, 2026 16:32
@cinar
cinar merged commit 8b05a5e into main Sep 5, 2026
5 checks passed
@cinar
cinar deleted the fix-html-template-escaping branch September 5, 2026 16:37
cinar added a commit that referenced this pull request Sep 5, 2026
## Problem

Every PR added its own bullet to `CHANGELOG.md`'s `## [Unreleased]`
section, always inserting at (or near) the same spot. With PRs landing
close together, that made this file a near-guaranteed merge conflict on
every concurrent pull request — the exact problem `DOC.md` caused before
#180 dropped it in favor of pkg.go.dev.

This wasn't theoretical: it's what just happened resolving conflicts on
#219#222. Each conflict resolution round immediately conflicted again
as the next PR merged, because every remaining branch's `### Fixed`
entry inserted into the same place.

## Fix

- Freeze the current `## [Unreleased]` section as a one-time snapshot
(documenting the six P0 bugfixes plus the earlier accumulated
Added/Changed/Removed entries) and add a note explaining the new policy
at the top of the file.
- Going forward, unreleased changes are covered by [GitHub
Releases](https://github.com/cinar/checker/releases), whose notes are
generated automatically from merged pull requests — no file to conflict
on. `CHANGELOG.md` only gains a new entry when a version is actually
tagged, added by hand from that release's generated notes (a maintainer
decision, not a per-PR one).
- Updated `CLAUDE.md`'s conventions section: PRs no longer touch
`CHANGELOG.md`.

No code changes; docs/process only.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

https://claude.ai/code/session_01FLdVmP5daHiknrTW4Geh2i

Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
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.

check_error.go uses html/template, corrupting JSON API error messages

1 participant