Add comprehensive security tests for OWASP A02, A05, A07, and A08 categories
Some checks failed
Docs CI / lint-and-preview (push) Has been cancelled
Export Center CI / export-ci (push) Has been cancelled
Findings Ledger CI / build-test (push) Has been cancelled
Findings Ledger CI / migration-validation (push) Has been cancelled
Findings Ledger CI / generate-manifest (push) Has been cancelled
Manifest Integrity / Validate Schema Integrity (push) Has been cancelled
Lighthouse CI / Lighthouse Audit (push) Has been cancelled
Lighthouse CI / Axe Accessibility Audit (push) Has been cancelled
Manifest Integrity / Validate Contract Documents (push) Has been cancelled
Manifest Integrity / Validate Pack Fixtures (push) Has been cancelled
Manifest Integrity / Audit SHA256SUMS Files (push) Has been cancelled
Manifest Integrity / Verify Merkle Roots (push) Has been cancelled
Policy Lint & Smoke / policy-lint (push) Has been cancelled
Policy Simulation / policy-simulate (push) Has been cancelled

- Implemented tests for Cryptographic Failures (A02) to ensure proper handling of sensitive data, secure algorithms, and key management.
- Added tests for Security Misconfiguration (A05) to validate production configurations, security headers, CORS settings, and feature management.
- Developed tests for Authentication Failures (A07) to enforce strong password policies, rate limiting, session management, and MFA support.
- Created tests for Software and Data Integrity Failures (A08) to verify artifact signatures, SBOM integrity, attestation chains, and feed updates.
This commit is contained in:
master
2025-12-16 16:40:19 +02:00
parent 415eff1207
commit 2170a58734
206 changed files with 30547 additions and 534 deletions

View File

@@ -0,0 +1,255 @@
using System.CommandLine;
using System.CommandLine.Invocation;
using Microsoft.Extensions.Logging;
namespace StellaOps.Cli.Commands.Proof;
/// <summary>
/// Command group for proof chain operations.
/// Implements advisory §15 CLI commands.
/// </summary>
public class ProofCommandGroup
{
private readonly ILogger<ProofCommandGroup> _logger;
public ProofCommandGroup(ILogger<ProofCommandGroup> logger)
{
_logger = logger;
}
/// <summary>
/// Build the proof command tree.
/// </summary>
public Command BuildCommand()
{
var proofCommand = new Command("proof", "Proof chain operations");
proofCommand.AddCommand(BuildVerifyCommand());
proofCommand.AddCommand(BuildSpineCommand());
return proofCommand;
}
private Command BuildVerifyCommand()
{
var artifactArg = new Argument<string>(
name: "artifact",
description: "Artifact digest (sha256:...) or PURL");
var sbomOption = new Option<FileInfo?>(
aliases: ["-s", "--sbom"],
description: "Path to SBOM file");
var vexOption = new Option<FileInfo?>(
aliases: ["--vex"],
description: "Path to VEX file");
var anchorOption = new Option<Guid?>(
aliases: ["-a", "--anchor"],
description: "Trust anchor ID");
var offlineOption = new Option<bool>(
name: "--offline",
description: "Offline mode (skip Rekor verification)");
var outputOption = new Option<string>(
name: "--output",
getDefaultValue: () => "text",
description: "Output format: text, json");
var verboseOption = new Option<int>(
aliases: ["-v", "--verbose"],
getDefaultValue: () => 0,
description: "Verbose output level (use -vv for very verbose)");
var verifyCommand = new Command("verify", "Verify an artifact's proof chain")
{
artifactArg,
sbomOption,
vexOption,
anchorOption,
offlineOption,
outputOption,
verboseOption
};
verifyCommand.SetHandler(async (context) =>
{
var artifact = context.ParseResult.GetValueForArgument(artifactArg);
var sbomFile = context.ParseResult.GetValueForOption(sbomOption);
var vexFile = context.ParseResult.GetValueForOption(vexOption);
var anchorId = context.ParseResult.GetValueForOption(anchorOption);
var offline = context.ParseResult.GetValueForOption(offlineOption);
var output = context.ParseResult.GetValueForOption(outputOption) ?? "text";
var verbose = context.ParseResult.GetValueForOption(verboseOption);
context.ExitCode = await VerifyAsync(
artifact,
sbomFile,
vexFile,
anchorId,
offline,
output,
verbose,
context.GetCancellationToken());
});
return verifyCommand;
}
private Command BuildSpineCommand()
{
var spineCommand = new Command("spine", "Proof spine operations");
// stellaops proof spine create
var createCommand = new Command("create", "Create a proof spine for an artifact");
var artifactArg = new Argument<string>("artifact", "Artifact digest or PURL");
createCommand.AddArgument(artifactArg);
createCommand.SetHandler(async (context) =>
{
var artifact = context.ParseResult.GetValueForArgument(artifactArg);
context.ExitCode = await CreateSpineAsync(artifact, context.GetCancellationToken());
});
// stellaops proof spine show
var showCommand = new Command("show", "Show proof spine details");
var bundleArg = new Argument<string>("bundleId", "Proof bundle ID");
showCommand.AddArgument(bundleArg);
showCommand.SetHandler(async (context) =>
{
var bundleId = context.ParseResult.GetValueForArgument(bundleArg);
context.ExitCode = await ShowSpineAsync(bundleId, context.GetCancellationToken());
});
spineCommand.AddCommand(createCommand);
spineCommand.AddCommand(showCommand);
return spineCommand;
}
private async Task<int> VerifyAsync(
string artifact,
FileInfo? sbomFile,
FileInfo? vexFile,
Guid? anchorId,
bool offline,
string output,
int verbose,
CancellationToken ct)
{
try
{
if (verbose > 0)
{
_logger.LogDebug("Starting proof verification for {Artifact}", artifact);
}
// Validate artifact format
if (!IsValidArtifactId(artifact))
{
_logger.LogError("Invalid artifact format: {Artifact}", artifact);
return ProofExitCodes.SystemError;
}
if (verbose > 0)
{
_logger.LogDebug("Artifact format valid: {Artifact}", artifact);
}
// TODO: Implement actual verification using IVerificationPipeline
// 1. Load SBOM if provided
// 2. Load VEX if provided
// 3. Find or use specified trust anchor
// 4. Run verification pipeline
// 5. Check Rekor inclusion (unless offline)
// 6. Generate receipt
if (verbose > 0)
{
_logger.LogDebug("Verification pipeline not yet implemented");
}
if (output == "json")
{
Console.WriteLine("{");
Console.WriteLine($" \"artifact\": \"{artifact}\",");
Console.WriteLine(" \"status\": \"pass\",");
Console.WriteLine(" \"message\": \"Verification successful (stub)\"");
Console.WriteLine("}");
}
else
{
Console.WriteLine("StellaOps Scan Summary");
Console.WriteLine("══════════════════════");
Console.WriteLine($"Artifact: {artifact}");
Console.WriteLine("Status: PASS (stub - verification not yet implemented)");
}
return ProofExitCodes.Success;
}
catch (Exception ex)
{
_logger.LogError(ex, "Verification failed for {Artifact}", artifact);
return ProofExitCodes.SystemError;
}
}
private async Task<int> CreateSpineAsync(string artifact, CancellationToken ct)
{
try
{
_logger.LogInformation("Creating proof spine for {Artifact}", artifact);
// TODO: Implement spine creation using IProofSpineAssembler
Console.WriteLine($"Creating proof spine for: {artifact}");
Console.WriteLine("Spine creation not yet implemented");
return ProofExitCodes.Success;
}
catch (Exception ex)
{
_logger.LogError(ex, "Failed to create spine for {Artifact}", artifact);
return ProofExitCodes.SystemError;
}
}
private async Task<int> ShowSpineAsync(string bundleId, CancellationToken ct)
{
try
{
_logger.LogInformation("Showing proof spine {BundleId}", bundleId);
// TODO: Implement spine retrieval
Console.WriteLine($"Proof spine: {bundleId}");
Console.WriteLine("Spine display not yet implemented");
return ProofExitCodes.Success;
}
catch (Exception ex)
{
_logger.LogError(ex, "Failed to show spine {BundleId}", bundleId);
return ProofExitCodes.SystemError;
}
}
private static bool IsValidArtifactId(string artifact)
{
if (string.IsNullOrWhiteSpace(artifact))
return false;
// sha256:<64-hex>
if (artifact.StartsWith("sha256:", StringComparison.OrdinalIgnoreCase))
{
var hash = artifact[7..];
return hash.Length == 64 && hash.All(c => "0123456789abcdef".Contains(char.ToLowerInvariant(c)));
}
// pkg:type/...
if (artifact.StartsWith("pkg:", StringComparison.OrdinalIgnoreCase))
{
return artifact.Length > 5; // Minimal PURL validation
}
return false;
}
}