license switch agpl -> busl1, sprints work, new product advisories

This commit is contained in:
master
2026-01-20 15:32:20 +02:00
parent 4903395618
commit c32fff8f86
1835 changed files with 38630 additions and 4359 deletions

View File

@@ -1,5 +1,5 @@
// Copyright (c) StellaOps. All rights reserved.
// Licensed under the AGPL-3.0-or-later license.
// Licensed under the BUSL-1.1 license.
using System;
using System.Collections.Generic;

View File

@@ -0,0 +1,169 @@
// -----------------------------------------------------------------------------
// AttestTimestampCommandTests.cs
// Sprint: SPRINT_20260119_010 Attestor TST Integration
// Description: Tests for attestation timestamp CLI handling.
// -----------------------------------------------------------------------------
using System.Text;
using System.Text.Json;
using Microsoft.Extensions.DependencyInjection;
using StellaOps.Cli.Commands;
using StellaOps.Cli.Tests.Testing;
using StellaOps.TestKit;
using Xunit;
namespace StellaOps.Cli.Tests.Commands;
public sealed class AttestTimestampCommandTests
{
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task HandleAttestSignAsync_WithTimestamp_WritesTimestampMetadata()
{
using var temp = new TempDirectory();
var predicatePath = Path.Combine(temp.Path, "predicate.json");
var outputPath = Path.Combine(temp.Path, "attestation.json");
await File.WriteAllTextAsync(predicatePath, "{}", CancellationToken.None);
var services = new ServiceCollection().BuildServiceProvider();
var exitCode = await CommandHandlers.HandleAttestSignAsync(
services,
predicatePath,
"https://example.test/predicate",
"artifact",
"sha256:abc123",
keyId: null,
keyless: false,
useRekor: false,
includeTimestamp: true,
tsaUrl: "https://tsa.example",
outputPath: outputPath,
format: "dsse",
verbose: false,
cancellationToken: CancellationToken.None);
Assert.Equal(0, exitCode);
using var doc = JsonDocument.Parse(await File.ReadAllTextAsync(outputPath, CancellationToken.None));
Assert.True(doc.RootElement.TryGetProperty("timestamp", out var timestamp));
Assert.True(timestamp.TryGetProperty("rfc3161", out _));
}
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task HandleAttestVerifyAsync_RequireTimestamp_FailsWhenMissing()
{
using var temp = new TempDirectory();
var envelopePath = Path.Combine(temp.Path, "envelope.json");
var outputPath = Path.Combine(temp.Path, "verification.json");
var rootPath = Path.Combine(temp.Path, "root.pem");
await File.WriteAllTextAsync(envelopePath, CreateEnvelopeJson(includeTimestamp: false), CancellationToken.None);
await File.WriteAllTextAsync(rootPath, "root", CancellationToken.None);
var services = new ServiceCollection().BuildServiceProvider();
var exitCode = await CommandHandlers.HandleAttestVerifyAsync(
services,
envelopePath,
policyPath: null,
rootPath: rootPath,
checkpointPath: null,
outputPath: outputPath,
format: "json",
explain: false,
requireTimestamp: true,
maxSkew: null,
verbose: false,
cancellationToken: CancellationToken.None);
Assert.Equal(2, exitCode);
using var doc = JsonDocument.Parse(await File.ReadAllTextAsync(outputPath, CancellationToken.None));
Assert.Equal("FAILED", doc.RootElement.GetProperty("status").GetString());
Assert.True(doc.RootElement.GetProperty("timestamp").GetProperty("required").GetBoolean());
Assert.False(doc.RootElement.GetProperty("timestamp").GetProperty("present").GetBoolean());
}
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task HandleAttestVerifyAsync_RequireTimestamp_PassesWhenPresent()
{
using var temp = new TempDirectory();
var envelopePath = Path.Combine(temp.Path, "envelope.json");
var outputPath = Path.Combine(temp.Path, "verification.json");
var rootPath = Path.Combine(temp.Path, "root.pem");
await File.WriteAllTextAsync(envelopePath, CreateEnvelopeJson(includeTimestamp: true), CancellationToken.None);
await File.WriteAllTextAsync(rootPath, "root", CancellationToken.None);
var services = new ServiceCollection().BuildServiceProvider();
var exitCode = await CommandHandlers.HandleAttestVerifyAsync(
services,
envelopePath,
policyPath: null,
rootPath: rootPath,
checkpointPath: null,
outputPath: outputPath,
format: "json",
explain: false,
requireTimestamp: true,
maxSkew: null,
verbose: false,
cancellationToken: CancellationToken.None);
Assert.Equal(0, exitCode);
using var doc = JsonDocument.Parse(await File.ReadAllTextAsync(outputPath, CancellationToken.None));
Assert.Equal("PASSED", doc.RootElement.GetProperty("status").GetString());
Assert.True(doc.RootElement.GetProperty("timestamp").GetProperty("present").GetBoolean());
}
private static string CreateEnvelopeJson(bool includeTimestamp)
{
var statement = new
{
_type = "https://in-toto.io/Statement/v1",
subject = new[]
{
new
{
name = "artifact",
digest = new Dictionary<string, string> { ["sha256"] = "abc123" }
}
},
predicateType = "https://example.test/predicate",
predicate = new { }
};
var payloadJson = JsonSerializer.Serialize(statement);
var payloadBase64 = Convert.ToBase64String(Encoding.UTF8.GetBytes(payloadJson));
var envelope = new Dictionary<string, object?>
{
["payloadType"] = "application/vnd.in-toto+json",
["payload"] = payloadBase64,
["signatures"] = new[]
{
new Dictionary<string, string>
{
["keyid"] = "test-key",
["sig"] = "abc123"
}
}
};
if (includeTimestamp)
{
envelope["timestamp"] = new Dictionary<string, object?>
{
["rfc3161"] = new Dictionary<string, object?>
{
["tsaUrl"] = "https://tsa.example",
["tokenDigest"] = "sha256:abc123",
["generationTime"] = new DateTimeOffset(2026, 1, 19, 12, 0, 0, TimeSpan.Zero).ToString("o")
}
};
}
return JsonSerializer.Serialize(envelope);
}
}

View File

@@ -84,4 +84,28 @@ public sealed class CommandFactoryTests
Assert.Contains(sbom.Subcommands, command => string.Equals(command.Name, "upload", StringComparison.Ordinal));
}
[Fact]
public void Create_ExposesTimestampCommands()
{
using var loggerFactory = LoggerFactory.Create(builder => builder.SetMinimumLevel(LogLevel.None));
var services = new ServiceCollection().BuildServiceProvider();
var root = CommandFactory.Create(services, new StellaOpsCliOptions(), CancellationToken.None, loggerFactory);
var ts = Assert.Single(root.Subcommands, command => string.Equals(command.Name, "ts", StringComparison.Ordinal));
Assert.Contains(ts.Subcommands, command => string.Equals(command.Name, "rfc3161", StringComparison.Ordinal));
Assert.Contains(ts.Subcommands, command => string.Equals(command.Name, "verify", StringComparison.Ordinal));
Assert.Contains(ts.Subcommands, command => string.Equals(command.Name, "info", StringComparison.Ordinal));
}
[Fact]
public void Create_ExposesEvidenceStoreCommand()
{
using var loggerFactory = LoggerFactory.Create(builder => builder.SetMinimumLevel(LogLevel.None));
var services = new ServiceCollection().BuildServiceProvider();
var root = CommandFactory.Create(services, new StellaOpsCliOptions(), CancellationToken.None, loggerFactory);
var evidence = Assert.Single(root.Subcommands, command => string.Equals(command.Name, "evidence", StringComparison.Ordinal));
Assert.Contains(evidence.Subcommands, command => string.Equals(command.Name, "store", StringComparison.Ordinal));
}
}

View File

@@ -1,5 +1,5 @@
// <copyright file="ConfigCommandTests.cs" company="StellaOps">
// SPDX-License-Identifier: AGPL-3.0-or-later
// SPDX-License-Identifier: BUSL-1.1
// Sprint: SPRINT_20260112_014_CLI_config_viewer (CLI-CONFIG-014)
// </copyright>

View File

@@ -1,5 +1,5 @@
// <copyright file="DoctorCommandGroupTests.cs" company="Stella Operations">
// Copyright (c) Stella Operations. Licensed under AGPL-3.0-or-later.
// Copyright (c) Stella Operations. Licensed under BUSL-1.1.
// </copyright>
// -----------------------------------------------------------------------------

View File

@@ -0,0 +1,54 @@
// -----------------------------------------------------------------------------
// EvidenceStoreCommandTests.cs
// Sprint: SPRINT_20260119_010 Attestor TST Integration
// Description: Unit tests for evidence store CLI command.
// -----------------------------------------------------------------------------
using System.CommandLine;
using System.Security.Cryptography;
using System.Text;
using System.Text.Json;
using Microsoft.Extensions.DependencyInjection;
using StellaOps.Cli.Commands;
using StellaOps.Cli.Configuration;
using StellaOps.Cli.Tests.Testing;
using StellaOps.TestKit;
using Xunit;
namespace StellaOps.Cli.Tests.Commands;
public sealed class EvidenceStoreCommandTests
{
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task EvidenceStoreCommand_WritesManifest()
{
using var temp = new TempDirectory();
var artifactPath = Path.Combine(temp.Path, "artifact.dsse");
var storeDir = Path.Combine(temp.Path, "store");
var bytes = Encoding.UTF8.GetBytes("evidence-store");
await File.WriteAllBytesAsync(artifactPath, bytes, CancellationToken.None);
var services = new ServiceCollection().BuildServiceProvider();
var options = new StellaOpsCliOptions();
var evidenceCommand = EvidenceCommandGroup.BuildEvidenceCommand(
services,
options,
new Option<bool>("--verbose"),
CancellationToken.None);
var root = new RootCommand { evidenceCommand };
var exitCode = await root.Parse($"evidence store --artifact \"{artifactPath}\" --store-dir \"{storeDir}\"").InvokeAsync();
Assert.Equal(0, exitCode);
var digest = "sha256:" + Convert.ToHexString(SHA256.HashData(bytes)).ToLowerInvariant();
var evidenceDir = Path.Combine(storeDir, digest.Replace(':', '_'));
var manifestPath = Path.Combine(evidenceDir, "manifest.json");
Assert.True(Directory.Exists(evidenceDir));
Assert.True(File.Exists(manifestPath));
using var doc = JsonDocument.Parse(await File.ReadAllTextAsync(manifestPath, CancellationToken.None));
Assert.Equal(digest, doc.RootElement.GetProperty("artifactDigest").GetString());
}
}

View File

@@ -1,5 +1,5 @@
// <copyright file="ProveCommandTests.cs" company="Stella Operations">
// Copyright (c) Stella Operations. Licensed under AGPL-3.0-or-later.
// Copyright (c) Stella Operations. Licensed under BUSL-1.1.
// </copyright>
// -----------------------------------------------------------------------------

View File

@@ -0,0 +1,76 @@
// -----------------------------------------------------------------------------
// TimestampCommandTests.cs
// Sprint: SPRINT_20260119_010 Attestor TST Integration
// Description: Unit tests for timestamp CLI commands.
// -----------------------------------------------------------------------------
using System.CommandLine;
using System.Security.Cryptography;
using System.Text;
using StellaOps.Cli.Commands;
using StellaOps.Cli.Tests.Testing;
using StellaOps.TestKit;
using Xunit;
namespace StellaOps.Cli.Tests.Commands;
public sealed class TimestampCommandTests
{
private readonly Option<bool> _verboseOption = new("--verbose");
private readonly CancellationToken _ct = CancellationToken.None;
[Trait("Category", TestCategories.Unit)]
[Fact]
public void TimestampCommandGroup_ExposesSubcommands()
{
var command = TimestampCommandGroup.BuildTimestampCommand(_verboseOption, _ct);
Assert.Contains(command.Children, child => child.Name == "rfc3161");
Assert.Contains(command.Children, child => child.Name == "verify");
Assert.Contains(command.Children, child => child.Name == "info");
}
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task TimestampCommandGroup_Rfc3161ThenVerify_Succeeds()
{
using var temp = new TempDirectory();
var artifactPath = Path.Combine(temp.Path, "artifact.bin");
var tokenPath = Path.Combine(temp.Path, "artifact.tst");
var bytes = Encoding.UTF8.GetBytes("timestamp-test");
await File.WriteAllBytesAsync(artifactPath, bytes, _ct);
var digest = "sha256:" + Convert.ToHexString(SHA256.HashData(bytes)).ToLowerInvariant();
var root = new RootCommand { TimestampCommandGroup.BuildTimestampCommand(_verboseOption, _ct) };
var originalOut = Console.Out;
var writer = new StringWriter();
int exitCode;
try
{
Console.SetOut(writer);
exitCode = await root.Parse($"ts rfc3161 --hash {digest} --tsa https://tsa.example --out \"{tokenPath}\"").InvokeAsync();
}
finally
{
Console.SetOut(originalOut);
}
Assert.Equal(0, exitCode);
Assert.True(File.Exists(tokenPath));
var verifyWriter = new StringWriter();
try
{
Console.SetOut(verifyWriter);
exitCode = await root.Parse($"ts verify --tst \"{tokenPath}\" --artifact \"{artifactPath}\"").InvokeAsync();
}
finally
{
Console.SetOut(originalOut);
}
Assert.Equal(0, exitCode);
Assert.Contains("PASSED", verifyWriter.ToString());
}
}

View File

@@ -1,5 +1,5 @@
// <copyright file="UnknownsGreyQueueCommandTests.cs" company="StellaOps">
// SPDX-License-Identifier: AGPL-3.0-or-later
// SPDX-License-Identifier: BUSL-1.1
// Sprint: SPRINT_20260112_010_CLI_unknowns_grey_queue_cli (CLI-UNK-005)
// </copyright>

View File

@@ -1,5 +1,5 @@
// <copyright file="VerifyBundleCommandTests.cs" company="Stella Operations">
// Copyright (c) Stella Operations. Licensed under AGPL-3.0-or-later.
// Copyright (c) Stella Operations. Licensed under BUSL-1.1.
// </copyright>
using System;

View File

@@ -1,4 +1,4 @@
// SPDX-License-Identifier: AGPL-3.0-or-later
// SPDX-License-Identifier: BUSL-1.1
// Sprint: SPRINT_4100_0006_0001 - Crypto Plugin CLI Architecture
// Task: T11 - Integration tests for crypto commands

View File

@@ -1,4 +1,4 @@
// SPDX-License-Identifier: AGPL-3.0-or-later
// SPDX-License-Identifier: BUSL-1.1
// Sprint: SPRINT_20260112_011_CLI_evidence_card_remediate_cli (REMPR-CLI-003)
// Task: REMPR-CLI-003 - CLI tests for open-pr command

View File

@@ -31,3 +31,4 @@ Source of truth: `docs-archived/implplan/2025-12-29-csproj-audit/SPRINT_20251229
| CLI-ISSUER-KEYS-TESTS-0001 | DONE | SPRINT_20260117_009 - Issuer keys tests added. |
| CLI-BINARY-ANALYSIS-TESTS-0001 | DONE | SPRINT_20260117_007 - Binary fingerprint/diff tests added. |
| CLI-POLICY-TESTS-0001 | DONE | SPRINT_20260117_010 - Policy lattice/verdict/promote tests added. |
| ATT-005 | DONE | SPRINT_20260119_010 - Timestamp CLI workflow tests added. |