< Summary

Information
Class: AsiBackbone.Core.Metadata.GovernanceMetadataBudget
Assembly: AsiBackbone.Core
File(s): /home/runner/work/AsiBackbone/AsiBackbone/src/AsiBackbone.Core/Metadata/GovernanceMetadataBudget.cs
Line coverage
100%
Covered lines: 66
Uncovered lines: 0
Coverable lines: 66
Total lines: 160
Line coverage: 100%
Branch coverage
100%
Covered branches: 10
Total branches: 10
Branch coverage: 100%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.cctor()100%11100%
.ctor(...)100%11100%
get_Recommended()100%11100%
get_MaxCount()100%11100%
get_MaxKeyLength()100%11100%
get_MaxValueLength()100%11100%
get_MaxSerializedBytes()100%11100%
get_ReservedKeyFragments()100%11100%
Create(...)100%22100%
NormalizeKeyForComparison(...)100%44100%
NormalizeReservedKeyFragments(...)100%22100%
ThrowIfLessThanOne(...)100%22100%

File(s)

/home/runner/work/AsiBackbone/AsiBackbone/src/AsiBackbone.Core/Metadata/GovernanceMetadataBudget.cs

#LineLine coverage
 1using System.Collections.ObjectModel;
 2
 3namespace AsiBackbone.Core.Metadata;
 4
 5/// <summary>
 6/// Defines optional host-owned budget limits for governance metadata dictionaries.
 7/// </summary>
 8/// <remarks>
 9/// The budget is intentionally advisory and helper-oriented. AsiBackbone cannot determine whether
 10/// host-provided metadata is sensitive in a specific deployment, but hosts can use these limits to
 11/// keep audit and governance metadata bounded before durable storage, emission, or signing.
 12/// </remarks>
 13public sealed class GovernanceMetadataBudget
 14{
 15    /// <summary>
 16    /// Gets the default recommended maximum number of metadata entries.
 17    /// </summary>
 18    public const int DefaultMaxCount = 32;
 19
 20    /// <summary>
 21    /// Gets the default recommended maximum metadata key length, in characters.
 22    /// </summary>
 23    public const int DefaultMaxKeyLength = 64;
 24
 25    /// <summary>
 26    /// Gets the default recommended maximum metadata value length, in characters.
 27    /// </summary>
 28    public const int DefaultMaxValueLength = 512;
 29
 30    /// <summary>
 31    /// Gets the default recommended estimated serialized metadata size, in UTF-8 bytes.
 32    /// </summary>
 33    public const int DefaultMaxSerializedBytes = 8192;
 34
 335    private static readonly ReadOnlyCollection<string> DefaultReservedKeyFragments =
 336        new(
 337            new[]
 338            {
 339                "accesstoken",
 340                "apikey",
 341                "authorization",
 342                "bearer",
 343                "connectionstring",
 344                "credential",
 345                "password",
 346                "privatekey",
 347                "refreshtoken",
 348                "secret",
 349                "socialsecurity",
 350                "ssn"
 351            });
 52
 1553    private GovernanceMetadataBudget(
 1554        int maxCount,
 1555        int maxKeyLength,
 1556        int maxValueLength,
 1557        int maxSerializedBytes,
 1558        IReadOnlyList<string> reservedKeyFragments)
 59    {
 1560        ThrowIfLessThanOne(maxCount, nameof(maxCount));
 1361        ThrowIfLessThanOne(maxKeyLength, nameof(maxKeyLength));
 1362        ThrowIfLessThanOne(maxValueLength, nameof(maxValueLength));
 1363        ThrowIfLessThanOne(maxSerializedBytes, nameof(maxSerializedBytes));
 1364        ArgumentNullException.ThrowIfNull(reservedKeyFragments);
 65
 1366        MaxCount = maxCount;
 1367        MaxKeyLength = maxKeyLength;
 1368        MaxValueLength = maxValueLength;
 1369        MaxSerializedBytes = maxSerializedBytes;
 1370        ReservedKeyFragments = reservedKeyFragments;
 1371    }
 72
 73    /// <summary>
 74    /// Gets the recommended governance metadata budget.
 75    /// </summary>
 2476    public static GovernanceMetadataBudget Recommended { get; } = Create();
 77
 78    /// <summary>
 79    /// Gets the maximum number of normalized metadata entries allowed by the budget.
 80    /// </summary>
 3381    public int MaxCount { get; }
 82
 83    /// <summary>
 84    /// Gets the maximum normalized metadata key length allowed by the budget, in characters.
 85    /// </summary>
 2686    public int MaxKeyLength { get; }
 87
 88    /// <summary>
 89    /// Gets the maximum normalized metadata value length allowed by the budget, in characters.
 90    /// </summary>
 3091    public int MaxValueLength { get; }
 92
 93    /// <summary>
 94    /// Gets the maximum estimated serialized metadata size allowed by the budget, in UTF-8 bytes.
 95    /// </summary>
 3196    public int MaxSerializedBytes { get; }
 97
 98    /// <summary>
 99    /// Gets normalized reserved or discouraged metadata key fragments.
 100    /// </summary>
 36101    public IReadOnlyList<string> ReservedKeyFragments { get; }
 102
 103    /// <summary>
 104    /// Creates a governance metadata budget.
 105    /// </summary>
 106    public static GovernanceMetadataBudget Create(
 107        int maxCount = DefaultMaxCount,
 108        int maxKeyLength = DefaultMaxKeyLength,
 109        int maxValueLength = DefaultMaxValueLength,
 110        int maxSerializedBytes = DefaultMaxSerializedBytes,
 111        IEnumerable<string>? reservedKeyFragments = null)
 112    {
 15113        return new GovernanceMetadataBudget(
 15114            maxCount,
 15115            maxKeyLength,
 15116            maxValueLength,
 15117            maxSerializedBytes,
 15118            NormalizeReservedKeyFragments(reservedKeyFragments ?? DefaultReservedKeyFragments));
 119    }
 120
 121    internal static string NormalizeKeyForComparison(string key)
 122    {
 76123        string normalizedKey = key.Trim().ToLowerInvariant();
 76124        char[] buffer = new char[normalizedKey.Length];
 76125        int count = 0;
 126
 1530127        foreach (char character in normalizedKey)
 128        {
 689129            if (char.IsLetterOrDigit(character))
 130            {
 679131                buffer[count] = character;
 679132                count++;
 133            }
 134        }
 135
 76136        return new string(buffer, 0, count);
 137    }
 138
 139    private static ReadOnlyCollection<string> NormalizeReservedKeyFragments(IEnumerable<string> reservedKeyFragments)
 140    {
 15141        string[] normalizedFragments = [.. reservedKeyFragments
 68142            .Where(fragment => !string.IsNullOrWhiteSpace(fragment))
 15143            .Select(NormalizeKeyForComparison)
 66144            .Where(fragment => fragment.Length > 0)
 15145            .Distinct(StringComparer.Ordinal)
 79146            .OrderBy(fragment => fragment, StringComparer.Ordinal)];
 147
 15148        return normalizedFragments.Length == 0
 15149            ? Array.AsReadOnly(Array.Empty<string>())
 15150            : Array.AsReadOnly(normalizedFragments);
 151    }
 152
 153    private static void ThrowIfLessThanOne(int value, string parameterName)
 154    {
 54155        if (value < 1)
 156        {
 2157            throw new ArgumentOutOfRangeException(parameterName, value, "Value must be greater than or equal to one.");
 158        }
 52159    }
 160}