From 3999f735a712b59d5d0047d48cc65bbc0cd9ffec Mon Sep 17 00:00:00 2001 From: Mira <56395159+TheXorog@users.noreply.github.com> Date: Thu, 16 May 2024 22:20:44 +0200 Subject: [PATCH 1/8] feat: Add StripTokens Co-authored-by: quinchs <> --- DisCatSharp/Utilities.cs | 57 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/DisCatSharp/Utilities.cs b/DisCatSharp/Utilities.cs index 67f2a6bb64..21ea99d810 100644 --- a/DisCatSharp/Utilities.cs +++ b/DisCatSharp/Utilities.cs @@ -90,6 +90,63 @@ static Utilities() VersionHeader = $"DiscordBot (https://github.com/Aiko-IT-Systems/DisCatSharp, v{vs})"; } + + + /// + /// Removes discord-based tokens from a given string. + /// + /// The string to remove the tokens from. + /// A new string with the tokens replaced with {KEY_TOKEN} + public static string StripTokens(string str) + { + if (string.IsNullOrWhiteSpace(str)) + return str; + + var parts = str.Split('/'); + + // calculate the base64 data size + var base64Size = parts.Max(x => + { + var padding = x.EndsWith("==", StringComparison.Ordinal) + ? 2 + : x.EndsWith("=", StringComparison.Ordinal) + ? 1 + : 0; + return (Encoding.UTF8.GetByteCount(x) * (3 / 4)) - padding; + }); + + // allocate a buffer for any base64 decoding. + var dest = base64Size <= 1024 ? stackalloc byte[base64Size] : new byte[base64Size]; + + var isWebhook = parts.Contains("webhooks") || parts.Contains("webhook"); + + for (var i = 0; i < parts.Length; i++) + { + var part = parts[i]; + + if (isWebhook && Regex.IsMatch(part, @"([a-zA-Z0-9]{68})")) + { + parts[i] = "{WEBHOOK_TOKEN}"; + continue; + } + + if (!Convert.TryFromBase64String(part, dest, out var count)) + continue; + + var b64Content = Encoding.UTF8.GetString(dest[..count]); + + var tokenInnersMatch = Regex.Match(b64Content, @"^(.+):\d+?:[a-zA-Z0-9]{128}$"); + + if (!tokenInnersMatch.Success) + continue; + + // replaces the token with the {KEY}_TOKEN, ex 'interaction:USERID:TOKEN' is replaced as 'INTERACTION_TOKEN' + parts[i] = $"{{{tokenInnersMatch.Groups[1].Value.ToUpperInvariant()}_TOKEN}}"; + } + + return string.Join('/', parts); + } + /// /// Adds the specified parameter to the Query String. /// From 6e95e8c82797c715f42076883c02179c083aace2 Mon Sep 17 00:00:00 2001 From: Mira <56395159+TheXorog@users.noreply.github.com> Date: Thu, 16 May 2024 22:24:21 +0200 Subject: [PATCH 2/8] add breadcrumb filter --- DisCatSharp/Clients/BaseDiscordClient.cs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/DisCatSharp/Clients/BaseDiscordClient.cs b/DisCatSharp/Clients/BaseDiscordClient.cs index 25b00bfde0..e7d4bc0452 100644 --- a/DisCatSharp/Clients/BaseDiscordClient.cs +++ b/DisCatSharp/Clients/BaseDiscordClient.cs @@ -249,6 +249,15 @@ protected BaseDiscordClient(DiscordConfiguration config) EnableScopeSync = true, Debug = this.Configuration.SentryDebug }; + + options.SetBeforeBreadcrumb(b + => new Breadcrumb(Utilities.StripTokens(b.Message), + b.Type, + b.Data?.Select(x => new KeyValuePair(x.Key, Utilities.StripTokens(x.Value))) + .ToDictionary(x => x.Key, x => x.Value), + b.Category, + b.Level)); + options.SetBeforeSend((e, _) => { if (!this.Configuration.DisableExceptionFilter) From 22a4ce97a478b46798efeb5edf8add013cb3536a Mon Sep 17 00:00:00 2001 From: Mira <56395159+TheXorog@users.noreply.github.com> Date: Thu, 16 May 2024 22:37:49 +0200 Subject: [PATCH 3/8] add before transaction filter --- DisCatSharp/Clients/BaseDiscordClient.cs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/DisCatSharp/Clients/BaseDiscordClient.cs b/DisCatSharp/Clients/BaseDiscordClient.cs index e7d4bc0452..d0f3525782 100644 --- a/DisCatSharp/Clients/BaseDiscordClient.cs +++ b/DisCatSharp/Clients/BaseDiscordClient.cs @@ -258,6 +258,14 @@ protected BaseDiscordClient(DiscordConfiguration config) b.Category, b.Level)); + options.SetBeforeSendTransaction(tr => + { + if (tr.Request.Data is string str) + tr.Request.Data = Utilities.StripTokens(str); + + return tr; + }); + options.SetBeforeSend((e, _) => { if (!this.Configuration.DisableExceptionFilter) From ab6c303f2ae873c9751fb3cbab13f1ded44e8faf Mon Sep 17 00:00:00 2001 From: Mira <56395159+TheXorog@users.noreply.github.com> Date: Thu, 16 May 2024 23:41:55 +0200 Subject: [PATCH 4/8] simplify implementation + and actually make it work in this use case --- DisCatSharp/Utilities.cs | 48 +++++----------------------------------- 1 file changed, 5 insertions(+), 43 deletions(-) diff --git a/DisCatSharp/Utilities.cs b/DisCatSharp/Utilities.cs index 21ea99d810..0b7810b9f9 100644 --- a/DisCatSharp/Utilities.cs +++ b/DisCatSharp/Utilities.cs @@ -97,54 +97,16 @@ static Utilities() /// /// The string to remove the tokens from. /// A new string with the tokens replaced with {KEY_TOKEN} - public static string StripTokens(string str) + public static string? StripTokens(string? str) { if (string.IsNullOrWhiteSpace(str)) return str; - var parts = str.Split('/'); + str = Regex.Replace(str, @"([a-zA-Z0-9]{68})", "{WEBHOOK_TOKEN}"); + str = Regex.Replace(str, @"^(.+):\d+?:[a-zA-Z0-9]{128}$", "{INTERACTION_TOKEN}"); + str = Regex.Replace(str, @"(mfa\.[a-z0-9_-]{20,})|((?[a-z0-9_-]{23,28})\.(?[a-z0-9_-]{6,7})\.(?[a-z0-9_-]{27,}))", "{ACCOUNT_TOKEN}"); - // calculate the base64 data size - var base64Size = parts.Max(x => - { - var padding = x.EndsWith("==", StringComparison.Ordinal) - ? 2 - : x.EndsWith("=", StringComparison.Ordinal) - ? 1 - : 0; - return (Encoding.UTF8.GetByteCount(x) * (3 / 4)) - padding; - }); - - // allocate a buffer for any base64 decoding. - var dest = base64Size <= 1024 ? stackalloc byte[base64Size] : new byte[base64Size]; - - var isWebhook = parts.Contains("webhooks") || parts.Contains("webhook"); - - for (var i = 0; i < parts.Length; i++) - { - var part = parts[i]; - - if (isWebhook && Regex.IsMatch(part, @"([a-zA-Z0-9]{68})")) - { - parts[i] = "{WEBHOOK_TOKEN}"; - continue; - } - - if (!Convert.TryFromBase64String(part, dest, out var count)) - continue; - - var b64Content = Encoding.UTF8.GetString(dest[..count]); - - var tokenInnersMatch = Regex.Match(b64Content, @"^(.+):\d+?:[a-zA-Z0-9]{128}$"); - - if (!tokenInnersMatch.Success) - continue; - - // replaces the token with the {KEY}_TOKEN, ex 'interaction:USERID:TOKEN' is replaced as 'INTERACTION_TOKEN' - parts[i] = $"{{{tokenInnersMatch.Groups[1].Value.ToUpperInvariant()}_TOKEN}}"; - } - - return string.Join('/', parts); + return str; } /// From 4e4f0183054448277929a4a7d66cea01e3335203 Mon Sep 17 00:00:00 2001 From: Mira <56395159+TheXorog@users.noreply.github.com> Date: Fri, 17 May 2024 16:14:58 +0200 Subject: [PATCH 5/8] fix regex to actually match tokens & simplify Co-authored-by: quinchs <49576606+quinchs@users.noreply.github.com> --- DisCatSharp/Utilities.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/DisCatSharp/Utilities.cs b/DisCatSharp/Utilities.cs index 0b7810b9f9..2860e9a6be 100644 --- a/DisCatSharp/Utilities.cs +++ b/DisCatSharp/Utilities.cs @@ -102,8 +102,7 @@ static Utilities() if (string.IsNullOrWhiteSpace(str)) return str; - str = Regex.Replace(str, @"([a-zA-Z0-9]{68})", "{WEBHOOK_TOKEN}"); - str = Regex.Replace(str, @"^(.+):\d+?:[a-zA-Z0-9]{128}$", "{INTERACTION_TOKEN}"); + str = Regex.Replace(str, @"([a-zA-Z0-9]{68,})", "{WEBHOOK_OR_INTERACTION_TOKEN}"); // Any alphanumeric string this long is likely to be sensitive information anyways str = Regex.Replace(str, @"(mfa\.[a-z0-9_-]{20,})|((?[a-z0-9_-]{23,28})\.(?[a-z0-9_-]{6,7})\.(?[a-z0-9_-]{27,}))", "{ACCOUNT_TOKEN}"); return str; From 37e408bbe52df6ea45bff63ecd0c02cf0a042ad1 Mon Sep 17 00:00:00 2001 From: Lala Sabathil Date: Sun, 19 May 2024 07:38:36 +0200 Subject: [PATCH 6/8] Update DisCatSharp/Utilities.cs Signed-off-by: Lala Sabathil --- DisCatSharp/Utilities.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DisCatSharp/Utilities.cs b/DisCatSharp/Utilities.cs index 2860e9a6be..1b4a243543 100644 --- a/DisCatSharp/Utilities.cs +++ b/DisCatSharp/Utilities.cs @@ -103,7 +103,7 @@ static Utilities() return str; str = Regex.Replace(str, @"([a-zA-Z0-9]{68,})", "{WEBHOOK_OR_INTERACTION_TOKEN}"); // Any alphanumeric string this long is likely to be sensitive information anyways - str = Regex.Replace(str, @"(mfa\.[a-z0-9_-]{20,})|((?[a-z0-9_-]{23,28})\.(?[a-z0-9_-]{6,7})\.(?[a-z0-9_-]{27,}))", "{ACCOUNT_TOKEN}"); + str = Regex.Replace(str, @"(mfa\.[a-z0-9_-]{20,})|((?[a-z0-9_-]{23,28})\.(?[a-z0-9_-]{6,7})\.(?[a-z0-9_-]{27,}))", "{BOT_OR_USER_TOKEN}"); return str; } From 9cb90eea2bab87ca922b9555616803f8b629bf27 Mon Sep 17 00:00:00 2001 From: Lala Sabathil Date: Fri, 24 May 2024 09:48:24 +0200 Subject: [PATCH 7/8] chore: fix docs --- DisCatSharp/DiscordConfiguration.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/DisCatSharp/DiscordConfiguration.cs b/DisCatSharp/DiscordConfiguration.cs index 891128dd03..60d96f3551 100644 --- a/DisCatSharp/DiscordConfiguration.cs +++ b/DisCatSharp/DiscordConfiguration.cs @@ -239,10 +239,12 @@ public UdpClientFactoryDelegate UdpClientFactory /// public IServiceProvider ServiceProvider { internal get; init; } = new ServiceCollection().BuildServiceProvider(true); + // TODO: Add disclaimer and docs for sentry /// - /// Whether to report missing fields for discord object. + /// Whether to emable sentry. /// This helps us to track missing data and library bugs better. /// Defaults to . + /// TODO: Add disclaimer and docs. /// public bool EnableSentry { internal get; set; } = false; From 50ccaa229f2d93dc7d523de16ae7f7d61167df49 Mon Sep 17 00:00:00 2001 From: Lala Sabathil Date: Fri, 24 May 2024 09:49:00 +0200 Subject: [PATCH 8/8] revert: ex can be in senex since it's not read by sentry itself, rather if u manually call it might need more fixes --- DisCatSharp/Net/Rest/RestClient.cs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/DisCatSharp/Net/Rest/RestClient.cs b/DisCatSharp/Net/Rest/RestClient.cs index 40bd26eb2b..1060194935 100644 --- a/DisCatSharp/Net/Rest/RestClient.cs +++ b/DisCatSharp/Net/Rest/RestClient.cs @@ -563,8 +563,7 @@ private async Task ExecuteRequestAsync(BaseRestRequest request, RateLimitBucket? case HttpStatusCode.BadRequest: case HttpStatusCode.MethodNotAllowed: ex = new BadRequestException(request, response); - // ex won't be added to avoid possible leaks - senex = new(ex.Message + "\nJson Response: " + ((ex as BadRequestException)?.JsonMessage ?? "null")); + senex = new(ex.Message + "\nJson Response: " + ((ex as BadRequestException)?.JsonMessage ?? "null"), ex); break; case HttpStatusCode.Unauthorized: @@ -629,8 +628,7 @@ private async Task ExecuteRequestAsync(BaseRestRequest request, RateLimitBucket? case HttpStatusCode.ServiceUnavailable: case HttpStatusCode.GatewayTimeout: ex = new ServerErrorException(request, response); - // ex won't be added to avoid possible leaks - senex = new(ex.Message + "\nJson Response: " + ((ex as ServerErrorException)!.JsonMessage ?? "null")); + senex = new(ex.Message + "\nJson Response: " + ((ex as ServerErrorException)!.JsonMessage ?? "null"), ex); break; }