Skip to content

fix(content-type): decode dollar sign in Story Block JSON on read (#35782)#36346

Open
dsilvam wants to merge 1 commit into
mainfrom
issue-35782-storyblock-dollar-sign
Open

fix(content-type): decode dollar sign in Story Block JSON on read (#35782)#36346
dsilvam wants to merge 1 commit into
mainfrom
issue-35782-storyblock-dollar-sign

Conversation

@dsilvam

@dsilvam dsilvam commented Jun 29, 2026

Copy link
Copy Markdown
Member

Proposed Changes

  • UtilMethods.escapeHTMLCodeFromJSON(...) now also decodes the decimal HTML numeric entity $ back to a literal $, alongside the existing :: and ,, decoding.
  • Added a unit test in UtilMethodsTest locking in $ decoding (plus the existing :/, behavior).

Additional Info

A $ typed into a Block Editor (Story Block) field was persisted in contentlet_as_json as its decimal HTML numeric entity $ (same family as the :/, case from #25903 / #28020). On every content read, ContentletTransformer.transform() passes the whole JSON through escapeHTMLCodeFromJSON, which only reversed : and , — so $ survived into the value handed to the editor.

The symptom was editor-only: the browser HTML parser renders $ as $ (front-end output looked correct), but the ProseMirror/TipTap editor displays the raw text node $50 literally on reopen. The shared read path (ContentletTransformerStoryBlockViewStrategy) is why both the legacy and new editors were affected.

Fixing at this universal read boundary is what the issue investigation recommended ("fix at the read/transform boundary so all API consumers — not just the editor — receive the correct $"), and it self-heals content that was already saved with $ — no DB migration required.

Note: # has the identical sister-bug potential (#) since $ and # are both Velocity-special. The issue's acceptance criteria is $-only, so # was intentionally left out of scope.

Checklist

  • Tests
  • Translations
  • Security Implications Contemplated (read-side decode only; no change to validation/storage of incoming content)

🤖 Generated with Claude Code

This PR fixes: #35782

…5782)

The $ character was persisted in contentlet_as_json as its decimal HTML
entity $ and survived into the editor because the read-time decoder
only reversed : and ,; extend escapeHTMLCodeFromJSON to also decode
$ so the Block Editor and all API consumers receive a literal $.

Refs: #35782

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@claude

claude Bot commented Jun 29, 2026

Copy link
Copy Markdown
Contributor

Claude finished @dsilvam's task in 2m 37s —— View job


dotCMS Backend Review Pipeline

  • Load dotCMS conventions
  • Get the Java diff
  • Spawn 4 specialized sub-agents in parallel (Security, Database, Java Standards, REST API)
  • Consolidate findings
  • Post review comment
  • Submit formal GitHub review (blocked — requires user approval via tool permission)

Review posted at #36346 (comment)

Summary of findings:

  • Security: ✅ NO_FINDINGS
  • Database: ✅ NO_FINDINGS
  • REST API: ✅ NO_FINDINGS
  • Java Standards: 🟠 1 High (misleading method name — pre-existing), 🟡 2 Medium (missing null guard + incomplete test coverage)

@github-actions

github-actions Bot commented Jun 29, 2026

Copy link
Copy Markdown
Contributor

🤖 dotBot Review (Bedrock)

Reviewed 2 file(s); 1 candidate(s) → 0 confirmed, 0 uncertain (unverified, kept for review).

✅ No issues found after verification.


us.deepseek.r1-v1:0 · Run: #28371278901 · tokens: in: 5870 · out: 1868 · total: 7738 · calls: 3 · est. ~$0.018

@claude

claude Bot commented Jun 29, 2026

Copy link
Copy Markdown
Contributor

🔍 dotCMS Backend Review

[🟠 High] dotCMS/src/main/java/com/dotmarketing/util/UtilMethods.java:1521

The method is named escapeHTMLCodeFromJSON but performs the inverse operation — it decodes HTML numeric entities, not encodes/escapes them. This is a pre-existing naming issue; the PR extends it by one more entity. A misleading name risks misuse by future callers expecting an escape function. Parameter reassignment (json = json.replace(...)) also reduces readability.

public static String escapeHTMLCodeFromJSON(String json) {
    json = json.replace("&#58;",":")   // parameter reassigned; method actually decodes

💡 Consider renaming to decodeHTMLEntitiesFromJSON and using a local variable — but note this is pre-existing; a rename is a separate, larger refactor.


[🟡 Medium] dotCMS/src/main/java/com/dotmarketing/util/UtilMethods.java:1522

No null-safety guard. If json is null, json.replace(...) throws NullPointerException. The rest of UtilMethods is defensively written; this method is the odd one out. Pre-existing issue, but the PR is the right moment to address it since it touches this method.

public static String escapeHTMLCodeFromJSON(String json) {
    json = json.replace("&#58;",":")  // NPE if json is null

💡 Add if (json == null) return null; (or if (!UtilMethods.isSet(json)) return json;) as the first line.

Fix this →


[🟡 Medium] dotCMS/src/test/java/com/dotmarketing/util/UtilMethodsTest.java:706

The new test covers only the happy path (all three entities present). Missing: null-input test (which would expose the NPE above), identity test (string with no entities), and partial-presence tests. Adding a null test here would serve as a regression guard for the null-safety fix.

// Only happy-path tested; null input not covered
final String encoded = "{\"text\":\"The application fee is &#36;50, see http&#58;//x.com\"}";

💡 Add test_escapeHTMLCodeFromJSON_null_input and test_escapeHTMLCodeFromJSON_no_entities test methods.

Fix this →


Next steps

  • 🟠 The naming issue is pre-existing and out of scope for this fix — a follow-up ticket is the right vehicle
  • 🟡 Null guard and missing test coverage can be addressed inline: @claude fix the null guard in escapeHTMLCodeFromJSON and add null/identity test cases

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

AI: Safe To Rollback Area : Backend PR changes Java/Maven backend code

Projects

Status: No status

Development

Successfully merging this pull request may close these issues.

Block Editor: Dollar sign ($) replaced with HTML entity &#36; after save/publish

1 participant