Skip to content

Commit

Permalink
fix: Support pre-releases on trusted repos (#552)
Browse files Browse the repository at this point in the history
Support pre-releases on trusted repos

---------

Signed-off-by: Ian Lewis <ianlewis@google.com>
  • Loading branch information
ianlewis committed Apr 10, 2023
1 parent b01cb9d commit f96d91b
Show file tree
Hide file tree
Showing 9 changed files with 139 additions and 26 deletions.
3 changes: 2 additions & 1 deletion .github/workflows/pre-submit.e2e.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ jobs:
repository: slsa-framework/example-package

- name: Run verification script with testdata and slsa-verifier HEAD
run: ./__THIS_REPO__/.github/workflows/scripts/e2e-cli.sh
env:
SLSA_VERIFIER_TESTING: "true"
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Necessary to use the gh CLI.
run: ./__THIS_REPO__/.github/workflows/scripts/e2e-cli.sh
22 changes: 11 additions & 11 deletions .github/workflows/scripts/e2e-cli.sh
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,15 @@ repo="slsa-framework/example-package"
api_version="X-GitHub-Api-Version: 2022-11-28"
# Verify provenance authenticity with slsa-verifier at HEAD

download_artifact(){
local run_id="$1"
local artifact_name="$2"
# Get the artifact ID for 'artifact1'
artifact_id=$(gh api -H "Accept: application/vnd.github+json" -H "$api_version" "/repos/$repo/actions/runs/$run_id/artifacts" | jq ".artifacts[] | select(.name == \"$artifact_name\") | .id")
echo "artifact_id:$artifact_id"

gh api -H "Accept: application/vnd.github+json" -H "$api_version" "/repos/$repo/actions/artifacts/$artifact_id/zip" > "$artifact_name.zip"
unzip "$artifact_name".zip
download_artifact() {
local run_id="$1"
local artifact_name="$2"
# Get the artifact ID for 'artifact1'
artifact_id=$(gh api -H "Accept: application/vnd.github+json" -H "$api_version" "/repos/$repo/actions/runs/$run_id/artifacts" | jq ".artifacts[] | select(.name == \"$artifact_name\") | .id")
echo "artifact_id:$artifact_id"

gh api -H "Accept: application/vnd.github+json" -H "$api_version" "/repos/$repo/actions/artifacts/$artifact_id/zip" >"$artifact_name.zip"
unzip "$artifact_name".zip
}

# Get workflow ID.
Expand All @@ -26,7 +26,7 @@ echo "run_id:$run_id"
download_artifact "$run_id" "artifacts1"
download_artifact "$run_id" "attestation1.intoto.jsonl"

cd __EXAMPLE_PACKAGE__
cd __EXAMPLE_PACKAGE__ || exit 1
# shellcheck source=/dev/null
source "./.github/workflows/scripts/e2e-verify.common.sh"

Expand All @@ -35,7 +35,7 @@ export THIS_FILE=e2e.generic.schedule.main.multi-uses.slsa3.yml
export BRANCH=main

# Set BINARY and PROVENANCE
cd -
cd - || exit 1
export BINARY=artifact1
export PROVENANCE=attestation1.intoto.jsonl

Expand Down
8 changes: 8 additions & 0 deletions cli/slsa-verifier/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"fmt"
"os"

"github.com/slsa-framework/slsa-verifier/v2/options"
"github.com/spf13/cobra"
"sigs.k8s.io/release-utils/version"
)
Expand All @@ -16,6 +17,12 @@ func check(err error) {
}
}

func envWarnings() {
if options.TestingEnabled() {
fmt.Fprintf(os.Stderr, "WARNING: Insecure SLSA_VERIFIER_TESTING is enabled.\n")
}
}

func rootCmd() *cobra.Command {
c := &cobra.Command{
Use: "slsa-verifier",
Expand All @@ -36,5 +43,6 @@ For more information on SLSA, visit https://slsa.dev`,
}

func main() {
envWarnings()
check(rootCmd().Execute())
}
1 change: 1 addition & 0 deletions cli/slsa-verifier/main_regression_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1303,6 +1303,7 @@ func Test_runVerifyGCBArtifactImage(t *testing.T) {
func Test_runVerifyGHADockerBased(t *testing.T) {
// We cannot use t.Setenv due to parallelized tests.
os.Setenv("SLSA_VERIFIER_EXPERIMENTAL", "1")
os.Setenv("SLSA_VERIFIER_TESTING", "1")

t.Parallel()

Expand Down
21 changes: 19 additions & 2 deletions options/env.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,24 @@
package options

import "os"
import (
"os"
"strconv"
)

// ExperimentalEnabled returns true if experimental features are currently
// enabled.
func ExperimentalEnabled() bool {
return os.Getenv("SLSA_VERIFIER_EXPERIMENTAL") == "1"
if b, err := strconv.ParseBool(os.Getenv("SLSA_VERIFIER_EXPERIMENTAL")); err == nil {
return b
}
return false
}

// TestingEnabled returns true if the SLSA_VERIFIER_TESTING environment
// variable is set.
func TestingEnabled() bool {
if b, err := strconv.ParseBool(os.Getenv("SLSA_VERIFIER_TESTING")); err == nil {
return b
}
return false
}
22 changes: 21 additions & 1 deletion verifiers/internal/gha/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,27 @@ func verifyTrustedBuilderID(certPath, certTag string, expectedBuilderID *string,
func verifyTrustedBuilderRef(id *WorkflowIdentity, ref string) error {
if (id.CallerRepository == trustedBuilderRepository ||
id.CallerRepository == e2eTestRepository) &&
strings.EqualFold("refs/heads/main", ref) {
options.TestingEnabled() {
// Allow verification on the main branch to support e2e tests.
if ref == "refs/heads/main" {
return nil
}

// Extract the tag.
pin, err := utils.TagFromGitHubRef(ref)
if err != nil {
return err
}

// Tags on trusted repositories should be a valid semver with version
// core including all three parts and no build identifier.
versionCore := strings.Split(pin, "-")[0]
if !semver.IsValid(pin) ||
len(strings.Split(versionCore, ".")) != 3 ||
semver.Build(pin) != "" {
return fmt.Errorf("%w: %s: version tag not valid", serrors.ErrorInvalidRef, pin)
}

return nil
}

Expand Down
67 changes: 58 additions & 9 deletions verifiers/internal/gha/builder_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -488,18 +488,25 @@ func Test_verifyTrustedBuilderID(t *testing.T) {
}

func Test_verifyTrustedBuilderRef(t *testing.T) {
t.Parallel()
tests := []struct {
name string
callerRepo string
builderRef string
expected error
name string
callerRepo string
builderRef string
expected error
testingEnabled bool
}{
// Trusted repo.
{
name: "main allowed for builder",
name: "main not allowed for builder",
callerRepo: trustedBuilderRepository,
builderRef: "refs/heads/main",
expected: serrors.ErrorInvalidRef,
},
{
name: "main allowed for builder w/ testing enabled",
callerRepo: trustedBuilderRepository,
builderRef: "refs/heads/main",
testingEnabled: true,
},
{
name: "full semver for builder",
Expand Down Expand Up @@ -538,10 +545,18 @@ func Test_verifyTrustedBuilderRef(t *testing.T) {
},
// E2e tests repo.
{
name: "main allowed for test repo",
name: "main not allowed for test repo",
callerRepo: e2eTestRepository,
builderRef: "refs/heads/main",
expected: serrors.ErrorInvalidRef,
},
{
name: "main allowed for test repo w/ testing enabled",
callerRepo: e2eTestRepository,
builderRef: "refs/heads/main",
testingEnabled: true,
},

{
name: "full semver for test repo",
callerRepo: e2eTestRepository,
Expand Down Expand Up @@ -585,6 +600,14 @@ func Test_verifyTrustedBuilderRef(t *testing.T) {
expected: serrors.ErrorInvalidRef,
},
{
name: "main not allowed for other repos w/ testing enabled",
callerRepo: "some/repo",
builderRef: "refs/heads/main",
testingEnabled: true,
expected: serrors.ErrorInvalidRef,
},
{

name: "full semver for other repos",
callerRepo: "some/repo",
builderRef: "refs/tags/v1.2.3",
Expand All @@ -607,28 +630,54 @@ func Test_verifyTrustedBuilderRef(t *testing.T) {
builderRef: "refs/tags/v1.2.3-alpha",
expected: serrors.ErrorInvalidRef,
},
{
name: "full semver with prerelease for other repos w/ testing enabled",
callerRepo: "some/repo",
builderRef: "refs/tags/v1.2.3-alpha",
testingEnabled: true,
expected: serrors.ErrorInvalidRef,
},
{
name: "full semver with build for other repos",
callerRepo: "some/repo",
builderRef: "refs/tags/v1.2.3+123",
expected: serrors.ErrorInvalidRef,
},
{
name: "full semver with build for other repos w/ testing enabled",
callerRepo: "some/repo",
builderRef: "refs/tags/v1.2.3+123",
testingEnabled: true,
expected: serrors.ErrorInvalidRef,
},
{
name: "full semver with build/prerelease for other repos",
callerRepo: "some/repo",
builderRef: "refs/tags/v1.2.3-alpha+123",
expected: serrors.ErrorInvalidRef,
},
{
name: "full semver with build/prerelease for other repos w/ testing enabled",
callerRepo: "some/repo",
builderRef: "refs/tags/v1.2.3-alpha+123",
testingEnabled: true,
expected: serrors.ErrorInvalidRef,
},
}
for _, tt := range tests {
tt := tt // Re-initializing variable so it is not changed while executing the closure below
t.Run(tt.name, func(t *testing.T) {
t.Parallel()

wf := WorkflowIdentity{
CallerRepository: tt.callerRepo,
}

if tt.testingEnabled {
t.Setenv("SLSA_VERIFIER_TESTING", "1")
} else {
// Ensure that the variable is not set.
t.Setenv("SLSA_VERIFIER_TESTING", "")
}

err := verifyTrustedBuilderRef(&wf, tt.builderRef)
if !errCmp(err, tt.expected) {
t.Errorf(cmp.Diff(err, tt.expected, cmpopts.EquateErrors()))
Expand Down
4 changes: 2 additions & 2 deletions verifiers/internal/gha/provenance.go
Original file line number Diff line number Diff line change
Expand Up @@ -317,7 +317,7 @@ func VerifyBranch(prov slsaprovenance.Provenance, expectedBranch string) error {
}

expectedBranch = "refs/heads/" + expectedBranch
if !strings.EqualFold(branch, expectedBranch) {
if branch != expectedBranch {
return fmt.Errorf("expected branch '%s', got '%s': %w", expectedBranch, branch, serrors.ErrorMismatchBranch)
}

Expand All @@ -331,7 +331,7 @@ func VerifyTag(prov slsaprovenance.Provenance, expectedTag string) error {
}

expectedTag = "refs/tags/" + expectedTag
if !strings.EqualFold(tag, expectedTag) {
if tag != expectedTag {
return fmt.Errorf("expected tag '%s', got '%s': %w", expectedTag, tag, serrors.ErrorMismatchTag)
}

Expand Down
17 changes: 17 additions & 0 deletions verifiers/internal/gha/provenance_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -638,6 +638,12 @@ func Test_VerifyBranch(t *testing.T) {
path: "./testdata/dsse-branch3-ref-v1.intoto.jsonl",
branch: "branch3",
},
{
name: "ref main case-sensitive",
path: "./testdata/dsse-main-ref-v1.intoto.jsonl",
branch: "Main",
expected: serrors.ErrorMismatchBranch,
},

{
name: "invalid ref type",
Expand Down Expand Up @@ -868,11 +874,22 @@ func Test_VerifyTag(t *testing.T) {
path: "./testdata/dsse-invalid-ref-type-v1.intoto.jsonl",
expected: serrors.ErrorInvalidDssePayload,
},
{
name: "tag vSLSA1 case-sensitive",
path: "./testdata/dsse-vslsa1-tag.intoto.jsonl",
tag: "vSLSA1",
expected: serrors.ErrorMismatchTag,
},
{
name: "tag vslsa1",
path: "./testdata/dsse-vslsa1-tag-v1.intoto.jsonl",
tag: "vslsa1",
},
{
name: "case sensitive",
path: "./testdata/dsse-vslsa1-tag-v1.intoto.jsonl",
tag: "vslsa1",
},
}
for _, tt := range tests {
tt := tt // Re-initializing variable so it is not changed while executing the closure below
Expand Down

0 comments on commit f96d91b

Please sign in to comment.