Table of Contents

Class RemoteText

Namespace
FishMMO.Client
Assembly
FishMMO.Client.dll

Builds UnityEngine.UIElements.Labels for text that came from somewhere this client does not control, and sanitises such text before it reaches a log line.

public static class RemoteText
Inheritance
RemoteText
Inherited Members

Remarks

The thing this exists to prevent. A UI Toolkit UnityEngine.UIElements.TextElement — and therefore UnityEngine.UIElements.Label and UnityEngine.UIElements.Button, both of which derive from it — has UnityEngine.UIElements.TextElement.enableRichText defaulting to true. Assigning label.text does not "just show the string": the text is parsed for Unity rich-text markup, so a news feed or an update server that returns <color=#00FF00>Verified by FishMMO</color> gets exactly that, rendered as trusted-looking launcher chrome. <size=2000> is the same primitive pointed at the layout instead: one tag makes the pane unusable.

The launcher's news renderer additionally runs every text node through WebUtility.HtmlDecode, which turns an *escaped* &lt;color=...&gt; back into a live tag — so escaping upstream is not a defence, and a feed that looks inert as HTML is not inert as Unity markup.

Belt and braces, deliberately. CreateLabel(string, string, bool, int) turns rich text off, which is the actual fix; Sanitize(string, int, bool) strips the control characters and caps the length, which is what protects the log file and anything that later renders the same string somewhere this helper did not build. Either alone would be enough today. Both, because "today" is the part that changes: a future panel that builds its own Label from the same string is a one-line regression, and one that logs it is not covered by enableRichText at all.

Nothing here parses or rewrites markup. There is no tag allowlist and no attempt to "clean" tags, because a sanitiser that rewrites markup is a parser, and a parser is something to be bypassed. Remote text is rendered as literal characters or not at all.

Fields

MaxLength

Longest remote string rendered or logged verbatim.

public const int MaxLength = 2048

Field Value

int

Remarks

Generous for a status line or an error detail; nowhere near enough to be a layout or a log-file problem. The news body is bounded separately and much higher — this cap is for the short server-controlled strings (versions, error details) that reach the status label.

Methods

CreateLabel(string, string, bool, int)

Creates a UnityEngine.UIElements.Label that renders text literally.

public static Label CreateLabel(string text, string ussClass = null, bool allowNewlines = false, int maxLength = 2048)

Parameters

text string

Untrusted text. Null is treated as empty.

ussClass string

Optional USS class to add.

allowNewlines bool

True for body copy that is meant to wrap across paragraphs; false (the default) for single-line chrome such as a status line, where an embedded newline is only ever a way to push real text out of view.

maxLength int

Length cap; defaults to MaxLength.

Returns

Label

A label with rich text disabled and the text sanitised.

Sanitize(string, int, bool)

Returns text with control characters removed and its length capped, safe to put in a log line or a single-line UI element.

public static string Sanitize(string text, int maxLength = 2048, bool allowNewlines = false)

Parameters

text string

Untrusted text. Null returns an empty string.

maxLength int

Length cap; defaults to MaxLength.

allowNewlines bool

True to preserve line breaks. Only for text rendered as a wrapping block; never for a log line, where a newline is the log-forging primitive, and never for single-line chrome. CR and CRLF are normalised to a single LF either way so one line break is one line break regardless of who authored the string.

Returns

string

Remarks

Removes CR and LF (CWE-117: a server-controlled error string containing a newline forges whole log lines, which is how a real failure gets buried under fabricated "success" entries), the remaining C0/C1 control characters, and the bidirectional override codepoints — U+202A-U+202E and U+2066-U+2069 — which reorder rendered text and can make a hostile version string display as a benign one.

Tabs are kept: they are ordinary layout in a status message and carry no ambiguity.

SetText(Label, string, bool, int)

Assigns text to label as literal text.

public static void SetText(Label label, string text, bool allowNewlines = false, int maxLength = 2048)

Parameters

label Label

The label to write into. Null is ignored.

text string

Untrusted text. Null is treated as empty.

allowNewlines bool

True to keep line breaks (body copy).

maxLength int

Length cap; defaults to MaxLength.

Remarks

Use this for every write of remote content into an existing label, not just the first: enableRichText is a property of the element, and a label resolved from a freshly cloned UXML tree is a different element with the default back in place. (See the project's "mutate then Show" contract — UIDocument re-clones its tree on every enable, so a flag set once on a cached element does not survive.)