mirror of
https://github.com/Tyrrrz/DiscordChatExporter.git
synced 2026-08-13 22:13:03 -06:00
Only allow http(s) URLs in exported HTML links (fixes #1575)
Embed URLs never pass through the markdown parser, so the regex change in #1568 did not cover them and a javascript: URL in embed.url or embed.author.url still reached a live href. Adds ExportContext.EnsureSafeUrl, which returns the URL only when it parses as an absolute http or https URI, and applies it at the four embed anchors. An unsafe URL now takes the existing else branch, so the title and author text still render, just without a link. HtmlMarkdownVisitor.VisitLinkAsync uses the same helper and renders the link children without an anchor when the scheme is not allowed, so the three link regexes in MarkdownParser are no longer the only thing keeping unsafe schemes out of the export.
This commit is contained in:
parent
b89dd4c924
commit
ce5207c8a0
|
|
@ -120,6 +120,12 @@ internal class ExportContext(DiscordClient discord, ExportRequest request)
|
||||||
public Color? TryGetUserColor(Snowflake id) =>
|
public Color? TryGetUserColor(Snowflake id) =>
|
||||||
GetUserRoles(id).Where(r => r.Color is not null).Select(r => r.Color).FirstOrDefault();
|
GetUserRoles(id).Where(r => r.Color is not null).Select(r => r.Color).FirstOrDefault();
|
||||||
|
|
||||||
|
public static string? EnsureSafeUrl(string? url) =>
|
||||||
|
Uri.TryCreate(url, UriKind.Absolute, out var uri)
|
||||||
|
&& (uri.Scheme == Uri.UriSchemeHttp || uri.Scheme == Uri.UriSchemeHttps)
|
||||||
|
? url
|
||||||
|
: null;
|
||||||
|
|
||||||
public async ValueTask<string> ResolveAssetUrlAsync(
|
public async ValueTask<string> ResolveAssetUrlAsync(
|
||||||
string url,
|
string url,
|
||||||
CancellationToken cancellationToken = default
|
CancellationToken cancellationToken = default
|
||||||
|
|
|
||||||
|
|
@ -188,12 +188,19 @@ internal partial class HtmlMarkdownVisitor(
|
||||||
.Groups[1]
|
.Groups[1]
|
||||||
.Value;
|
.Value;
|
||||||
|
|
||||||
|
var safeUrl = ExportContext.EnsureSafeUrl(link.Url);
|
||||||
|
if (string.IsNullOrWhiteSpace(safeUrl))
|
||||||
|
{
|
||||||
|
await VisitAsync(link.Children, cancellationToken);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
buffer.Append(
|
buffer.Append(
|
||||||
!string.IsNullOrWhiteSpace(linkedMessageId)
|
!string.IsNullOrWhiteSpace(linkedMessageId)
|
||||||
// lang=html
|
// lang=html
|
||||||
? $"""<a href="{HtmlEncode(link.Url)}" onclick="scrollToMessage(event, '{linkedMessageId}')">"""
|
? $"""<a href="{HtmlEncode(safeUrl)}" onclick="scrollToMessage(event, '{linkedMessageId}')">"""
|
||||||
// lang=html
|
// lang=html
|
||||||
: $"""<a href="{HtmlEncode(link.Url)}">"""
|
: $"""<a href="{HtmlEncode(safeUrl)}">"""
|
||||||
);
|
);
|
||||||
|
|
||||||
await VisitAsync(link.Children, cancellationToken);
|
await VisitAsync(link.Children, cancellationToken);
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,9 @@
|
||||||
string FormatDate(DateTimeOffset instant, string format = "g") =>
|
string FormatDate(DateTimeOffset instant, string format = "g") =>
|
||||||
Context.FormatDate(instant, format);
|
Context.FormatDate(instant, format);
|
||||||
|
|
||||||
|
string? EnsureSafeUrl(string? url) =>
|
||||||
|
ExportContext.EnsureSafeUrl(url);
|
||||||
|
|
||||||
async ValueTask<IEncodedContent> FormatMarkdownAsync(string markdown) =>
|
async ValueTask<IEncodedContent> FormatMarkdownAsync(string markdown) =>
|
||||||
Context.Request.ShouldFormatMarkdown
|
Context.Request.ShouldFormatMarkdown
|
||||||
? Html.Raw(await HtmlMarkdownVisitor.FormatAsync(Context, markdown, true, CancellationToken))
|
? Html.Raw(await HtmlMarkdownVisitor.FormatAsync(Context, markdown, true, CancellationToken))
|
||||||
|
|
@ -484,9 +487,10 @@
|
||||||
|
|
||||||
@if (!string.IsNullOrWhiteSpace(embed.Author.Name))
|
@if (!string.IsNullOrWhiteSpace(embed.Author.Name))
|
||||||
{
|
{
|
||||||
if (!string.IsNullOrWhiteSpace(embed.Author.Url))
|
var authorUrl = EnsureSafeUrl(embed.Author.Url);
|
||||||
|
if (!string.IsNullOrWhiteSpace(authorUrl))
|
||||||
{
|
{
|
||||||
<a class="chatlog__embed-author-link" href="@embed.Author.Url">
|
<a class="chatlog__embed-author-link" href="@authorUrl">
|
||||||
<div class="chatlog__embed-author">@embed.Author.Name</div>
|
<div class="chatlog__embed-author">@embed.Author.Name</div>
|
||||||
</a>
|
</a>
|
||||||
}
|
}
|
||||||
|
|
@ -502,9 +506,12 @@
|
||||||
@if (!string.IsNullOrWhiteSpace(embed.Title))
|
@if (!string.IsNullOrWhiteSpace(embed.Title))
|
||||||
{
|
{
|
||||||
<div class="chatlog__embed-title">
|
<div class="chatlog__embed-title">
|
||||||
@if (!string.IsNullOrWhiteSpace(embed.Url))
|
@{
|
||||||
|
var titleUrl = EnsureSafeUrl(embed.Url);
|
||||||
|
}
|
||||||
|
@if (!string.IsNullOrWhiteSpace(titleUrl))
|
||||||
{
|
{
|
||||||
<a class="chatlog__embed-title-link" href="@embed.Url">
|
<a class="chatlog__embed-title-link" href="@titleUrl">
|
||||||
<div class="chatlog__markdown chatlog__markdown-preserve"><!--wmm:ignore-->@(await FormatEmbedMarkdownAsync(embed.Title))<!--/wmm:ignore--></div>
|
<div class="chatlog__markdown chatlog__markdown-preserve"><!--wmm:ignore-->@(await FormatEmbedMarkdownAsync(embed.Title))<!--/wmm:ignore--></div>
|
||||||
</a>
|
</a>
|
||||||
}
|
}
|
||||||
|
|
@ -604,9 +611,10 @@
|
||||||
|
|
||||||
@if (!string.IsNullOrWhiteSpace(embed.Author.Name))
|
@if (!string.IsNullOrWhiteSpace(embed.Author.Name))
|
||||||
{
|
{
|
||||||
if (!string.IsNullOrWhiteSpace(embed.Author.Url))
|
var authorUrl = EnsureSafeUrl(embed.Author.Url);
|
||||||
|
if (!string.IsNullOrWhiteSpace(authorUrl))
|
||||||
{
|
{
|
||||||
<a class="chatlog__embed-author-link" href="@embed.Author.Url">
|
<a class="chatlog__embed-author-link" href="@authorUrl">
|
||||||
<div class="chatlog__embed-author">@embed.Author.Name</div>
|
<div class="chatlog__embed-author">@embed.Author.Name</div>
|
||||||
</a>
|
</a>
|
||||||
}
|
}
|
||||||
|
|
@ -622,9 +630,12 @@
|
||||||
@if (!string.IsNullOrWhiteSpace(embed.Title))
|
@if (!string.IsNullOrWhiteSpace(embed.Title))
|
||||||
{
|
{
|
||||||
<div class="chatlog__embed-title">
|
<div class="chatlog__embed-title">
|
||||||
@if (!string.IsNullOrWhiteSpace(embed.Url))
|
@{
|
||||||
|
var titleUrl = EnsureSafeUrl(embed.Url);
|
||||||
|
}
|
||||||
|
@if (!string.IsNullOrWhiteSpace(titleUrl))
|
||||||
{
|
{
|
||||||
<a class="chatlog__embed-title-link" href="@embed.Url">
|
<a class="chatlog__embed-title-link" href="@titleUrl">
|
||||||
<div class="chatlog__markdown chatlog__markdown-preserve"><!--wmm:ignore-->@(await FormatEmbedMarkdownAsync(embed.Title))<!--/wmm:ignore--></div>
|
<div class="chatlog__markdown chatlog__markdown-preserve"><!--wmm:ignore-->@(await FormatEmbedMarkdownAsync(embed.Title))<!--/wmm:ignore--></div>
|
||||||
</a>
|
</a>
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue