Break proxied links if the sender doesn't have embed permission

This commit is contained in:
Ske 2020-07-10 16:35:52 +02:00
parent a2930acbad
commit ba48f22ad2
2 changed files with 30 additions and 6 deletions

View File

@ -57,11 +57,14 @@ namespace PluralKit.Bot
if (!await CheckBotPermissionsOrError(message.Channel)) return false; if (!await CheckBotPermissionsOrError(message.Channel)) return false;
if (!CheckProxyNameBoundsOrError(match.Member.ProxyName(ctx))) return false; if (!CheckProxyNameBoundsOrError(match.Member.ProxyName(ctx))) return false;
// Check if we can mention everyone/here // Check if the sender account can mention everyone/here + embed links
var allowEveryone = (message.Channel.PermissionsInSync(message.Author) & Permissions.MentionEveryone) != 0; // we need to "mirror" these permissions when proxying to prevent exploits
var senderPermissions = message.Channel.PermissionsInSync(message.Author);
var allowEveryone = (senderPermissions & Permissions.MentionEveryone) != 0;
var allowEmbeds = (senderPermissions & Permissions.EmbedLinks) != 0;
// Everything's in order, we can execute the proxy! // Everything's in order, we can execute the proxy!
await ExecuteProxy(conn, message, ctx, match, allowEveryone); await ExecuteProxy(conn, message, ctx, match, allowEveryone, allowEmbeds);
return true; return true;
} }
@ -88,12 +91,14 @@ namespace PluralKit.Bot
} }
private async Task ExecuteProxy(IPKConnection conn, DiscordMessage trigger, MessageContext ctx, private async Task ExecuteProxy(IPKConnection conn, DiscordMessage trigger, MessageContext ctx,
ProxyMatch match, bool allowEveryone) ProxyMatch match, bool allowEveryone, bool allowEmbeds)
{ {
// Send the webhook // Send the webhook
var content = match.ProxyContent;
if (!allowEmbeds) content = content.BreakLinkEmbeds();
var id = await _webhookExecutor.ExecuteWebhook(trigger.Channel, match.Member.ProxyName(ctx), var id = await _webhookExecutor.ExecuteWebhook(trigger.Channel, match.Member.ProxyName(ctx),
match.Member.ProxyAvatar(ctx), match.Member.ProxyAvatar(ctx),
match.ProxyContent, trigger.Attachments, allowEveryone); content, trigger.Attachments, allowEveryone);
Task SaveMessage() => _data.AddMessage(conn, trigger.Author.Id, trigger.Channel.GuildId, trigger.Channel.Id, id, trigger.Id, match.Member.Id); Task SaveMessage() => _data.AddMessage(conn, trigger.Author.Id, trigger.Channel.GuildId, trigger.Channel.Id, id, trigger.Id, match.Member.Id);
Task LogMessage() => _logChannel.LogMessage(ctx, match, trigger, id).AsTask(); Task LogMessage() => _logChannel.LogMessage(ctx, match, trigger, id).AsTask();

View File

@ -30,6 +30,14 @@ namespace PluralKit.Bot
private static readonly Regex ROLE_MENTION = new Regex("<@&(\\d{17,19})>"); private static readonly Regex ROLE_MENTION = new Regex("<@&(\\d{17,19})>");
private static readonly Regex EVERYONE_HERE_MENTION = new Regex("@(everyone|here)"); private static readonly Regex EVERYONE_HERE_MENTION = new Regex("@(everyone|here)");
// Discord uses Khan Academy's simple-markdown library for parsing Markdown,
// which uses the following regex for link detection:
// ^(https?:\/\/[^\s<]+[^<.,:;"')\]\s])
// Source: https://raw.githubusercontent.com/DJScias/Discord-Datamining/master/2020/2020-07-10/47efb8681861cb7c5ffa.js @ line 20633
// corresponding to: https://github.com/Khan/simple-markdown/blob/master/src/index.js#L1489
// I added <? and >? at the start/end; they need to be handled specially later...
private static readonly Regex UNBROKEN_LINK_REGEX = new Regex("<?(https?:\\/\\/[^\\s<]+[^<.,:;\"')\\]\\s])>?");
private static readonly FieldInfo _roleIdsField = typeof(DiscordMember).GetField("_role_ids", BindingFlags.NonPublic | BindingFlags.Instance); private static readonly FieldInfo _roleIdsField = typeof(DiscordMember).GetField("_role_ids", BindingFlags.NonPublic | BindingFlags.Instance);
public static string NameAndMention(this DiscordUser user) { public static string NameAndMention(this DiscordUser user) {
@ -197,7 +205,7 @@ namespace PluralKit.Bot
public static string EscapeBacktickPair(this string input){ public static string EscapeBacktickPair(this string input){
Regex doubleBacktick = new Regex(@"``", RegexOptions.Multiline); Regex doubleBacktick = new Regex(@"``", RegexOptions.Multiline);
//Run twice to catch any pairs that are created from the first pass, pairs shouldn't be created in the second as they are created from odd numbers of backticks, even numbers are all caught on the first pass //Run twice to catch any pairs that are created from the first pass, pairs shouldn't be created in the second as they are created from odd numbers of backticks, even numbers are all caught on the first pass
if(input != null) return doubleBacktick.Replace(doubleBacktick.Replace(input, @"``"),@"``"); if(input != null) return doubleBacktick.Replace(doubleBacktick.Replace(input, @"` `"),@"``");
else return input; else return input;
} }
@ -277,5 +285,16 @@ namespace PluralKit.Bot
return eb; return eb;
} }
public static string BreakLinkEmbeds(this string str) =>
// Encases URLs in <brackets>
UNBROKEN_LINK_REGEX.Replace(str, match =>
{
// Don't break already-broken links
// The regex will include the brackets in the match, so we can check for their presence here
if (match.Value.StartsWith("<") && match.Value.EndsWith(">"))
return match.Value;
return $"<{match.Value}>";
});
} }
} }