Some checks failed
Docs CI / lint-and-preview (push) Has been cancelled
- 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.
132 lines
4.1 KiB
C#
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();
|
|
}
|