1
0
mirror of https://github.com/bitwarden/server synced 2026-02-26 17:33:40 +00:00

PM-30799 added validation for DomainName (#6856)

This commit is contained in:
Vijay Oommen
2026-01-23 08:34:19 -06:00
committed by GitHub
parent 867e61694b
commit b623e381b4
3 changed files with 150 additions and 0 deletions

View File

@@ -2,11 +2,13 @@
#nullable disable
using System.ComponentModel.DataAnnotations;
using Bit.Core.Utilities;
namespace Bit.Api.AdminConsole.Models.Request;
public class OrganizationDomainRequestModel
{
[Required]
[DomainNameValidator]
public string DomainName { get; set; }
}

View File

@@ -0,0 +1,64 @@
using System.ComponentModel.DataAnnotations;
using System.Text.RegularExpressions;
namespace Bit.Core.Utilities;
/// <summary>
/// https://bitwarden.atlassian.net/browse/VULN-376
/// Domain names are vulnerable to XSS attacks if not properly validated.
/// Domain names can contain letters, numbers, dots, and hyphens.
/// Domain names maybe internationalized (IDN) and contain unicode characters.
/// </summary>
public class DomainNameValidatorAttribute : ValidationAttribute
{
// RFC 1123 compliant domain name regex
// - Allows alphanumeric characters and hyphens
// - Cannot start or end with a hyphen
// - Each label (part between dots) must be 1-63 characters
// - Total length should not exceed 253 characters
// - Supports internationalized domain names (IDN) - which is why this regex includes unicode ranges
private static readonly Regex _domainNameRegex = new(
@"^(?:[a-zA-Z0-9\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF](?:[a-zA-Z0-9\-\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]{0,61}[a-zA-Z0-9\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])?\.)*[a-zA-Z0-9\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF](?:[a-zA-Z0-9\-\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]{0,61}[a-zA-Z0-9\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])?$",
RegexOptions.Compiled | RegexOptions.IgnoreCase
);
public DomainNameValidatorAttribute()
: base("The {0} field is not a valid domain name.")
{ }
public override bool IsValid(object? value)
{
if (value == null)
{
return true; // Use [Required] for null checks
}
var domainName = value.ToString();
if (string.IsNullOrWhiteSpace(domainName))
{
return false;
}
// Reject if contains any whitespace (including leading/trailing spaces, tabs, newlines)
if (domainName.Any(char.IsWhiteSpace))
{
return false;
}
// Check length constraints
if (domainName.Length > 253)
{
return false;
}
// Check for control characters or other dangerous characters
if (domainName.Any(c => char.IsControl(c) || c == '<' || c == '>' || c == '"' || c == '\'' || c == '&'))
{
return false;
}
// Validate against domain name regex
return _domainNameRegex.IsMatch(domainName);
}
}