< Summary - Envilder .NET SDK

Information
Class: Envilder.Domain.EnvironmentVariableName
Assembly: Envilder
File(s): /home/runner/work/envilder/envilder/src/sdks/dotnet/Domain/EnvironmentVariableName.cs
Tag: 561_41292037308
Line coverage
100%
Covered lines: 18
Uncovered lines: 0
Coverable lines: 18
Total lines: 78
Line coverage: 100%
Branch coverage
96%
Covered branches: 29
Total branches: 30
Branch coverage: 96.6%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
IsValid(...)100%88100%
InvalidMessage(...)100%44100%
IsReadableNameChar(...)94.44%1818100%
Quote(...)100%11100%

File(s)

/home/runner/work/envilder/envilder/src/sdks/dotnet/Domain/EnvironmentVariableName.cs

#LineLine coverage
 1namespace Envilder.Domain;
 2
 3using System.Text.Json;
 4
 5/// <summary>
 6/// Which strings are usable as environment variable names, per the map-file
 7/// naming constraint in ADR-0008: an accepted name must survive a <c>.env</c>
 8/// round-trip as itself and nothing else. Anything outside
 9/// <c>^[A-Za-z0-9_./-]+$</c> is either read back as a different assignment
 10/// (<c>=</c>, CR, LF, U+2028, U+2029) or silently lost (a space, <c>#</c>, a
 11/// tab, a non-Latin letter). <c>/</c> stays valid: it is the section separator
 12/// map files have always used (<c>Database/Password</c>).
 13/// <c>__proto__</c> is inside the allowlist but every JavaScript reader drops
 14/// it, so it is rejected as well to keep the contract uniform across the CLI
 15/// and every SDK.
 16/// </summary>
 17internal static class EnvironmentVariableName
 18{
 19  private const string UnsupportedName = "__proto__";
 20
 21  public static bool IsValid(string name)
 22  {
 123    if (string.IsNullOrEmpty(name) || name == UnsupportedName)
 24    {
 125      return false;
 26    }
 27
 28    // A character check rather than a Regex: .NET's `$` also matches
 29    // before a trailing newline, which would accept "SAFE\n".
 130    foreach (var c in name)
 31    {
 132      if (!IsReadableNameChar(c))
 33      {
 134        return false;
 35      }
 36    }
 37
 138    return true;
 39  }
 40
 41  /// <summary>
 42  /// Builds the rejection message. The name is untrusted input, so it is
 43  /// quoted rather than interpolated: a name carrying line terminators cannot
 44  /// break the message across lines. Only the name is echoed, never the
 45  /// mapped value.
 46  /// </summary>
 47  public static string InvalidMessage(string name)
 48  {
 149    if (string.IsNullOrWhiteSpace(name))
 50    {
 151      return "Environment variable name cannot be empty";
 52    }
 53
 154    if (name == UnsupportedName)
 55    {
 156      return $"Unsupported environment variable name {Quote(name)}: "
 157        + "a .env parser cannot read this name back, so it would be written "
 158        + "but never resolved";
 59    }
 60
 161    return $"Invalid environment variable name {Quote(name)}: names may only "
 162      + "contain letters, digits, underscore, dot, hyphen and slash, so that "
 163      + "they can be read back from a .env file";
 64  }
 65
 66  private static bool IsReadableNameChar(char c)
 67  {
 168    return c is (>= 'A' and <= 'Z') or (>= 'a' and <= 'z') or (>= '0' and <= '9')
 169      or '_' or '.' or '-' or '/';
 70  }
 71
 72  // The default encoder escapes control characters and every non-ASCII
 73  // character, U+2028/U+2029 included.
 74  private static string Quote(string name)
 75  {
 176    return JsonSerializer.Serialize(name);
 77  }
 78}