fix(discord): sanitize embed join links
This commit is contained in:
@@ -272,14 +272,16 @@ public sealed class DiscordPlatformMessenger : IPlatformMessenger
|
||||
? "—"
|
||||
: string.Join(", ", notification.ConfirmedPlayers.Select(p => Mention(p.User)));
|
||||
|
||||
return new EmbedProperties()
|
||||
var embed = new EmbedProperties()
|
||||
.WithTitle($"Ссылка на игру: {notification.Title}")
|
||||
.WithDescription(
|
||||
$"Время: **{notification.ScheduledAt.FormatMoscow()}** (МСК)\n" +
|
||||
$"Ссылка: {notification.JoinLink}\n\n" +
|
||||
$"Участники: {mentions}")
|
||||
.WithUrl(notification.JoinLink)
|
||||
.WithColor(new Color(0x57F287));
|
||||
|
||||
var embedUrl = DiscordEmbedUrls.NormalizeHttpUrl(notification.JoinLink);
|
||||
return embedUrl is null ? embed : embed.WithUrl(embedUrl);
|
||||
}
|
||||
|
||||
private static IReadOnlyList<ActionRowProperties> BuildRsvpRows(Guid sessionId, bool disabled)
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
namespace GmRelay.DiscordBot.Rendering;
|
||||
|
||||
public static class DiscordEmbedUrls
|
||||
{
|
||||
public static string? NormalizeHttpUrl(string? value)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(value))
|
||||
return null;
|
||||
|
||||
var candidate = value.Trim();
|
||||
if (IsSupportedHttpUrl(candidate, out var normalized))
|
||||
return normalized;
|
||||
|
||||
if (candidate.Contains("://", StringComparison.Ordinal))
|
||||
return null;
|
||||
|
||||
return IsSupportedHttpUrl($"https://{candidate}", out normalized)
|
||||
&& HasPublicHost(normalized)
|
||||
? normalized
|
||||
: null;
|
||||
}
|
||||
|
||||
private static bool IsSupportedHttpUrl(string value, out string normalized)
|
||||
{
|
||||
normalized = string.Empty;
|
||||
|
||||
if (!Uri.TryCreate(value, UriKind.Absolute, out var uri))
|
||||
return false;
|
||||
|
||||
if (!string.Equals(uri.Scheme, Uri.UriSchemeHttp, StringComparison.OrdinalIgnoreCase)
|
||||
&& !string.Equals(uri.Scheme, Uri.UriSchemeHttps, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
normalized = uri.ToString();
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool HasPublicHost(string value) =>
|
||||
Uri.TryCreate(value, UriKind.Absolute, out var uri)
|
||||
&& uri.Host.Contains('.', StringComparison.Ordinal);
|
||||
}
|
||||
@@ -70,9 +70,10 @@ public static class DiscordSessionBatchRenderer
|
||||
.WithInline()
|
||||
};
|
||||
|
||||
if (!string.IsNullOrEmpty(session.JoinLink))
|
||||
var embedUrl = DiscordEmbedUrls.NormalizeHttpUrl(session.JoinLink);
|
||||
if (embedUrl is not null)
|
||||
{
|
||||
embed = embed.WithUrl(session.JoinLink);
|
||||
embed = embed.WithUrl(embedUrl);
|
||||
}
|
||||
|
||||
embed = embed.WithColor(GetColor(session));
|
||||
|
||||
@@ -176,6 +176,32 @@ public sealed class DiscordSessionBatchRendererTests
|
||||
Assert.Equal("https://example.com/game", embeds[0].Url);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Render_ShouldNormalizeBareDomainJoinLinkForEmbedUrl()
|
||||
{
|
||||
var sessionId = Guid.NewGuid();
|
||||
var sessions = new[] { new SessionBatchDto(sessionId, DateTime.UtcNow, SessionStatus.Planned, 4, "mobaxterm.mobatek.net/game") };
|
||||
var participants = Array.Empty<ParticipantBatchDto>();
|
||||
|
||||
var view = SessionBatchViewBuilder.Build("Test", sessions, participants);
|
||||
var (embeds, _) = DiscordSessionBatchRenderer.Render(view);
|
||||
|
||||
Assert.Equal("https://mobaxterm.mobatek.net/game", embeds[0].Url);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Render_ShouldNotSetEmbedUrlWhenJoinLinkIsNotHttpUrl()
|
||||
{
|
||||
var sessionId = Guid.NewGuid();
|
||||
var sessions = new[] { new SessionBatchDto(sessionId, DateTime.UtcNow, SessionStatus.Planned, 4, "test") };
|
||||
var participants = Array.Empty<ParticipantBatchDto>();
|
||||
|
||||
var view = SessionBatchViewBuilder.Build("Test", sessions, participants);
|
||||
var (embeds, _) = DiscordSessionBatchRenderer.Render(view);
|
||||
|
||||
Assert.Null(embeds[0].Url);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Render_ShouldEmbedCorrectFieldValues()
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user