Version 11.4 continues the work we started in 11.3: making ScroogeXHTML behave predictably when the RTF it receives is malformed, unusual, or outright hostile. This release is smaller than 11.3, but it closes a few real gaps, one of them in concurrent use, and it documents a security boundary we want you to be able to rely on.

Security: RTF content can’t reach your <head>

We audited every path by which content from an RTF document could end up in the generated <head> element (<title>, <style>, <link>, <meta>) and found none.

Here is why:

  • configuration.HtmlHeadConfiguration is written only through its public setters, which are meant to be called by you, the integrator. Nothing in the conversion pipeline writes to it.
  • The RTF \info destination group (\title, \author, \subject, \keywords, \doccomm) is dropped by the parser. It is never read into the head configuration.

In practical terms, an attacker who controls the RTF cannot inject a title, a stylesheet, a link, or a meta tag into your output page. What ends up in the head is exactly what your own code put there.

We’ve now written this down as an explicit trust boundary, both in the javadoc for HtmlHeadConfiguration and in docs/SECURITY.md. If you populate the head from RTF metadata in your own code, you’ll want to read that section, since at that point validation becomes your responsibility.

Fixes

Syriac and Divehi language tags

LanguageUtil had no mapping for LCID 1114 (Syriac) or 1125 (Divehi). Runs tagged \lang1114 or \lang1125 came out with no lang attribute at all. They now produce lang="syr" and lang="dv" respectively. This matters for screen readers, hyphenation, font selection, and anything else that depends on correct language metadata.

Stale lang attributes

This one was subtle. When the language changed partway through a paragraph, XMLDOMWriter.applyTextProperties() set the lang attribute correctly if the current language was non-empty. It did not, however, remove a stale value left by an earlier \lang change on the same still-empty span.

The visible symptom: a run whose language reverted to the document default (for example, an explicit \lang1033 inside an ambient non-English paragraph) kept the previous run’s lang value instead of being left unmarked. Text could therefore be labelled with the wrong language.

We saw this in real documents. If you convert multilingual content, your output may change slightly in 11.4. The new output is the correct one.

A thread-safety race in ResultBuilder

ResultBuilder used a shared TransformerFactory and reconfigured it (setFeature/setAttribute) on every conversion. If two conversions ran at the same time, that was a data race, even across different ScroogeXHTML instances. A single instance has always been documented as not thread-safe, but this shared factory reached beyond any one instance, so separate instances weren’t fully independent as you would reasonably expect.

The factory is now hardened once, rather than on every conversion. If you run conversions in parallel, for example in a web service handling concurrent uploads, this removes a source of rare and hard-to-reproduce failures.

Note that the guidance for individual instances is unchanged: don’t share one ScroogeXHTML instance across threads.

New tests

We’ve added two tests that extend the adversarial and robustness test infrastructure introduced in 11.3:

  • com.scroogexhtml.security.HeadConfigurationTrustTest verifies the head trust boundary described above.
  • ConcurrentConversionTest exercises concurrent conversions so the TransformerFactory race can’t quietly return.

Where the fixes came from

Two of the fixes above (the missing language mappings and the stale lang attribute) came out of a cross-check of our output against the RTF specification. That review continues, and we expect it to keep turning up this kind of small correctness issue.

Upgrading

11.4 contains no API changes. The one thing worth checking is multilingual documents: if you compare output between versions, expect lang attributes to differ where the old behaviour was wrong.

As always, if you find an RTF file that misbehaves, send it our way. Malformed input is exactly what this work is for.


About ScroogeXHTML RTF Converter

ScroogeXHTML is a library for the Java platform that converts RTF (Rich Text Format) documents to HTML5 or XHTML. It can produce either a complete standalone document or a markup fragment that you embed in your own pages, and it needs very little code to get started. It supports common RTF features such as hyperlinks, simple tables, multi-language and left-to-right/right-to-left text, and embedded pictures, which can be inlined as data URIs. Output is kept compact through CSS and default font definitions, and the intermediate DOM can be post-processed for fine tuning. The library has no external runtime dependencies beyond the SLF4J logging API, and full source code is available.

Learn more, try the live examples, and read the release notes at scroogexhtml.com.