File: Agents\SigstoreNpmProvenanceCheckerTests.cs
Web Access
Project: src\tests\Aspire.Cli.Tests\Aspire.Cli.Tests.csproj (Aspire.Cli.Tests)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using Aspire.Cli.Npm;
 
namespace Aspire.Cli.Tests.Agents;
 
public class SigstoreNpmProvenanceCheckerTests
{
    [Fact]
    public void ParseAttestation_WithValidSlsaAttestation_ReturnsBundleAndProvenance()
    {
        var json = BuildAttestationJsonWithBundle("https://github.com/microsoft/playwright-cli");
 
        var result = SigstoreNpmProvenanceChecker.ParseAttestation(json);
 
        Assert.Equal(ProvenanceVerificationOutcome.Verified, result.Outcome);
        Assert.NotNull(result.BundleNode);
        Assert.NotNull(result.BundleNode["dsseEnvelope"]);
        Assert.NotNull(result.Provenance);
        Assert.Equal("https://github.com/microsoft/playwright-cli", result.Provenance.SourceRepository);
        Assert.Equal(".github/workflows/publish.yml", result.Provenance.WorkflowPath);
        Assert.Equal("refs/tags/v0.1.1", result.Provenance.WorkflowRef);
    }
 
    [Fact]
    public void ParseAttestation_WithNoSlsaPredicate_ReturnsSlsaProvenanceNotFound()
    {
        var json = """
        {
            "attestations": [
                {
                    "predicateType": "https://github.com/npm/attestation/tree/main/specs/publish/v0.1",
                    "bundle": {
                        "dsseEnvelope": {
                            "payload": ""
                        }
                    }
                }
            ]
        }
        """;
 
        var result = SigstoreNpmProvenanceChecker.ParseAttestation(json);
 
        Assert.Equal(ProvenanceVerificationOutcome.SlsaProvenanceNotFound, result.Outcome);
    }
 
    [Fact]
    public void ParseAttestation_WithEmptyAttestations_ReturnsSlsaProvenanceNotFound()
    {
        var json = """{"attestations": []}""";
 
        var result = SigstoreNpmProvenanceChecker.ParseAttestation(json);
 
        Assert.Equal(ProvenanceVerificationOutcome.SlsaProvenanceNotFound, result.Outcome);
    }
 
    [Fact]
    public void ParseAttestation_WithInvalidJson_ReturnsAttestationParseFailed()
    {
        var result = SigstoreNpmProvenanceChecker.ParseAttestation("not valid json {{{");
 
        Assert.Equal(ProvenanceVerificationOutcome.AttestationParseFailed, result.Outcome);
    }
 
    [Fact]
    public void ParseAttestation_WithMissingPayload_ReturnsPayloadDecodeFailed()
    {
        var json = """
        {
            "attestations": [
                {
                    "predicateType": "https://slsa.dev/provenance/v1",
                    "bundle": {
                        "dsseEnvelope": {}
                    }
                }
            ]
        }
        """;
 
        var result = SigstoreNpmProvenanceChecker.ParseAttestation(json);
 
        Assert.Equal(ProvenanceVerificationOutcome.PayloadDecodeFailed, result.Outcome);
        Assert.NotNull(result.BundleNode);
    }
 
    [Fact]
    public void ParseProvenanceFromStatement_WithValidStatement_ReturnsProvenance()
    {
        var payload = BuildProvenancePayload("https://github.com/microsoft/playwright-cli");
        var bytes = System.Text.Encoding.UTF8.GetBytes(payload);
 
        var provenance = SigstoreNpmProvenanceChecker.ParseProvenanceFromStatement(bytes);
 
        Assert.NotNull(provenance);
        Assert.Equal("https://github.com/microsoft/playwright-cli", provenance.SourceRepository);
        Assert.Equal(".github/workflows/publish.yml", provenance.WorkflowPath);
        Assert.Equal("refs/tags/v0.1.1", provenance.WorkflowRef);
        Assert.Equal("https://github.com/actions/runner/github-hosted", provenance.BuilderId);
        Assert.Equal("https://slsa-framework.github.io/github-actions-buildtypes/workflow/v1", provenance.BuildType);
    }
 
    [Fact]
    public void ParseProvenanceFromStatement_WithInvalidJson_ReturnsNull()
    {
        var bytes = System.Text.Encoding.UTF8.GetBytes("not json");
 
        var provenance = SigstoreNpmProvenanceChecker.ParseProvenanceFromStatement(bytes);
 
        Assert.Null(provenance);
    }
 
    [Fact]
    public void VerifyProvenanceFields_WithAllFieldsMatching_ReturnsVerified()
    {
        var provenance = new NpmProvenanceData
        {
            SourceRepository = "https://github.com/microsoft/playwright-cli",
            WorkflowPath = ".github/workflows/publish.yml",
            BuildType = "https://slsa-framework.github.io/github-actions-buildtypes/workflow/v1",
            WorkflowRef = "refs/tags/v0.1.1",
            BuilderId = "https://github.com/actions/runner/github-hosted"
        };
 
        var result = SigstoreNpmProvenanceChecker.VerifyProvenanceFields(
            provenance,
            "https://github.com/microsoft/playwright-cli",
            ".github/workflows/publish.yml",
            "https://slsa-framework.github.io/github-actions-buildtypes/workflow/v1",
            refInfo => refInfo.Kind == "tags");
 
        Assert.Equal(ProvenanceVerificationOutcome.Verified, result.Outcome);
    }
 
    [Fact]
    public void VerifyProvenanceFields_WithSourceRepoMismatch_ReturnsSourceRepositoryMismatch()
    {
        var provenance = new NpmProvenanceData
        {
            SourceRepository = "https://github.com/evil/repo",
            WorkflowPath = ".github/workflows/publish.yml",
            BuildType = "https://slsa-framework.github.io/github-actions-buildtypes/workflow/v1",
        };
 
        var result = SigstoreNpmProvenanceChecker.VerifyProvenanceFields(
            provenance,
            "https://github.com/microsoft/playwright-cli",
            ".github/workflows/publish.yml",
            "https://slsa-framework.github.io/github-actions-buildtypes/workflow/v1",
            null);
 
        Assert.Equal(ProvenanceVerificationOutcome.SourceRepositoryMismatch, result.Outcome);
    }
 
    [Fact]
    public void VerifyProvenanceFields_WithWorkflowMismatch_ReturnsWorkflowMismatch()
    {
        var provenance = new NpmProvenanceData
        {
            SourceRepository = "https://github.com/microsoft/playwright-cli",
            WorkflowPath = ".github/workflows/evil.yml",
            BuildType = "https://slsa-framework.github.io/github-actions-buildtypes/workflow/v1",
        };
 
        var result = SigstoreNpmProvenanceChecker.VerifyProvenanceFields(
            provenance,
            "https://github.com/microsoft/playwright-cli",
            ".github/workflows/publish.yml",
            "https://slsa-framework.github.io/github-actions-buildtypes/workflow/v1",
            null);
 
        Assert.Equal(ProvenanceVerificationOutcome.WorkflowMismatch, result.Outcome);
    }
 
    [Fact]
    public void VerifyProvenanceFields_WithBuildTypeMismatch_ReturnsBuildTypeMismatch()
    {
        var provenance = new NpmProvenanceData
        {
            SourceRepository = "https://github.com/microsoft/playwright-cli",
            WorkflowPath = ".github/workflows/publish.yml",
            BuildType = "https://evil.example.com/build/v1",
        };
 
        var result = SigstoreNpmProvenanceChecker.VerifyProvenanceFields(
            provenance,
            "https://github.com/microsoft/playwright-cli",
            ".github/workflows/publish.yml",
            "https://slsa-framework.github.io/github-actions-buildtypes/workflow/v1",
            null);
 
        Assert.Equal(ProvenanceVerificationOutcome.BuildTypeMismatch, result.Outcome);
    }
 
    [Fact]
    public void VerifyProvenanceFields_WithWorkflowRefValidationFailure_ReturnsWorkflowRefMismatch()
    {
        var provenance = new NpmProvenanceData
        {
            SourceRepository = "https://github.com/microsoft/playwright-cli",
            WorkflowPath = ".github/workflows/publish.yml",
            BuildType = "https://slsa-framework.github.io/github-actions-buildtypes/workflow/v1",
            WorkflowRef = "refs/heads/main"
        };
 
        var result = SigstoreNpmProvenanceChecker.VerifyProvenanceFields(
            provenance,
            "https://github.com/microsoft/playwright-cli",
            ".github/workflows/publish.yml",
            "https://slsa-framework.github.io/github-actions-buildtypes/workflow/v1",
            refInfo => refInfo.Kind == "tags");
 
        Assert.Equal(ProvenanceVerificationOutcome.WorkflowRefMismatch, result.Outcome);
    }
 
    [Theory]
    [InlineData("https://github.com/microsoft/playwright-cli", "microsoft", "playwright-cli")]
    [InlineData("https://github.com/dotnet/aspire", "dotnet", "aspire")]
    [InlineData("https://github.com/owner/repo", "owner", "repo")]
    public void TryParseGitHubOwnerRepo_WithValidUrl_ReturnsTrueAndParsesComponents(string url, string expectedOwner, string expectedRepo)
    {
        var result = SigstoreNpmProvenanceChecker.TryParseGitHubOwnerRepo(url, out var owner, out var repo);
 
        Assert.True(result);
        Assert.Equal(expectedOwner, owner);
        Assert.Equal(expectedRepo, repo);
    }
 
    [Theory]
    [InlineData("not-a-url")]
    [InlineData("https://github.com/")]
    [InlineData("https://github.com/only-owner")]
    public void TryParseGitHubOwnerRepo_WithInvalidUrl_ReturnsFalse(string url)
    {
        var result = SigstoreNpmProvenanceChecker.TryParseGitHubOwnerRepo(url, out _, out _);
 
        Assert.False(result);
    }
 
    private static string BuildAttestationJsonWithBundle(string sourceRepository)
    {
        var payload = BuildProvenancePayload(sourceRepository);
        var payloadBase64 = Convert.ToBase64String(System.Text.Encoding.UTF8.GetBytes(payload));
 
        return $$"""
        {
            "attestations": [
                {
                    "predicateType": "https://slsa.dev/provenance/v1",
                    "bundle": {
                        "mediaType": "application/vnd.dev.sigstore.bundle.v0.3+json",
                        "dsseEnvelope": {
                            "payload": "{{payloadBase64}}",
                            "payloadType": "application/vnd.in-toto+json",
                            "signatures": [
                                {
                                    "sig": "MEUCIQC+fake+signature",
                                    "keyid": ""
                                }
                            ]
                        },
                        "verificationMaterial": {
                            "certificate": {
                                "rawBytes": "MIIFake..."
                            },
                            "tlogEntries": [
                                {
                                    "logIndex": "12345",
                                    "logId": {
                                        "keyId": "fake-key-id"
                                    },
                                    "kindVersion": {
                                        "kind": "dsse",
                                        "version": "0.0.1"
                                    },
                                    "integratedTime": "1700000000",
                                    "inclusionPromise": {
                                        "signedEntryTimestamp": "MEUC..."
                                    },
                                    "canonicalizedBody": "eyJ..."
                                }
                            ]
                        }
                    }
                }
            ]
        }
        """;
    }
 
    private static string BuildProvenancePayload(string sourceRepository)
    {
        return $$"""
        {
            "_type": "https://in-toto.io/Statement/v1",
            "subject": [
                {
                    "name": "pkg:npm/@playwright/cli@0.1.1",
                    "digest": { "sha512": "abc123" }
                }
            ],
            "predicateType": "https://slsa.dev/provenance/v1",
            "predicate": {
                "buildDefinition": {
                    "buildType": "https://slsa-framework.github.io/github-actions-buildtypes/workflow/v1",
                    "externalParameters": {
                        "workflow": {
                            "ref": "refs/tags/v0.1.1",
                            "repository": "{{sourceRepository}}",
                            "path": ".github/workflows/publish.yml"
                        }
                    }
                },
                "runDetails": {
                    "builder": {
                        "id": "https://github.com/actions/runner/github-hosted"
                    }
                }
            }
        }
        """;
    }
}