Skip to content

[Fix] Sanitize XHTML and encode text from ReqIF files before rendering - #87

Open
samatstariongroup wants to merge 2 commits into
developmentfrom
fix/57-sanitize-xhtml
Open

samatstariongroup wants to merge 2 commits into
developmentfrom
fix/57-sanitize-xhtml

Conversation

@samatstariongroup

@samatstariongroup samatstariongroup commented Oct 18, 2026 •

Copy link
Copy Markdown
Member

Prerequisites

  • I have written a descriptive pull-request title
  • I have verified that there are no overlapping pull-requests open
  • I have verified that I am following the reqifviewer code style guidelines
  • I have provided test coverage for my change (where applicable)

Description

Fixes #57.

Content of an uploaded ReqIF file was rendered with new MarkupString(...) in 21 places, so a crafted file could inject script into the page of whoever opened it. This was not limited to XHTML values: long names, identifiers, default values and text decoded from XHTML (<script> becoming live markup again) were also rendered as raw HTML.

One safe path for file content (Utilities/SafeMarkup.cs)

  • FromXhtml: removes namespace prefixes (xhtml:, other prefixes, default namespace) and sanitizes with HtmlSanitizer against an allow-list of the XHTML modules ReqIF uses: text, hypertext, list, presentation, edit and table. Removed: scripts and other non-text elements (with their content), event handler attributes, javascript: and data: links (also entity-encoded or space-prefixed), class and id attributes, and positioning or layering CSS (so file content cannot cover the page). url() and expression() in styles are removed.
  • FromText: HTML-encodes plain text.
  • ToPlainText: the visible text of an XHTML value for labels. Unlike ReqIFSharp's ExtractUnformattedTextFromValue it does not parse the value as XML, so values that are not well-formed no longer throw (this could make the relation matrix and the sidebar fail).
  • ExtractDisplayName returns a safe MarkupString in every case and falls back to the identifier when nothing visible is left; all call sites use it or SafeMarkup instead of wrapping content themselves.

Differences from the acceptance criteria of #57

  • object is not on the allow-list. Objects (embedded pictures) are shown by their fallback text, which is what users saw before: browsers never rendered the prefixed <xhtml:object> element. Showing pictures inline is [Feature]: Show pictures inline in rich text  #71.
  • Relative links (#anchor, other.html) are kept: they stay within the viewer, and an unknown address shows the not-found page.

Also

  • The Target column of the spec relations list linked to the source object.
  • CLAUDE.md: a rule for rendering content from a ReqIF file.
  • HtmlSanitizer 9.2.1039 is referenced directly (Blazor Blueprint brings 9.1.982 transitively; this resolves upward).

Testing

  • 114 tests pass (73 new; development has 41).
    • SafeMarkupTestFixture: legitimate formatting kept (paragraphs, bold, lists, tables, safe and relative links), each attack removed, plain text extraction.
    • XssAttemptsTestFixture renders TestData/xss-attempts.reqif, a ReqIF file with script, image onerror, javascript: link, SVG onload and fixed-position overlay payloads in texts, long names, identifiers and the document title, through the attribute values grid, the "Selected Value" card, the specification tree, the XHTML default value page and the embedded objects section.
    • Relation matrix labels are plain text and a text-only XHTML value no longer breaks the matrix.
  • The tests were checked against deliberate regressions: with the sanitizer disabled 20 tests fail; with the tree, the selected value card, the default value and the text extraction put back to the old code, 6 tests fail.
  • Checked in Chrome with xss-attempts.reqif: nothing executes (each payload would set window.__xss), markup in names shows as text, the overlay stays inside its cell, legitimate formatting renders.
  • CI: "Build & Test & SonarQube" will fail at the SonarCloud step until the SONAR_TOKEN secret is renewed ([Feature]: Fix CI: renew SONAR_TOKEN and re-enable CodeQL  #59).

🤖 Generated with Claude Code

samatstarion and others added 2 commits October 18, 2026 15:46
fixes #57

Content of a ReqIF file was rendered with `new MarkupString(...)` in 21
places, so a crafted file could inject script into the page: XHTML values,
but also long names, identifiers and text decoded from XHTML.

- New SafeMarkup helper: FromXhtml removes namespace prefixes and sanitizes
  with HtmlSanitizer against an allow-list of the XHTML modules ReqIF uses
  (no scripts, event handlers, javascript:/data: links, class or id
  attributes, positioning styles); FromText HTML-encodes plain text.
- ExtractDisplayName returns a safe MarkupString in every case; all call
  sites use it or SafeMarkup instead of wrapping content themselves.
- The relation matrix uses the plain-text ExtractDisplayText for labels.
- Tests: SafeMarkup unit tests (formatting kept, attacks removed) and
  component tests that render TestData/xss-attempts.reqif, a ReqIF file with
  script, event handler, javascript:, SVG and overlay payloads.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ests

- SafeMarkup.ToPlainText extracts the visible text of an XHTML value without
  parsing it as XML; ExtractDisplayText uses it, so a value that is not
  well-formed (for example plain text without an element) no longer makes the
  relation matrix or the sidebar fail.
- ExtractDisplayName falls back to the identifier when nothing visible is
  left (empty value, or XHTML with only removed content).
- The Target column of the spec relations list linked to the source.
- Tests for the specification tree, the "Selected Value" card, the XHTML
  default value page, embedded object captions, relation matrix labels, CSS
  url()/expression() in allowed properties, elements removed with their
  content, allowed elements, link schemes (relative links are kept) and
  ToPlainText.
- CLAUDE.md: rule for rendering content from a ReqIF file.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@sonarqubecloud

Copy link
Copy Markdown

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.

[Bug]: Sanitize XHTML from ReqIF files before rendering it

1 participant