feat: Implement DefaultCryptoHmac for compliance-aware HMAC operations

- Added DefaultCryptoHmac class implementing ICryptoHmac interface.
- Introduced purpose-based HMAC computation methods.
- Implemented verification methods for HMACs with constant-time comparison.
- Created HmacAlgorithms and HmacPurpose classes for well-known identifiers.
- Added compliance profile support for HMAC algorithms.
- Included asynchronous methods for HMAC computation from streams.
This commit is contained in:
StellaOps Bot
2025-12-06 00:41:04 +02:00
parent 43c281a8b2
commit f0662dd45f
362 changed files with 8441 additions and 22338 deletions

View File

@@ -0,0 +1,308 @@
using System.Collections.Immutable;
using System.Text.Json;
using Microsoft.Extensions.Options;
using StellaOps.Aoc;
using StellaOps.Concelier.Core.Aoc;
using StellaOps.Concelier.RawModels;
namespace StellaOps.Concelier.Core.Tests.Aoc;
/// <summary>
/// Tests for <see cref="AdvisorySchemaValidator"/> per WEB-AOC-19-002.
/// Covers ERR_AOC_001 (forbidden), ERR_AOC_002 (merge), ERR_AOC_006 (derived), ERR_AOC_007 (unknown).
/// </summary>
public sealed class AdvisorySchemaValidatorTests
{
private static readonly AocGuardOptions GuardOptions = AocGuardOptions.Default;
private static AdvisoryRawDocument CreateValidDocument(string tenant = "tenant-a")
{
using var rawDocument = JsonDocument.Parse("""{"id":"demo"}""");
return new AdvisoryRawDocument(
Tenant: tenant,
Source: new RawSourceMetadata("vendor-x", "connector-y", "1.0.0"),
Upstream: new RawUpstreamMetadata(
UpstreamId: "GHSA-xxxx",
DocumentVersion: "1",
RetrievedAt: DateTimeOffset.UtcNow,
ContentHash: "sha256:abc",
Signature: new RawSignatureMetadata(false),
Provenance: ImmutableDictionary<string, string>.Empty),
Content: new RawContent(
Format: "OSV",
SpecVersion: "1.0",
Raw: rawDocument.RootElement.Clone()),
Identifiers: new RawIdentifiers(
Aliases: ImmutableArray.Create("GHSA-xxxx"),
PrimaryId: "GHSA-xxxx"),
Linkset: new RawLinkset
{
Aliases = ImmutableArray<string>.Empty,
PackageUrls = ImmutableArray<string>.Empty,
Cpes = ImmutableArray<string>.Empty,
References = ImmutableArray<RawReference>.Empty,
ReconciledFrom = ImmutableArray<string>.Empty,
Notes = ImmutableDictionary<string, string>.Empty
},
Links: ImmutableArray<RawLink>.Empty);
}
private static AdvisorySchemaValidator CreateValidator()
=> new(new AocWriteGuard(), Options.Create(GuardOptions));
[Fact]
public void ValidateSchema_AllowsValidDocument()
{
var validator = CreateValidator();
var document = CreateValidDocument();
var result = validator.ValidateSchema(document);
Assert.True(result.IsValid);
Assert.Empty(result.Violations);
}
[Fact]
public void ValidateForbiddenFields_ReturnsSuccessForValidDocument()
{
var validator = CreateValidator();
var document = CreateValidDocument();
var result = validator.ValidateForbiddenFields(document);
Assert.True(result.IsValid);
}
[Fact]
public void ValidateDerivedFields_ReturnsSuccessForValidDocument()
{
var validator = CreateValidator();
var document = CreateValidDocument();
var result = validator.ValidateDerivedFields(document);
Assert.True(result.IsValid);
}
[Fact]
public void ValidateAllowedFields_ReturnsSuccessForValidDocument()
{
var validator = CreateValidator();
var document = CreateValidDocument();
var result = validator.ValidateAllowedFields(document);
Assert.True(result.IsValid);
}
[Fact]
public void ValidateMergeAttempt_ReturnsSuccessForValidDocument()
{
var validator = CreateValidator();
var document = CreateValidDocument();
var result = validator.ValidateMergeAttempt(document);
Assert.True(result.IsValid);
}
// Direct IAocGuard tests for ERR_AOC_001, ERR_AOC_002, ERR_AOC_006, ERR_AOC_007
// These test the underlying guard behavior with arbitrary JSON
[Fact]
public void AocGuard_DetectsForbiddenField_ERR_AOC_001()
{
var guard = new AocWriteGuard();
using var jsonDoc = JsonDocument.Parse("""
{
"tenant": "test",
"severity": "high",
"source": {"vendor": "test", "connector": "test", "version": "1.0"},
"upstream": {
"upstream_id": "CVE-2024-0001",
"content_hash": "sha256:abc",
"retrieved_at": "2024-01-01T00:00:00Z",
"signature": {"present": false},
"provenance": {}
},
"content": {"format": "OSV", "raw": {}},
"identifiers": {"aliases": [], "primary": "CVE-2024-0001"},
"linkset": {}
}
""");
var result = guard.Validate(jsonDoc.RootElement, GuardOptions);
Assert.False(result.IsValid);
Assert.Contains(result.Violations, v =>
v.Code == AocViolationCode.ForbiddenField &&
v.ErrorCode == "ERR_AOC_001" &&
v.Path == "/severity");
}
[Fact]
public void AocGuard_DetectsMergedFromField_ERR_AOC_001()
{
var guard = new AocWriteGuard();
using var jsonDoc = JsonDocument.Parse("""
{
"tenant": "test",
"merged_from": ["obs-1", "obs-2"],
"source": {"vendor": "test", "connector": "test", "version": "1.0"},
"upstream": {
"upstream_id": "CVE-2024-0001",
"content_hash": "sha256:abc",
"retrieved_at": "2024-01-01T00:00:00Z",
"signature": {"present": false},
"provenance": {}
},
"content": {"format": "OSV", "raw": {}},
"identifiers": {"aliases": [], "primary": "CVE-2024-0001"},
"linkset": {}
}
""");
var result = guard.Validate(jsonDoc.RootElement, GuardOptions);
Assert.False(result.IsValid);
Assert.Contains(result.Violations, v =>
v.Code == AocViolationCode.ForbiddenField &&
v.ErrorCode == "ERR_AOC_001" &&
v.Path == "/merged_from");
}
[Fact]
public void AocGuard_DetectsDerivedField_ERR_AOC_006()
{
var guard = new AocWriteGuard();
using var jsonDoc = JsonDocument.Parse("""
{
"tenant": "test",
"effective_status": "affected",
"source": {"vendor": "test", "connector": "test", "version": "1.0"},
"upstream": {
"upstream_id": "CVE-2024-0001",
"content_hash": "sha256:abc",
"retrieved_at": "2024-01-01T00:00:00Z",
"signature": {"present": false},
"provenance": {}
},
"content": {"format": "OSV", "raw": {}},
"identifiers": {"aliases": [], "primary": "CVE-2024-0001"},
"linkset": {}
}
""");
var result = guard.Validate(jsonDoc.RootElement, GuardOptions);
Assert.False(result.IsValid);
Assert.Contains(result.Violations, v =>
v.Code == AocViolationCode.DerivedFindingDetected &&
v.ErrorCode == "ERR_AOC_006" &&
v.Path == "/effective_status");
}
[Fact]
public void AocGuard_DetectsUnknownField_ERR_AOC_007()
{
var guard = new AocWriteGuard();
using var jsonDoc = JsonDocument.Parse("""
{
"tenant": "test",
"unknown_custom_field": "value",
"source": {"vendor": "test", "connector": "test", "version": "1.0"},
"upstream": {
"upstream_id": "CVE-2024-0001",
"content_hash": "sha256:abc",
"retrieved_at": "2024-01-01T00:00:00Z",
"signature": {"present": false},
"provenance": {}
},
"content": {"format": "OSV", "raw": {}},
"identifiers": {"aliases": [], "primary": "CVE-2024-0001"},
"linkset": {}
}
""");
var result = guard.Validate(jsonDoc.RootElement, GuardOptions);
Assert.False(result.IsValid);
Assert.Contains(result.Violations, v =>
v.Code == AocViolationCode.UnknownField &&
v.ErrorCode == "ERR_AOC_007" &&
v.Path == "/unknown_custom_field");
}
[Theory]
[InlineData("cvss")]
[InlineData("cvss_vector")]
[InlineData("consensus_provider")]
[InlineData("reachability")]
[InlineData("asset_criticality")]
[InlineData("risk_score")]
public void AocGuard_DetectsAllForbiddenFields(string forbiddenField)
{
var guard = new AocWriteGuard();
var json = $$"""
{
"tenant": "test",
"{{forbiddenField}}": "forbidden_value",
"source": {"vendor": "test", "connector": "test", "version": "1.0"},
"upstream": {
"upstream_id": "CVE-2024-0001",
"content_hash": "sha256:abc",
"retrieved_at": "2024-01-01T00:00:00Z",
"signature": {"present": false},
"provenance": {}
},
"content": {"format": "OSV", "raw": {}},
"identifiers": {"aliases": [], "primary": "CVE-2024-0001"},
"linkset": {}
}
""";
using var jsonDoc = JsonDocument.Parse(json);
var result = guard.Validate(jsonDoc.RootElement, GuardOptions);
Assert.False(result.IsValid);
Assert.Contains(result.Violations, v =>
v.Code == AocViolationCode.ForbiddenField &&
v.ErrorCode == "ERR_AOC_001");
}
[Theory]
[InlineData("effective_range")]
[InlineData("effective_severity")]
[InlineData("effective_cvss")]
public void AocGuard_DetectsAllDerivedFields(string derivedField)
{
var guard = new AocWriteGuard();
var json = $$"""
{
"tenant": "test",
"{{derivedField}}": "derived_value",
"source": {"vendor": "test", "connector": "test", "version": "1.0"},
"upstream": {
"upstream_id": "CVE-2024-0001",
"content_hash": "sha256:abc",
"retrieved_at": "2024-01-01T00:00:00Z",
"signature": {"present": false},
"provenance": {}
},
"content": {"format": "OSV", "raw": {}},
"identifiers": {"aliases": [], "primary": "CVE-2024-0001"},
"linkset": {}
}
""";
using var jsonDoc = JsonDocument.Parse(json);
var result = guard.Validate(jsonDoc.RootElement, GuardOptions);
Assert.False(result.IsValid);
// Derived fields (effective_*) trigger both ForbiddenField and DerivedFindingDetected
// if they're in the forbidden list, otherwise just DerivedFindingDetected
Assert.Contains(result.Violations, v =>
v.Code == AocViolationCode.DerivedFindingDetected &&
v.ErrorCode == "ERR_AOC_006");
}
}

View File

@@ -6,6 +6,8 @@
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<IsPackable>false</IsPackable>
<!-- Disable Concelier Testing infra which requires Storage.Mongo -->
<UseConcelierTestInfra>false</UseConcelierTestInfra>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="../../__Libraries/StellaOps.Concelier.Core/StellaOps.Concelier.Core.csproj" />
@@ -15,6 +17,6 @@
<ProjectReference Include="../../../Aoc/__Libraries/StellaOps.Aoc/StellaOps.Aoc.csproj" />
<!-- Test packages inherited from Directory.Build.props -->
<PackageReference Include="FluentAssertions" Version="6.12.0" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="10.0.0-rc.2.25502.107" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="10.0.0" />
</ItemGroup>
</Project>