Fix build and code structure improvements. New but essential UI functionality. CI improvements. Documentation improvements. AI module improvements.
This commit is contained in:
@@ -5421,6 +5421,11 @@ internal static class CommandFactory
|
||||
bundle.Add(bundleBuild);
|
||||
bundle.Add(bundleVerify);
|
||||
|
||||
// Sprint: SPRINT_20251228_002_BE_oci_attestation_attach (T3)
|
||||
// OCI attestation attachment workflow
|
||||
var attach = BuildOciAttachCommand(services, verboseOption, cancellationToken);
|
||||
var ociList = BuildOciListCommand(services, verboseOption, cancellationToken);
|
||||
|
||||
attest.Add(sign);
|
||||
attest.Add(verify);
|
||||
attest.Add(list);
|
||||
@@ -5428,10 +5433,254 @@ internal static class CommandFactory
|
||||
attest.Add(fetch);
|
||||
attest.Add(key);
|
||||
attest.Add(bundle);
|
||||
attest.Add(attach); // stella attest attach --image ...
|
||||
attest.Add(ociList); // stella attest oci-list --image ...
|
||||
|
||||
// Sprint: SPRINT_20251228_002_BE_oci_attestation_attach (T4)
|
||||
// OCI attestation verification workflow
|
||||
var ociVerify = BuildOciVerifyCommand(services, verboseOption, cancellationToken);
|
||||
attest.Add(ociVerify); // stella attest oci-verify --image ...
|
||||
|
||||
return attest;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Builds 'attest attach' subcommand for OCI attestation attachment.
|
||||
/// Sprint: SPRINT_20251228_002_BE_oci_attestation_attach (T3)
|
||||
/// </summary>
|
||||
private static Command BuildOciAttachCommand(
|
||||
IServiceProvider services,
|
||||
Option<bool> verboseOption,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var imageOption = new Option<string>("--image", new[] { "-i" })
|
||||
{
|
||||
Description = "OCI image reference (registry/repo@sha256:... or registry/repo:tag)",
|
||||
Required = true
|
||||
};
|
||||
|
||||
var attestationOption = new Option<string>("--attestation", new[] { "-a" })
|
||||
{
|
||||
Description = "Path to DSSE attestation JSON file",
|
||||
Required = true
|
||||
};
|
||||
|
||||
var predicateTypeOption = new Option<string?>("--predicate-type", new[] { "-t" })
|
||||
{
|
||||
Description = "Predicate type URI (auto-detected from attestation if not specified)"
|
||||
};
|
||||
|
||||
var signOption = new Option<bool>("--sign", new[] { "-s" })
|
||||
{
|
||||
Description = "Sign the attestation before attaching"
|
||||
};
|
||||
|
||||
var keyOption = new Option<string?>("--key", new[] { "-k" })
|
||||
{
|
||||
Description = "Path to private key for signing (PEM or PKCS#8)"
|
||||
};
|
||||
|
||||
var keylessOption = new Option<bool>("--sign-keyless")
|
||||
{
|
||||
Description = "Use Sigstore keyless signing (OIDC)"
|
||||
};
|
||||
|
||||
var replaceOption = new Option<bool>("--replace")
|
||||
{
|
||||
Description = "Replace existing attestation with same predicate type"
|
||||
};
|
||||
|
||||
var rekorOption = new Option<bool>("--rekor")
|
||||
{
|
||||
Description = "Record attestation in Sigstore Rekor transparency log"
|
||||
};
|
||||
|
||||
var attach = new Command("attach", "Attach a DSSE attestation to an OCI artifact in registry")
|
||||
{
|
||||
imageOption,
|
||||
attestationOption,
|
||||
predicateTypeOption,
|
||||
signOption,
|
||||
keyOption,
|
||||
keylessOption,
|
||||
replaceOption,
|
||||
rekorOption,
|
||||
verboseOption
|
||||
};
|
||||
|
||||
attach.SetAction(async (parseResult, ct) =>
|
||||
{
|
||||
var image = parseResult.GetValue(imageOption) ?? string.Empty;
|
||||
var attestationPath = parseResult.GetValue(attestationOption) ?? string.Empty;
|
||||
var predicateType = parseResult.GetValue(predicateTypeOption);
|
||||
var sign = parseResult.GetValue(signOption);
|
||||
var keyPath = parseResult.GetValue(keyOption);
|
||||
var keyless = parseResult.GetValue(keylessOption);
|
||||
var replace = parseResult.GetValue(replaceOption);
|
||||
var rekor = parseResult.GetValue(rekorOption);
|
||||
var verbose = parseResult.GetValue(verboseOption);
|
||||
|
||||
return await CommandHandlers.HandleOciAttestAttachAsync(
|
||||
services,
|
||||
image,
|
||||
attestationPath,
|
||||
predicateType,
|
||||
sign,
|
||||
keyPath,
|
||||
keyless,
|
||||
replace,
|
||||
rekor,
|
||||
verbose,
|
||||
cancellationToken);
|
||||
});
|
||||
|
||||
return attach;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Builds 'attest oci-list' subcommand for listing OCI attestations.
|
||||
/// Sprint: SPRINT_20251228_002_BE_oci_attestation_attach (T3)
|
||||
/// </summary>
|
||||
private static Command BuildOciListCommand(
|
||||
IServiceProvider services,
|
||||
Option<bool> verboseOption,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var imageOption = new Option<string>("--image", new[] { "-i" })
|
||||
{
|
||||
Description = "OCI image reference",
|
||||
Required = true
|
||||
};
|
||||
|
||||
var formatOption = new Option<string?>("--format", new[] { "-f" })
|
||||
{
|
||||
Description = "Output format (json, table). Default: table"
|
||||
};
|
||||
|
||||
var ociList = new Command("oci-list", "List attestations attached to an OCI artifact in registry")
|
||||
{
|
||||
imageOption,
|
||||
formatOption,
|
||||
verboseOption
|
||||
};
|
||||
|
||||
ociList.SetAction(async (parseResult, ct) =>
|
||||
{
|
||||
var image = parseResult.GetValue(imageOption) ?? string.Empty;
|
||||
var format = parseResult.GetValue(formatOption) ?? "table";
|
||||
var verbose = parseResult.GetValue(verboseOption);
|
||||
|
||||
return await CommandHandlers.HandleOciAttestListAsync(
|
||||
services,
|
||||
image,
|
||||
format,
|
||||
verbose,
|
||||
cancellationToken);
|
||||
});
|
||||
|
||||
return ociList;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Builds 'attest oci-verify' subcommand for verifying OCI attestations.
|
||||
/// Sprint: SPRINT_20251228_002_BE_oci_attestation_attach (T4)
|
||||
/// </summary>
|
||||
private static Command BuildOciVerifyCommand(
|
||||
IServiceProvider services,
|
||||
Option<bool> verboseOption,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var imageOption = new Option<string>("--image", new[] { "-i" })
|
||||
{
|
||||
Description = "OCI image reference (registry/repo@sha256:... or registry/repo:tag)",
|
||||
Required = true
|
||||
};
|
||||
|
||||
var predicateTypeOption = new Option<string?>("--predicate-type", new[] { "-t" })
|
||||
{
|
||||
Description = "Filter by predicate type URI (verifies at least one attestation matches)"
|
||||
};
|
||||
|
||||
var policyOption = new Option<string?>("--policy", new[] { "-p" })
|
||||
{
|
||||
Description = "Path to verification policy JSON/Rego file"
|
||||
};
|
||||
|
||||
var rootOption = new Option<string?>("--root")
|
||||
{
|
||||
Description = "Path to trusted root certificate (PEM format) for signature verification"
|
||||
};
|
||||
|
||||
var keyOption = new Option<string?>("--key", new[] { "-k" })
|
||||
{
|
||||
Description = "Path to public key (PEM format) for signature verification"
|
||||
};
|
||||
|
||||
var rekorOption = new Option<bool>("--rekor")
|
||||
{
|
||||
Description = "Verify inclusion in Sigstore Rekor transparency log"
|
||||
};
|
||||
|
||||
var strictOption = new Option<bool>("--strict")
|
||||
{
|
||||
Description = "Fail if any attestation fails verification (default: fail on no valid attestations)"
|
||||
};
|
||||
|
||||
var formatOption = new Option<string?>("--format", new[] { "-f" })
|
||||
{
|
||||
Description = "Output format (json, table). Default: table"
|
||||
};
|
||||
|
||||
var outputOption = new Option<string?>("--output", new[] { "-o" })
|
||||
{
|
||||
Description = "Write verification report to file"
|
||||
};
|
||||
|
||||
var ociVerify = new Command("oci-verify", "Verify attestations attached to an OCI artifact in registry")
|
||||
{
|
||||
imageOption,
|
||||
predicateTypeOption,
|
||||
policyOption,
|
||||
rootOption,
|
||||
keyOption,
|
||||
rekorOption,
|
||||
strictOption,
|
||||
formatOption,
|
||||
outputOption,
|
||||
verboseOption
|
||||
};
|
||||
|
||||
ociVerify.SetAction(async (parseResult, ct) =>
|
||||
{
|
||||
var image = parseResult.GetValue(imageOption) ?? string.Empty;
|
||||
var predicateType = parseResult.GetValue(predicateTypeOption);
|
||||
var policy = parseResult.GetValue(policyOption);
|
||||
var root = parseResult.GetValue(rootOption);
|
||||
var key = parseResult.GetValue(keyOption);
|
||||
var rekor = parseResult.GetValue(rekorOption);
|
||||
var strict = parseResult.GetValue(strictOption);
|
||||
var format = parseResult.GetValue(formatOption) ?? "table";
|
||||
var output = parseResult.GetValue(outputOption);
|
||||
var verbose = parseResult.GetValue(verboseOption);
|
||||
|
||||
return await CommandHandlers.HandleOciAttestVerifyAsync(
|
||||
services,
|
||||
image,
|
||||
predicateType,
|
||||
policy,
|
||||
root,
|
||||
key,
|
||||
rekor,
|
||||
strict,
|
||||
format,
|
||||
output,
|
||||
verbose,
|
||||
cancellationToken);
|
||||
});
|
||||
|
||||
return ociVerify;
|
||||
}
|
||||
|
||||
private static Command BuildRiskProfileCommand(Option<bool> verboseOption, CancellationToken cancellationToken)
|
||||
{
|
||||
_ = cancellationToken;
|
||||
|
||||
Reference in New Issue
Block a user