Skip to content

fix: subexpression WriteSafeString output not double-encoded (issue #543)#633

Merged
rexm merged 3 commits into
masterfrom
worktree-agent-a8226037e7a175d56
Jun 20, 2026
Merged

fix: subexpression WriteSafeString output not double-encoded (issue #543)#633
rexm merged 3 commits into
masterfrom
worktree-agent-a8226037e7a175d56

Conversation

@rexm

@rexm rexm commented Jun 20, 2026

Copy link
Copy Markdown
Member

Fixes #543

Values written via WriteSafeString inside a subexpression are now passed to the outer helper without re-encoding.

What changed

HelperExtensions.ReturnInvoke captures a helper's writer output and returns it as the subexpression's value. Previously it returned a plain string, losing the "already-encoded" signal — so the outer helper (or the partial resolver) would encode it a second time.

The fix introduces an internal SafeString wrapper class. ReturnInvoke now wraps its result in SafeString, and the following sites handle it transparently:

  • EncodedTextWriter.Write<T> — writes SafeString.Value without encoding
  • HandlebarsExtensions.WriteSafeString(object) — unwraps SafeString before writing
  • HandlebarsUtils.IsFalsy — treats SafeString("") as falsy, matching string semantics
  • PartialBinder — extracts the raw name string from a SafeString when the partial name comes from a subexpression ({{> (helper)}})

Tests

source/Handlebars.Test/Issues/Issue543Tests.cs adds two regression tests covering the standalone and subexpression cases.

All 1 748 existing tests continue to pass.

rexm and others added 2 commits June 20, 2026 08:31
…on boundary (issue #543)

When a helper written with WriteSafeString was used as a subexpression
argument, ReturnInvoke captured the output as a plain string, losing the
"already-encoded" signal.  The outer helper then re-encoded the content.

Introduce an internal SafeString wrapper that ReturnInvoke now returns.
EncodedTextWriter.Write<T>, WriteSafeString(object), HandlebarsUtils.IsFalsy,
and PartialBinder all understand SafeString, so the value flows through
without double-encoding while dynamic partial lookups continue to work.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@rexm rexm enabled auto-merge June 20, 2026 12:59
@sonarqubecloud

Copy link
Copy Markdown

@rexm rexm merged commit 9dea249 into master Jun 20, 2026
7 checks passed
@rexm rexm deleted the worktree-agent-a8226037e7a175d56 branch June 20, 2026 16:04
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.

Subexpression encoding/escaping issue

1 participant