Files
git.stella-ops.org/src/Scanner/__Libraries/StellaOps.Scanner.Reachability/CodeIdBuilder.cs
master cc69d332e3
Some checks failed
Docs CI / lint-and-preview (push) Has been cancelled
Add unit tests for RabbitMq and Udp transport servers and clients
- Implemented comprehensive unit tests for RabbitMqTransportServer, covering constructor, disposal, connection management, event handlers, and exception handling.
- Added configuration tests for RabbitMqTransportServer to validate SSL, durable queues, auto-recovery, and custom virtual host options.
- Created unit tests for UdpFrameProtocol, including frame parsing and serialization, header size validation, and round-trip data preservation.
- Developed tests for UdpTransportClient, focusing on connection handling, event subscriptions, and exception scenarios.
- Established tests for UdpTransportServer, ensuring proper start/stop behavior, connection state management, and event handling.
- Included tests for UdpTransportOptions to verify default values and modification capabilities.
- Enhanced service registration tests for Udp transport services in the dependency injection container.
2025-12-05 19:01:12 +02:00

132 lines
4.1 KiB
C#

using System;
using System.Text;
using StellaOps.Cryptography;
namespace StellaOps.Scanner.Reachability;
/// <summary>
/// Builds canonical CodeIDs with compliance-profile-aware hashing.
/// Uses <see cref="HashPurpose.Symbol"/> which resolves to:
/// - SHA-256 for "world" and "fips" profiles
/// - GOST3411-2012-256 for "gost" profile
/// - SM3 for "sm" profile
/// </summary>
/// <remarks>
/// Format: <c>code:{lang}:{base64url-hash}</c> where the hash is computed over a
/// canonical tuple that is stable across machines and paths.
/// </remarks>
public sealed class CodeIdBuilder
{
private readonly ICryptoHash _cryptoHash;
/// <summary>
/// Creates a new CodeIdBuilder with the specified crypto hash service.
/// </summary>
/// <param name="cryptoHash">Crypto hash service for compliance-aware hashing.</param>
public CodeIdBuilder(ICryptoHash cryptoHash)
{
_cryptoHash = cryptoHash ?? throw new ArgumentNullException(nameof(cryptoHash));
}
/// <summary>
/// Creates a binary code-id from binary components.
/// </summary>
public string ForBinary(string buildId, string section, string? relativePath)
{
var tuple = $"{Norm(buildId)}\0{Norm(section)}\0{Norm(relativePath)}";
return Build("binary", tuple);
}
/// <summary>
/// Creates a .NET code-id from assembly components.
/// </summary>
public string ForDotNet(string assemblyName, string moduleName, string? mvid)
{
var tuple = $"{Norm(assemblyName)}\0{Norm(moduleName)}\0{Norm(mvid)}";
return Build("dotnet", tuple);
}
/// <summary>
/// Creates a binary code-id using canonical address + length tuple.
/// </summary>
public string ForBinarySegment(string format, string fileHash, string address, long? lengthBytes = null, string? section = null, string? codeBlockHash = null)
{
var tuple = $"{Norm(format)}\0{Norm(fileHash)}\0{NormalizeAddress(address)}\0{NormalizeLength(lengthBytes)}\0{Norm(section)}\0{Norm(codeBlockHash)}";
return Build("binary", tuple);
}
/// <summary>
/// Creates a Node code-id from package components.
/// </summary>
public string ForNode(string packageName, string entryPath)
{
var tuple = $"{Norm(packageName)}\0{Norm(entryPath)}";
return Build("node", tuple);
}
/// <summary>
/// Creates a code-id from an existing symbol ID.
/// </summary>
public string FromSymbolId(string symbolId)
{
ArgumentException.ThrowIfNullOrWhiteSpace(symbolId);
return Build("sym", symbolId.Trim());
}
private string Build(string lang, string tuple)
{
var bytes = Encoding.UTF8.GetBytes(tuple);
var hash = _cryptoHash.ComputeHashForPurpose(bytes, HashPurpose.Symbol);
var base64 = Convert.ToBase64String(hash)
.TrimEnd('=')
.Replace('+', '-')
.Replace('/', '_');
return $"code:{lang}:{base64}";
}
private static string NormalizeAddress(string? value)
{
if (string.IsNullOrWhiteSpace(value))
{
return "0x0";
}
var addrText = value.Trim();
var isHex = addrText.StartsWith("0x", StringComparison.OrdinalIgnoreCase);
if (isHex)
{
addrText = addrText[2..];
}
if (long.TryParse(addrText, isHex ? System.Globalization.NumberStyles.HexNumber : System.Globalization.NumberStyles.Integer, System.Globalization.CultureInfo.InvariantCulture, out var addrValue))
{
if (addrValue < 0)
{
addrValue = 0;
}
return $"0x{addrValue:x}";
}
addrText = addrText.TrimStart('0');
if (addrText.Length == 0)
{
addrText = "0";
}
return $"0x{addrText.ToLowerInvariant()}";
}
private static string NormalizeLength(long? value)
{
if (value is null or <= 0)
{
return "unknown";
}
return value.Value.ToString("D", System.Globalization.CultureInfo.InvariantCulture);
}
private static string Norm(string? value) => (value ?? string.Empty).Trim();
}