mirror of
https://github.com/bitwarden/server
synced 2025-12-27 13:43:18 +00:00
308 lines
8.4 KiB
C#
308 lines
8.4 KiB
C#
using Bit.Seeder.Migration.Utils;
|
|
using Xunit;
|
|
|
|
namespace Bit.Seeder.Test.Migration.Utils;
|
|
|
|
public class IdentifierValidatorTests
|
|
{
|
|
[Fact]
|
|
public void IsValid_ValidIdentifier_ReturnsTrue()
|
|
{
|
|
// Arrange
|
|
var validIdentifier = "MyTable123";
|
|
|
|
// Act
|
|
var result = IdentifierValidator.IsValid(validIdentifier);
|
|
|
|
// Assert
|
|
Assert.True(result);
|
|
}
|
|
|
|
[Fact]
|
|
public void IsValid_ValidIdentifierStartingWithUnderscore_ReturnsTrue()
|
|
{
|
|
// Arrange
|
|
var validIdentifier = "_MyTable";
|
|
|
|
// Act
|
|
var result = IdentifierValidator.IsValid(validIdentifier);
|
|
|
|
// Assert
|
|
Assert.True(result);
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData(null)]
|
|
[InlineData("")]
|
|
[InlineData(" ")]
|
|
public void IsValid_NullOrWhiteSpace_ReturnsFalse(string? identifier)
|
|
{
|
|
// Act
|
|
var result = IdentifierValidator.IsValid(identifier);
|
|
|
|
// Assert
|
|
Assert.False(result);
|
|
}
|
|
|
|
[Fact]
|
|
public void IsValid_TooLongIdentifier_ReturnsFalse()
|
|
{
|
|
// Arrange - create identifier longer than 128 characters
|
|
var tooLongIdentifier = new string('a', 129);
|
|
|
|
// Act
|
|
var result = IdentifierValidator.IsValid(tooLongIdentifier);
|
|
|
|
// Assert
|
|
Assert.False(result);
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData("Table-Name")]
|
|
[InlineData("Table Name")]
|
|
[InlineData("Table.Name")]
|
|
[InlineData("Table;Name")]
|
|
[InlineData("Table'Name")]
|
|
[InlineData("Table\"Name")]
|
|
[InlineData("Table*Name")]
|
|
[InlineData("Table/Name")]
|
|
[InlineData("Table\\Name")]
|
|
[InlineData("123Table")]
|
|
public void IsValid_InvalidCharactersOrStartingWithNumber_ReturnsFalse(string identifier)
|
|
{
|
|
// Act
|
|
var result = IdentifierValidator.IsValid(identifier);
|
|
|
|
// Assert
|
|
Assert.False(result);
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData("SELECT")]
|
|
[InlineData("DROP")]
|
|
[InlineData("DELETE")]
|
|
[InlineData("INSERT")]
|
|
[InlineData("UPDATE")]
|
|
[InlineData("EXEC")]
|
|
[InlineData("EXECUTE")]
|
|
public void IsValid_SqlReservedKeyword_WithRestrictiveMode_ReturnsFalse(string keyword)
|
|
{
|
|
// Act
|
|
var result = IdentifierValidator.IsValid(keyword, useRestrictiveMode: true);
|
|
|
|
// Assert
|
|
Assert.False(result);
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData("SELECT")]
|
|
[InlineData("DROP")]
|
|
[InlineData("DELETE")]
|
|
public void IsValid_SqlReservedKeyword_WithoutRestrictiveMode_ReturnsTrue(string keyword)
|
|
{
|
|
// Act - without restrictive mode, these pass the regex but are still SQL keywords
|
|
var result = IdentifierValidator.IsValid(keyword, useRestrictiveMode: false);
|
|
|
|
// Assert
|
|
Assert.True(result);
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData("_LeadingUnderscore")]
|
|
[InlineData("_table")]
|
|
public void IsValid_LeadingUnderscore_WithRestrictiveMode_ReturnsFalse(string identifier)
|
|
{
|
|
// Act
|
|
var result = IdentifierValidator.IsValid(identifier, useRestrictiveMode: true);
|
|
|
|
// Assert
|
|
Assert.False(result);
|
|
}
|
|
|
|
[Fact]
|
|
public void ValidateOrThrow_ValidIdentifier_DoesNotThrow()
|
|
{
|
|
// Arrange
|
|
var validIdentifier = "MyTable";
|
|
|
|
// Act & Assert
|
|
var exception = Record.Exception(() =>
|
|
IdentifierValidator.ValidateOrThrow(validIdentifier, "table name"));
|
|
|
|
Assert.Null(exception);
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData(null)]
|
|
[InlineData("")]
|
|
[InlineData(" ")]
|
|
public void ValidateOrThrow_NullOrWhiteSpace_ThrowsArgumentException(string? identifier)
|
|
{
|
|
// Act & Assert
|
|
var exception = Assert.Throws<ArgumentException>(() =>
|
|
IdentifierValidator.ValidateOrThrow(identifier, "table name"));
|
|
|
|
Assert.Contains("null or empty", exception.Message);
|
|
}
|
|
|
|
[Fact]
|
|
public void ValidateOrThrow_InvalidIdentifier_ThrowsArgumentException()
|
|
{
|
|
// Arrange
|
|
var invalidIdentifier = "Table-Name";
|
|
|
|
// Act & Assert
|
|
var exception = Assert.Throws<ArgumentException>(() =>
|
|
IdentifierValidator.ValidateOrThrow(invalidIdentifier, "table name"));
|
|
|
|
Assert.Contains("Invalid table name", exception.Message);
|
|
}
|
|
|
|
[Fact]
|
|
public void ValidateOrThrow_SqlReservedKeyword_WithRestrictiveMode_ThrowsArgumentException()
|
|
{
|
|
// Arrange
|
|
var keyword = "SELECT";
|
|
|
|
// Act & Assert
|
|
var exception = Assert.Throws<ArgumentException>(() =>
|
|
IdentifierValidator.ValidateOrThrow(keyword, "table name", useRestrictiveMode: true));
|
|
|
|
Assert.Contains("SQL reserved keyword", exception.Message);
|
|
}
|
|
|
|
[Fact]
|
|
public void ValidateAllOrThrow_AllValidIdentifiers_DoesNotThrow()
|
|
{
|
|
// Arrange
|
|
var identifiers = new[] { "Table1", "Table2", "Column_Name" };
|
|
|
|
// Act & Assert
|
|
var exception = Record.Exception(() =>
|
|
IdentifierValidator.ValidateAllOrThrow(identifiers, "table names"));
|
|
|
|
Assert.Null(exception);
|
|
}
|
|
|
|
[Fact]
|
|
public void ValidateAllOrThrow_OneInvalidIdentifier_ThrowsArgumentException()
|
|
{
|
|
// Arrange
|
|
var identifiers = new[] { "Table1", "Invalid-Table", "Table3" };
|
|
|
|
// Act & Assert
|
|
var exception = Assert.Throws<ArgumentException>(() =>
|
|
IdentifierValidator.ValidateAllOrThrow(identifiers, "table names"));
|
|
|
|
Assert.Contains("Invalid table names", exception.Message);
|
|
}
|
|
|
|
[Fact]
|
|
public void FilterValid_MixedValidAndInvalid_ReturnsOnlyValid()
|
|
{
|
|
// Arrange
|
|
var identifiers = new[]
|
|
{
|
|
"ValidTable1",
|
|
"Invalid-Table",
|
|
"ValidTable2",
|
|
"Another Invalid",
|
|
"_ValidUnderscore",
|
|
"123Invalid"
|
|
};
|
|
|
|
// Act
|
|
var validIdentifiers = IdentifierValidator.FilterValid(identifiers);
|
|
|
|
// Assert
|
|
Assert.Equal(3, validIdentifiers.Count);
|
|
Assert.Contains("ValidTable1", validIdentifiers);
|
|
Assert.Contains("ValidTable2", validIdentifiers);
|
|
Assert.Contains("_ValidUnderscore", validIdentifiers);
|
|
}
|
|
|
|
[Fact]
|
|
public void FilterValid_WithRestrictiveMode_FiltersOutLeadingUnderscores()
|
|
{
|
|
// Arrange
|
|
var identifiers = new[]
|
|
{
|
|
"ValidTable",
|
|
"_UnderscoreTable",
|
|
"AnotherValid"
|
|
};
|
|
|
|
// Act
|
|
var validIdentifiers = IdentifierValidator.FilterValid(identifiers, useRestrictiveMode: true);
|
|
|
|
// Assert
|
|
Assert.Equal(2, validIdentifiers.Count);
|
|
Assert.Contains("ValidTable", validIdentifiers);
|
|
Assert.Contains("AnotherValid", validIdentifiers);
|
|
Assert.DoesNotContain("_UnderscoreTable", validIdentifiers);
|
|
}
|
|
|
|
[Fact]
|
|
public void FilterValid_WithRestrictiveMode_FiltersOutSqlKeywords()
|
|
{
|
|
// Arrange
|
|
var identifiers = new[]
|
|
{
|
|
"ValidTable",
|
|
"SELECT",
|
|
"DROP",
|
|
"AnotherValid"
|
|
};
|
|
|
|
// Act
|
|
var validIdentifiers = IdentifierValidator.FilterValid(identifiers, useRestrictiveMode: true);
|
|
|
|
// Assert
|
|
Assert.Equal(2, validIdentifiers.Count);
|
|
Assert.Contains("ValidTable", validIdentifiers);
|
|
Assert.Contains("AnotherValid", validIdentifiers);
|
|
Assert.DoesNotContain("SELECT", validIdentifiers);
|
|
Assert.DoesNotContain("DROP", validIdentifiers);
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData("'; DROP TABLE users--")]
|
|
[InlineData("1' OR '1'='1")]
|
|
[InlineData("admin'--")]
|
|
[InlineData("1; DELETE FROM users")]
|
|
public void IsValid_SqlInjectionAttempts_ReturnsFalse(string injectionAttempt)
|
|
{
|
|
// Act
|
|
var result = IdentifierValidator.IsValid(injectionAttempt);
|
|
|
|
// Assert
|
|
Assert.False(result);
|
|
}
|
|
|
|
[Fact]
|
|
public void IsValid_MaxLengthIdentifier_ReturnsTrue()
|
|
{
|
|
// Arrange - create identifier exactly 128 characters long
|
|
var maxLengthIdentifier = new string('a', 128);
|
|
|
|
// Act
|
|
var result = IdentifierValidator.IsValid(maxLengthIdentifier);
|
|
|
|
// Assert
|
|
Assert.True(result);
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData("select")]
|
|
[InlineData("Select")]
|
|
[InlineData("SeLeCt")]
|
|
public void IsValid_SqlKeywordsCaseInsensitive_WithRestrictiveMode_ReturnsFalse(string keyword)
|
|
{
|
|
// Act
|
|
var result = IdentifierValidator.IsValid(keyword, useRestrictiveMode: true);
|
|
|
|
// Assert - should be case-insensitive
|
|
Assert.False(result);
|
|
}
|
|
}
|