Vulnerable String Sanitization (Prevent `cmdlink`) (#4922)

* Fix label markup escapeing (#40600)

fix

* Sanitize NanoChat

* Escape PDA Owner/Job

* Escape ID Examine

* Early merge of https://github.com/space-wizards/space-station-14/pull/41799

---------

Co-authored-by: beck-thompson <107373427+beck-thompson@users.noreply.github.com>
Co-authored-by: Tobias Berger <toby@tobot.dev>
This commit is contained in:
Vanessa 2025-12-09 09:38:10 -06:00 committed by GitHub
parent da74750c7d
commit 67c127ecee
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 52 additions and 15 deletions

View File

@ -1,8 +1,11 @@
using Content.Client.Message;
using Content.Client.UserInterface.RichText; // DeltaV - Sanitize markup; see https://github.com/space-wizards/space-station-14/pull/41799
using Content.Shared.MassMedia.Systems;
using Robust.Client.AutoGenerated;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.RichText; // DeltaV - Sanitize markup; see https://github.com/space-wizards/space-station-14/pull/41799
using Robust.Client.UserInterface.XAML;
using Robust.Shared.Utility; // DeltaV - Sanitize markup; see https://github.com/space-wizards/space-station-14/pull/41799
namespace Content.Client.CartridgeLoader.Cartridges;
@ -14,6 +17,18 @@ public sealed partial class NewsReaderUiFragment : BoxContainer
public event Action? OnNotificationSwithPressed;
// DeltaV - Sanitize markup; see https://github.com/space-wizards/space-station-14/pull/41799
private static readonly Type[] AllowedTags =
[
typeof(BoldItalicTag),
typeof(BoldTag),
typeof(BulletTag),
typeof(ColorTag),
typeof(HeadingTag),
typeof(ItalicTag),
typeof(MonoTag),
];
public NewsReaderUiFragment()
{
RobustXamlLoader.Load(this);
@ -31,16 +46,17 @@ public sealed partial class NewsReaderUiFragment : BoxContainer
Author.Visible = true;
PageName.Text = article.Title;
PageText.SetMarkupPermissive(article.Content);
PageText.SetMessage(FormattedMessage.FromMarkupPermissive(article.Content), AllowedTags); // DeltaV - Sanitize markup; see https://github.com/space-wizards/space-station-14/pull/41799
PageNum.Text = $"{targetNum}/{totalNum}";
NotificationSwitch.Text = Loc.GetString(notificationOn ? "news-read-ui-notification-on" : "news-read-ui-notification-off");
string shareTime = article.ShareTime.ToString(@"hh\:mm\:ss");
var shareTime = article.ShareTime.ToString(@"hh\:mm\:ss");
ShareTime.SetMarkup(Loc.GetString("news-read-ui-time-prefix-text") + " " + shareTime);
Author.SetMarkup(Loc.GetString("news-read-ui-author-prefix") + " " + (article.Author != null ? article.Author : Loc.GetString("news-read-ui-no-author")));
var author = Loc.GetString("news-read-ui-author-prefix") + " " + (article.Author ?? Loc.GetString("news-read-ui-no-author")); // DeltaV - Sanitize markup; see https://github.com/space-wizards/space-station-14/pull/41799
Author.SetMessage(FormattedMessage.FromMarkupPermissive(author), AllowedTags); // DeltaV - Sanitize markup; see https://github.com/space-wizards/space-station-14/pull/41799
Prev.Disabled = targetNum <= 1;
Next.Disabled = targetNum >= totalNum;

View File

@ -1,10 +1,12 @@
using Content.Client.Message;
using Content.Client.Stylesheets;
using Content.Client.UserInterface.RichText; // DeltaV - Sanitize markup; see https://github.com/space-wizards/space-station-14/pull/41799
using Content.Shared.MassMedia.Systems;
using Robust.Client.AutoGenerated;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.CustomControls;
using Robust.Client.UserInterface.RichText; // DeltaV - Sanitize markup; see https://github.com/space-wizards/space-station-14/pull/41799
using Robust.Client.UserInterface.XAML;
using Robust.Shared.Utility;
@ -18,6 +20,18 @@ public sealed partial class ArticleEditorPanel : Control
private bool _preview;
// DeltaV - Sanitize markup; see https://github.com/space-wizards/space-station-14/pull/41799
private static readonly Type[] AllowedTags =
[
typeof(BoldItalicTag),
typeof(BoldTag),
typeof(BulletTag),
typeof(ColorTag),
typeof(HeadingTag),
typeof(ItalicTag),
typeof(MonoTag),
];
public ArticleEditorPanel()
{
RobustXamlLoader.Load(this);
@ -29,7 +43,7 @@ public sealed partial class ArticleEditorPanel : Control
// Customize scrollbar width and margin. This is not possible in xaml
var scrollbar = ContentField.GetChild(1);
scrollbar.SetWidth = 6f;
scrollbar.Margin = new Thickness(9, 0, 2 , 0);
scrollbar.Margin = new Thickness(9, 0, 2, 0);
RichTextInfoLabel.TooltipSupplier = sender =>
{
@ -81,7 +95,9 @@ public sealed partial class ArticleEditorPanel : Control
TextEditPanel.Visible = !_preview;
PreviewPanel.Visible = _preview;
PreviewLabel.SetMarkupPermissive(Rope.Collapse(ContentField.TextRope));
var articleBody = Rope.Collapse(ContentField.TextRope); // DeltaV - Sanitize markup; see https://github.com/space-wizards/space-station-14/pull/41799
PreviewLabel.SetMessage(FormattedMessage.FromMarkupPermissive(articleBody), AllowedTags); // DeltaV - Sanitize markup; see https://github.com/space-wizards/space-station-14/pull/41799
}
private void OnCancel(BaseButton.ButtonEventArgs eventArgs)

View File

@ -146,7 +146,7 @@ namespace Content.Client.PDA
if (state.PdaOwnerInfo.ActualOwnerName != null)
{
_pdaOwner = state.PdaOwnerInfo.ActualOwnerName;
_pdaOwner = FormattedMessage.EscapeText(state.PdaOwnerInfo.ActualOwnerName); // DeltaV - Escape actual owner
PdaOwnerLabel.SetMarkup(Loc.GetString("comp-pda-ui-owner",
("actualOwnerName", _pdaOwner)));
PdaOwnerLabel.Visible = true;
@ -159,8 +159,8 @@ namespace Content.Client.PDA
if (state.PdaOwnerInfo.IdOwner != null || state.PdaOwnerInfo.JobTitle != null)
{
_owner = state.PdaOwnerInfo.IdOwner ?? Loc.GetString("comp-pda-ui-unknown");
_jobTitle = state.PdaOwnerInfo.JobTitle ?? Loc.GetString("comp-pda-ui-unassigned");
_owner = FormattedMessage.EscapeText(state.PdaOwnerInfo.IdOwner ?? Loc.GetString("comp-pda-ui-unknown")); // DeltaV - Escape owner and job
_jobTitle = FormattedMessage.EscapeText(state.PdaOwnerInfo.JobTitle ?? Loc.GetString("comp-pda-ui-unassigned")); // DeltaV - Escape owner and job
IdInfoLabel.SetMarkup(Loc.GetString("comp-pda-ui",
("owner", _owner),
("jobTitle", _jobTitle)));

View File

@ -4,6 +4,7 @@ using Content.Shared.Access.Components;
using Robust.Client.AutoGenerated;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.XAML;
using Robust.Shared.Utility;
namespace Content.Client._DV.CartridgeLoader.Cartridges;
@ -38,9 +39,10 @@ public sealed partial class NanoChatEntry : BoxContainer
_pressHandler = _ => OnPressed?.Invoke(_number);
ChatButton.OnPressed += _pressHandler;
NameLabel.Text = SharedNanoChatSystem.Truncate(recipient.Name, _maxNameLength);
JobLabel.Text = SharedNanoChatSystem.Truncate(recipient.JobTitle ?? "", _maxIdJobLength);
JobLabel.Visible = !string.IsNullOrEmpty(recipient.JobTitle);
// FIXME: Truncate and Escape can lead to stray `\`, if the truncate cuts a markup tag in half
NameLabel.Text = FormattedMessage.EscapeText(SharedNanoChatSystem.Truncate(recipient.Name, _maxNameLength));
JobLabel.Text = FormattedMessage.EscapeText(SharedNanoChatSystem.Truncate(recipient.JobTitle ?? string.Empty, _maxIdJobLength));
JobLabel.Visible = !string.IsNullOrWhiteSpace(JobLabel.Text);
UnreadIndicator.Visible = recipient.HasUnread;
ChatButton.ModulateSelfOverride = isSelected ? NanoChatMessageBubble.OwnMessageColor : null;

View File

@ -3,6 +3,7 @@ using Robust.Client.AutoGenerated;
using Robust.Client.Graphics;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.XAML;
using Robust.Shared.Utility;
namespace Content.Client._DV.CartridgeLoader.Cartridges;
@ -31,7 +32,7 @@ public sealed partial class NanoChatMessageBubble : BoxContainer
style.BorderColor = BorderColor;
// Set message content
MessageText.Text = message.Content;
MessageText.Text = FormattedMessage.EscapeText(message.Content);
MessageText.Modulate = TextColor;
// Show delivery failed text if needed (only for own messages)

View File

@ -69,12 +69,14 @@ public sealed class IdExaminableSystem : EntitySystem
{
var jobSuffix = string.IsNullOrWhiteSpace(id.LocalizedJobTitle) ? string.Empty : $" ({id.LocalizedJobTitle})";
var val = string.IsNullOrWhiteSpace(id.FullName)
var val = FormattedMessage.EscapeText( // DeltaV - Escape job
string.IsNullOrWhiteSpace(id.FullName)
? Loc.GetString(id.NameLocId,
("jobSuffix", jobSuffix))
: Loc.GetString(id.FullNameLocId,
("fullName", id.FullName),
("jobSuffix", jobSuffix));
("jobSuffix", jobSuffix))
);
return val;
}

View File

@ -60,7 +60,7 @@ public sealed partial class LabelSystem : EntitySystem
return;
label ??= EnsureComp<LabelComponent>(uid);
label.CurrentLabel = text;
label.CurrentLabel = text == null ? null : FormattedMessage.EscapeText(text);
_nameModifier.RefreshNameModifiers(uid);
Dirty(uid, label);