Text.scala's four opaque types validate identically, so their names promise a safety distinction they do not make #52
Labels
No labels
bug
documentation
duplicate
enhancement
good first issue
help wanted
invalid
question
wontfix
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
iterative-works/support#52
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Problem
The
Text.scalatype family reads as a safety hierarchy but is not one. All four opaque typesvalidate identically —
Text.validateNonEmpty— so the names carry a guarantee theconstructors never make.
core/shared/src/main/scala/works/iterative/core/Text.scala:applyvalidationPlainMultiLineText.validateNonEmptyPlainOneLinevalidateNonEmpty+ no newlineMarkdownText.validateNonEmptyHtmlTextText.validateNonEmptyHtmlTextandPlainMultiLineare indistinguishable in what they guarantee. Neither performs HTMLescaping, character allow-listing, markup validation or a length cap. Each also has an
unsafeconstructor or a direct
Optionpath that skips even the non-emptiness check.Consequence
A reader reasons from the name: "it is
PlainMultiLine, so it is plain"; "it isHtmlText, soraw()is fine". Both inferences are unsupported. In practicePlainMultiLinecarriesstaff-authored free text that is later rendered on an end-user-facing page, and the only thing
standing between it and stored XSS is the caller remembering to use an auto-escaping renderer.
Observed in medeca-modul-poptavky (MEDECA-478): a security review had to trace
PlainMultiLineintoiw-support to establish that it guarantees nothing but non-emptiness. The rendering happened to be
safe, but nothing in the type said so.
Proposed fix
At minimum, Scaladoc on each of the four types stating exactly what is guaranteed (non-emptiness,
plus no-newline for
PlainOneLine) and what is not (no escaping, no markup validation, no lengthcap) — so the name is no longer the only documentation.
Better, if it is worth the change: make the safety distinction real rather than nominal.
HtmlTextbecomes the only type claiming "safe to emit unescaped" and gets a constructor thatearns it (a sanitiser, or an explicitly-trusted
unsafepath that is named as such); the plaintypes get an escaping path (
asHtml/escaped) so converting one into the other is a visiblestep rather than an assumption.
Provenance: MEDECA-478 lessons triage, L31.