Skip to content

fix(pages): escape head tag attributes and show descriptions as text - #720

Merged
ashleycaselli merged 1 commit into
masterfrom
fix/meta-tag-escaping
Sep 18, 2026
Merged

ashleycaselli merged 1 commit into
masterfrom
fix/meta-tag-escaping

Conversation

@ashleycaselli

Copy link
Copy Markdown
Member

Problem

The head tags added for search engines and link previews (#704) and the RDF alternate links (#710) were built with Wicket's MetaDataHeaderItem. It does not HTML-escape attribute values: it only replaces " with \", which HTML does not treat as an escape. Values taken from nanopublications (a space's description, a user's name, a page title) were therefore not safely contained in their attributes.

It also showed up visibly: descriptions are often HTML, and the space page of SEMIC 2026 carried <span>…<strong>\"…\"</strong>… in its description, og:description and twitter:description, so search results and link previews would show raw markup.

Fix

  • Escaping. NanodashPage.headTag writes the meta and link tags itself and escapes every attribute value with Strings.escapeMarkup (", ', <, >, &). It replaces MetaDataHeaderItem for the description, canonical, Open Graph and Twitter card tags, and for the rel="alternate" RDF links. The tag format is unchanged, so the existing metadata tests pass as they were.
  • Descriptions as text. setMetaDescription now reduces a description to plain text before the existing one-line and 300-character treatment: markup is removed with the OWASP sanitizer already used by Utils.sanitizeHtml (script contents included), entities are decoded, and block boundaries (p, br, li, …) become spaces so paragraphs do not run together.

The embedded JSON-LD block was already safe (every < is written as <), and <title> goes through Wicket's normal label escaping.

Tests

NanodashPageMetadataTest gains 8 tests:

  • Rendering a page whose description contains a quote and markup: no markup reaches the head, and description, og:description and twitter:description all hold the escaped text.
  • Rendering a page with an HTML description like SEMIC 2026's: the tags hold its text.
  • headTag escaping of each character, a null value, and odd name/value pairs.
  • Plain-text conversion: block separation, entity decoding, script contents dropped, the length cut applied after markup is removed, and descriptions that are empty once markup is gone.

Full suite: 1420 tests, 0 failures.

🤖 Generated with Claude Code

The description, canonical, Open Graph, Twitter card and alternate-link tags
were built with Wicket's MetaDataHeaderItem, which does not HTML-escape
attribute values: it only backslash-escapes double quotes, which HTML does not
honour. Values taken from nanopublications, such as a space's description, a
user's name or a page title, were therefore not safely contained in their
attributes.

The tags are now written by a helper that escapes every attribute value for
HTML. Meta descriptions are also reduced to plain text before they are
shortened: many descriptions are HTML, which search results and link previews
showed as raw markup. Markup is removed with the OWASP sanitizer already in
use, entities are decoded, and block boundaries are kept as spaces.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@ashleycaselli
ashleycaselli merged commit 63202b9 into master Sep 18, 2026
8 checks passed
@ashleycaselli
ashleycaselli deleted the fix/meta-tag-escaping branch September 18, 2026 09:40
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