From bb61efabf36efc524c7e307f5b7e6718089c07d0 Mon Sep 17 00:00:00 2001 From: Chris Johnston Date: Mon, 13 May 2019 15:27:45 -0700 Subject: [PATCH] feature: Check for whitespace or newline characters in tokens (#1305) * Trim whitespace from tokens before logging in This change trims whitespace characters from the supplied token before it is used to log in. Users can encounter this accidentally if they read their token from a file that ends with a blank line. Leading whitespace will make the token invalid. Trailing whitespace or \n (not \r\n) will also fail to log in. \r\n (CRLF) doesn't fail because of the line break style for http request headers. * revert trimming api token * add check for whitespace or newline characters to existing token validation Checks to see if a token contains any illegal characters, like whitespace or a newline. If it is, throws an ArgumentException warning the user that their token may be invalid. I considered only checking the first and last character, but given that a token containing whitespace or a newline wouldn't work either I figured this made sense. * removed unused usings These were leftover from a previous approach using an ImmutableHashSet --- src/Discord.Net.Core/Utils/TokenUtils.cs | 22 ++++++++++++++++++++++ test/Discord.Net.Tests/Tests.TokenUtils.cs | 10 ++++++++++ 2 files changed, 32 insertions(+) diff --git a/src/Discord.Net.Core/Utils/TokenUtils.cs b/src/Discord.Net.Core/Utils/TokenUtils.cs index b52ba3dd6..2efb1822a 100644 --- a/src/Discord.Net.Core/Utils/TokenUtils.cs +++ b/src/Discord.Net.Core/Utils/TokenUtils.cs @@ -119,6 +119,25 @@ namespace Discord return DecodeBase64UserId(segments[0]).HasValue; } + /// + /// The set of all characters that are not allowed inside of a token. + /// + internal static char[] IllegalTokenCharacters = new char[] + { + ' ', '\t', '\r', '\n' + }; + + /// + /// Checks if the given token contains a whitespace or newline character + /// that would fail to log in. + /// + /// The token to validate. + /// + /// True if the token contains a whitespace or newline character. + /// + internal static bool CheckContainsIllegalCharacters(string token) + => token.IndexOfAny(IllegalTokenCharacters) != -1; + /// /// Checks the validity of the supplied token of a specific type. /// @@ -131,6 +150,9 @@ namespace Discord // A Null or WhiteSpace token of any type is invalid. if (string.IsNullOrWhiteSpace(token)) throw new ArgumentNullException(paramName: nameof(token), message: "A token cannot be null, empty, or contain only whitespace."); + // ensure that there are no whitespace or newline characters + if (CheckContainsIllegalCharacters(token)) + throw new ArgumentException(message: "The token contains a whitespace or newline character. Ensure that the token has been properly trimmed.", paramName: nameof(token)); switch (tokenType) { diff --git a/test/Discord.Net.Tests/Tests.TokenUtils.cs b/test/Discord.Net.Tests/Tests.TokenUtils.cs index d9ed60ae8..428323c1d 100644 --- a/test/Discord.Net.Tests/Tests.TokenUtils.cs +++ b/test/Discord.Net.Tests/Tests.TokenUtils.cs @@ -99,6 +99,16 @@ namespace Discord [InlineData("937it3ow87i4ery69876wqire")] // 57 char bot token [InlineData("MTk4NjIyNDgzNDcxOTI1MjQ4.Cl2FMQ.ZnCjm1XVW7vRze4b7Cq4se7kK")] + // ends with invalid characters + [InlineData("MTk4NjIyNDgzNDcxOTI1MjQ4.Cl2FMQ.ZnCjm1XVW7vRze4b7Cq4se7k ")] + [InlineData("MTk4NjIyNDgzNDcxOTI1MjQ4.Cl2FMQ.ZnCjm1XVW7vRze4b7Cq4se7k\n")] + [InlineData("MTk4NjIyNDgzNDcxOTI1MjQ4.Cl2FMQ.ZnCjm1XVW7vRze4b7Cq4se7k\t")] + [InlineData("MTk4NjIyNDgzNDcxOTI1MjQ4.Cl2FMQ.ZnCjm1XVW7vRze4b7Cq4se7k\r\n")] + // starts with invalid characters + [InlineData(" MTk4NjIyNDgzNDcxOTI1MjQ4.Cl2FMQ.ZnCjm1XVW7vRze4b7Cq4se7k")] + [InlineData("\nMTk4NjIyNDgzNDcxOTI1MjQ4.Cl2FMQ.ZnCjm1XVW7vRze4b7Cq4se7k")] + [InlineData("\tMTk4NjIyNDgzNDcxOTI1MjQ4.Cl2FMQ.ZnCjm1XVW7vRze4b7Cq4se7k")] + [InlineData("\r\nMTk4NjIyNDgzNDcxOTI1MjQ4.Cl2FMQ.ZnCjm1XVW7vRze4b7Cq4se7k")] [InlineData("This is an invalid token, but it passes the check for string length.")] // valid token, but passed in twice [InlineData("MTk4NjIyNDgzNDcxOTI1MjQ4.Cl2FMQ.ZnCjm1XVW7vRze4b7Cq4se7kKWsMTk4NjIyNDgzNDcxOTI1MjQ4.Cl2FMQ.ZnCjm1XVW7vRze4b7Cq4se7kKWs")]