-
-
Notifications
You must be signed in to change notification settings - Fork 741
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add validation to bot tokens based on string length (#1128)
* Add input validation for bot tokens based on their length * Add token validation to BaseDiscordClient#LoginAsync Adds a TokenUtils class which is used to validate that tokens are correct * Revert changes to DiscordRestApiClient * Add Unit tests to the TokenUtils class, fix a logic error that was caught by those tests * Allow for API to throw exceptions Moves the validation of tokens to be inside of LoginInternalAsync, and writes a Warning to the console when the supplied tokens are invalid
- Loading branch information
1 parent
efdb4f9
commit 2de6cef
Showing
3 changed files
with
187 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Linq; | ||
using System.Text; | ||
using System.Threading.Tasks; | ||
|
||
namespace Discord | ||
{ | ||
public static class TokenUtils | ||
{ | ||
/// <summary> | ||
/// Checks the validity of the supplied token of a specific type. | ||
/// </summary> | ||
/// <param name="tokenType"> The type of token to validate. </param> | ||
/// <param name="token"> The token value to validate. </param> | ||
/// <exception cref="ArgumentNullException"> Thrown when the supplied token string is null, empty, or contains only whitespace.</exception> | ||
/// <exception cref="ArgumentException"> Thrown when the supplied TokenType or token value is invalid. </exception> | ||
public static void ValidateToken(TokenType tokenType, string token) | ||
{ | ||
// A Null or WhiteSpace token of any type is invalid. | ||
if (string.IsNullOrWhiteSpace(token)) | ||
throw new ArgumentNullException("A token cannot be null, empty, or contain only whitespace.", nameof(token)); | ||
|
||
switch (tokenType) | ||
{ | ||
case TokenType.Webhook: | ||
// no validation is performed on Webhook tokens | ||
break; | ||
case TokenType.Bearer: | ||
// no validation is performed on Bearer tokens | ||
break; | ||
case TokenType.Bot: | ||
// bot tokens are assumed to be at least 59 characters in length | ||
// this value was determined by referencing examples in the discord documentation, and by comparing with | ||
// pre-existing tokens | ||
if (token.Length < 59) | ||
throw new ArgumentException("A Bot token must be at least 59 characters in length.", nameof(token)); | ||
break; | ||
default: | ||
// All unrecognized TokenTypes (including User tokens) are considered to be invalid. | ||
throw new ArgumentException("Unrecognized TokenType.", nameof(token)); | ||
} | ||
} | ||
|
||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,124 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Text; | ||
using Xunit; | ||
|
||
namespace Discord | ||
{ | ||
public class TokenUtilsTests | ||
{ | ||
/// <summary> | ||
/// Tests the usage of <see cref="TokenUtils.ValidateToken(TokenType, string)"/> | ||
/// to see that when a null, empty or whitespace-only string is passed as the token, | ||
/// it will throw an ArgumentNullException. | ||
/// </summary> | ||
[Theory] | ||
[InlineData(null)] | ||
[InlineData("")] // string.Empty isn't a constant type | ||
[InlineData(" ")] | ||
[InlineData(" ")] | ||
[InlineData("\t")] | ||
public void TestNullOrWhitespaceToken(string token) | ||
{ | ||
// an ArgumentNullException should be thrown, regardless of the TokenType | ||
Assert.Throws<ArgumentNullException>(() => TokenUtils.ValidateToken(TokenType.Bearer, token)); | ||
Assert.Throws<ArgumentNullException>(() => TokenUtils.ValidateToken(TokenType.Bot, token)); | ||
Assert.Throws<ArgumentNullException>(() => TokenUtils.ValidateToken(TokenType.Webhook, token)); | ||
} | ||
|
||
/// <summary> | ||
/// Tests the behavior of <see cref="TokenUtils.ValidateToken(TokenType, string)"/> | ||
/// to see that valid Webhook tokens do not throw Exceptions. | ||
/// </summary> | ||
/// <param name="token"></param> | ||
[Theory] | ||
[InlineData("123123123")] | ||
// bot token | ||
[InlineData("MTk4NjIyNDgzNDcxOTI1MjQ4.Cl2FMQ.ZnCjm1XVW7vRze4b7Cq4se7kKWs")] | ||
// bearer token taken from discord docs | ||
[InlineData("6qrZcUqja7812RVdnEKjpzOL4CvHBFG")] | ||
// client secret | ||
[InlineData("937it3ow87i4ery69876wqire")] | ||
public void TestWebhookTokenDoesNotThrowExceptions(string token) | ||
{ | ||
TokenUtils.ValidateToken(TokenType.Webhook, token); | ||
} | ||
|
||
// No tests for invalid webhook token behavior, because there is nothing there yet. | ||
|
||
/// <summary> | ||
/// Tests the behavior of <see cref="TokenUtils.ValidateToken(TokenType, string)"/> | ||
/// to see that valid Webhook tokens do not throw Exceptions. | ||
/// </summary> | ||
/// <param name="token"></param> | ||
[Theory] | ||
[InlineData("123123123")] | ||
// bot token | ||
[InlineData("MTk4NjIyNDgzNDcxOTI1MjQ4.Cl2FMQ.ZnCjm1XVW7vRze4b7Cq4se7kKWs")] | ||
// bearer token taken from discord docs | ||
[InlineData("6qrZcUqja7812RVdnEKjpzOL4CvHBFG")] | ||
// client secret | ||
[InlineData("937it3ow87i4ery69876wqire")] | ||
public void TestBearerTokenDoesNotThrowExceptions(string token) | ||
{ | ||
TokenUtils.ValidateToken(TokenType.Bearer, token); | ||
} | ||
|
||
// No tests for invalid bearer token behavior, because there is nothing there yet. | ||
|
||
/// <summary> | ||
/// Tests the behavior of <see cref="TokenUtils.ValidateToken(TokenType, string)"/> | ||
/// to see that valid Bot tokens do not throw Exceptions. | ||
/// Valid Bot tokens can be strings of length 59 or above. | ||
/// </summary> | ||
[Theory] | ||
[InlineData("MTk4NjIyNDgzNDcxOTI1MjQ4.Cl2FMQ.ZnCjm1XVW7vRze4b7Cq4se7kKWs")] | ||
[InlineData("This appears to be completely invalid, however the current validation rules are not very strict.")] | ||
[InlineData("MTk4NjIyNDgzNDcxOTI1MjQ4.Cl2FMQ.ZnCjm1XVW7vRze4b7Cq4se7kKWss")] | ||
[InlineData("MTk4NjIyNDgzNDcxOTI1MjQ4.Cl2FMQ.ZnCjm1XVW7vRze4b7Cq4se7kKWsMTk4NjIyNDgzNDcxOTI1MjQ4.Cl2FMQ.ZnCjm1XVW7vRze4b7Cq4se7kKWs")] | ||
public void TestBotTokenDoesNotThrowExceptions(string token) | ||
{ | ||
// This example token is pulled from the Discord Docs | ||
// https://discordapp.com/developers/docs/reference#authentication-example-bot-token-authorization-header | ||
// should not throw any exception | ||
TokenUtils.ValidateToken(TokenType.Bot, token); | ||
} | ||
|
||
/// <summary> | ||
/// Tests the usage of <see cref="TokenUtils.ValidateToken(TokenType, string)"/> with | ||
/// a Bot token that is invalid. | ||
/// </summary> | ||
[Theory] | ||
[InlineData("This is invalid")] | ||
// missing a single character from the end | ||
[InlineData("MTk4NjIyNDgzNDcxOTI1MjQ4.Cl2FMQ.ZnCjm1XVW7vRze4b7Cq4se7kKW")] | ||
// bearer token | ||
[InlineData("6qrZcUqja7812RVdnEKjpzOL4CvHBFG")] | ||
// client secret | ||
[InlineData("937it3ow87i4ery69876wqire")] | ||
public void TestBotTokenInvalidThrowsArgumentException(string token) | ||
{ | ||
Assert.Throws<ArgumentException>(() => TokenUtils.ValidateToken(TokenType.Bot, token)); | ||
} | ||
|
||
/// <summary> | ||
/// Tests the behavior of <see cref="TokenUtils.ValidateToken(TokenType, string)"/> | ||
/// to see that an <see cref="ArgumentException"/> is thrown when an invalid | ||
/// <see cref="TokenType"/> is supplied as a parameter. | ||
/// </summary> | ||
/// <remarks> | ||
/// The <see cref="TokenType.User"/> type is treated as an invalid <see cref="TokenType"/>. | ||
/// </remarks> | ||
[Theory] | ||
// TokenType.User | ||
[InlineData(0)] | ||
// out of range TokenType | ||
[InlineData(4)] | ||
[InlineData(7)] | ||
public void TestUnrecognizedTokenType(int type) | ||
{ | ||
Assert.Throws<ArgumentException>(() => | ||
TokenUtils.ValidateToken((TokenType)type, "MTk4NjIyNDgzNDcxOTI1MjQ4.Cl2FMQ.ZnCjm1XVW7vRze4b7Cq4se7kKWs")); | ||
} | ||
} | ||
} |