File: Secrets\KubernetesName.cs
Web Access
Project: src\src\Aspire.Hosting.Radius\Aspire.Hosting.Radius.csproj (Aspire.Hosting.Radius)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
namespace Aspire.Hosting.Radius.Secrets;
 
/// <summary>
/// Validates Kubernetes object names against the DNS-1123 rules Kubernetes enforces, so a malformed
/// name (e.g. from an existing-secret reference or a <c>SealedSecret</c> manifest) fails fast at the
/// API/publish boundary rather than only when <c>kubectl apply</c> / Radius rejects it at deploy.
/// See https://kubernetes.io/docs/concepts/overview/working-with-objects/names/.
/// </summary>
internal static class KubernetesName
{
    // DNS-1123 label: 1-63 chars, lowercase alphanumeric or '-', must start and end alphanumeric.
    public static bool IsDns1123Label(string value)
    {
        if (value.Length is 0 or > 63)
        {
            return false;
        }
 
        for (var i = 0; i < value.Length; i++)
        {
            var c = value[i];
            var isEdge = i == 0 || i == value.Length - 1;
            if (!(c is (>= 'a' and <= 'z') or (>= '0' and <= '9') || (!isEdge && c == '-')))
            {
                return false;
            }
        }
 
        return true;
    }
 
    // DNS-1123 subdomain: 1-253 chars, a dot-separated series of DNS-1123 labels.
    public static bool IsDns1123Subdomain(string value)
    {
        if (value.Length is 0 or > 253)
        {
            return false;
        }
 
        foreach (var label in value.Split('.'))
        {
            if (!IsDns1123Label(label))
            {
                return false;
            }
        }
 
        return true;
    }
 
    // A Kubernetes Secret data key must consist of alphanumeric characters, '-', '_', or '.'
    // A Kubernetes Secret/ConfigMap data key must be non-empty, at most 253 characters, match the
    // grammar `[-._a-zA-Z0-9]+`, and must not be "." or ".." or start with "..". This mirrors
    // apimachinery's IsConfigMapKey so a key that would be rejected by the Kubernetes API is caught
    // at publish time instead. See:
    // - https://kubernetes.io/docs/concepts/configuration/secret/
    // - https://github.com/kubernetes/apimachinery/blob/master/pkg/util/validation/validation.go (IsConfigMapKey)
    public static bool IsValidSecretDataKey(string value)
    {
        if (value.Length is 0 or > 253)
        {
            return false;
        }
 
        if (value is "." or ".." || value.StartsWith("..", StringComparison.Ordinal))
        {
            return false;
        }
 
        foreach (var c in value)
        {
            if (c is not ((>= 'a' and <= 'z') or (>= 'A' and <= 'Z') or (>= '0' and <= '9') or '-' or '_' or '.'))
            {
                return false;
            }
        }
 
        return true;
    }
}