diff --git a/.github/workflows/actionlint.yaml b/.github/workflows/actionlint.yaml new file mode 100644 index 0000000..0a4be73 --- /dev/null +++ b/.github/workflows/actionlint.yaml @@ -0,0 +1,29 @@ +# Copyright 2022 Chainguard, Inc. +# SPDX-License-Identifier: Apache-2.0 + +name: Action Lint + +on: + pull_request: + branches: [ 'main', 'release-*' ] + +jobs: + + action-lint: + name: Action lint + runs-on: ubuntu-latest + + steps: + - name: Check out code + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + + - name: Find yamls + id: get_yamls + run: | + yamls="$(find .github/workflows -name "*.y*ml" | grep -v dependabot. | xargs echo)" + echo "files=${yamls}" >> "$GITHUB_OUTPUT" + + - name: Action lint + uses: reviewdog/action-actionlint@9ccda195fd3a290c8596db7f1958c897deaa8c76 # v1.40.0 + with: + actionlint_flags: ${{ steps.get_yamls.outputs.files }} diff --git a/.github/workflows/donotsubmit.yaml b/.github/workflows/donotsubmit.yaml new file mode 100644 index 0000000..a8557d0 --- /dev/null +++ b/.github/workflows/donotsubmit.yaml @@ -0,0 +1,21 @@ +# Copyright 2022 Chainguard, Inc. +# SPDX-License-Identifier: Apache-2.0 + +name: Do Not Submit + +on: + pull_request: + branches: [ 'main', 'release-*' ] + +jobs: + + donotsubmit: + name: Do Not Submit + runs-on: ubuntu-latest + + steps: + - name: Check out code + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + + - name: Do Not Submit + uses: chainguard-dev/actions/donotsubmit@main diff --git a/.github/workflows/go-test.yaml b/.github/workflows/go-test.yaml new file mode 100644 index 0000000..228f76b --- /dev/null +++ b/.github/workflows/go-test.yaml @@ -0,0 +1,41 @@ +# Copyright 2022 Chainguard, Inc. +# SPDX-License-Identifier: Apache-2.0 + +name: Test + +on: + pull_request: + branches: [ 'main', 'release-*' ] + push: + branches: [ 'main', 'release-*' ] + +jobs: + + test: + runs-on: ubuntu-latest + steps: + - name: Check out code onto GOPATH + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + + # https://github.com/mvdan/github-actions-golang#how-do-i-set-up-caching-between-builds + - uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 # v3.3.2 + with: + # In order: + # * Module download cache + # * Build cache (Linux) + path: | + ~/go/pkg/mod + ~/.cache/go-build + key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} + restore-keys: | + ${{ runner.os }}-go- + + - name: Set up Go + uses: actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491 # v5.0.0 + with: + go-version-file: './go.mod' + check-latest: true + + - run: | + # Exclude running unit tests against third_party repos. + go test -race ./... diff --git a/.github/workflows/style.yaml b/.github/workflows/style.yaml new file mode 100644 index 0000000..788b3dd --- /dev/null +++ b/.github/workflows/style.yaml @@ -0,0 +1,110 @@ +# Copyright 2022 Chainguard, Inc. +# SPDX-License-Identifier: Apache-2.0 + +name: Code Style + +on: + pull_request: + branches: [ 'main', 'release-*' ] + push: + branches: [ 'main', 'release-*' ] + +jobs: + + gofmt: + name: check gofmt + runs-on: ubuntu-latest + steps: + - name: Check out code + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + + - name: Set up Go + uses: actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491 # v5.0.0 + with: + go-version-file: './go.mod' + check-latest: true + + - uses: chainguard-dev/actions/gofmt@main + with: + args: -s + + goimports: + name: check goimports + runs-on: ubuntu-latest + steps: + - name: Check out code + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + + - name: Set up Go + uses: actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491 # v5.0.0 + with: + go-version-file: './go.mod' + check-latest: true + + - uses: chainguard-dev/actions/goimports@main + + golangci-lint: + name: golangci-lint + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + + - name: Set up Go + uses: actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491 # v5.0.0 + with: + go-version-file: './go.mod' + check-latest: true + cache: true + + - name: golangci-lint + uses: golangci/golangci-lint-action@3a919529898de77ec3da873e3063ca4b10e7f5cc # v3.7.0 + with: + # Required: the version of golangci-lint is required and must be specified without patch version: we always use the latest patch version. + version: v1.54 + args: --timeout=5m + + lint: + name: Lint + runs-on: ubuntu-latest + + steps: + - name: Check out code + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + + - name: Set up Go + uses: actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491 # v5.0.0 + with: + go-version-file: './go.mod' + check-latest: true + + - uses: chainguard-dev/actions/trailing-space@main + if: ${{ always() }} + + - uses: chainguard-dev/actions/eof-newline@main + if: ${{ always() }} + + - uses: reviewdog/action-tflint@master + if: ${{ always() }} + with: + github_token: ${{ secrets.github_token }} + fail_on_error: true + + - uses: reviewdog/action-misspell@06d6a480724fa783c220081bbc22336a78dbbe82 # v1.15.0 + if: ${{ always() }} + with: + github_token: ${{ secrets.github_token }} + fail_on_error: true + locale: "US" + exclude: | + **/go.sum + **/third_party/** + ./*.yml + + - uses: get-woke/woke-action-reviewdog@d71fd0115146a01c3181439ce714e21a69d75e31 # v0 + if: ${{ always() }} + with: + github-token: ${{ secrets.github_token }} + reporter: github-pr-check + level: error + fail-on-error: true diff --git a/.github/workflows/terraform.yaml b/.github/workflows/terraform.yaml new file mode 100644 index 0000000..104e50b --- /dev/null +++ b/.github/workflows/terraform.yaml @@ -0,0 +1,21 @@ +name: Validate, Lint and Test + +on: + push: + branches: + - main + pull_request: + +jobs: + lint-and-validate: + name: "Terraform fmt and validate" + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + - uses: hashicorp/setup-terraform@a1502cd9e758c50496cc9ac5308c4843bcd56d36 # v3.0.0 + + - run: terraform fmt -check + + - run: | + terraform init + terraform validate diff --git a/pkg/octosts/trust_policy_test.go b/pkg/octosts/trust_policy_test.go new file mode 100644 index 0000000..33f8a2d --- /dev/null +++ b/pkg/octosts/trust_policy_test.go @@ -0,0 +1,201 @@ +package octosts + +import ( + "testing" + + "github.com/coreos/go-oidc/v3/oidc" +) + +func TestCompile(t *testing.T) { + tests := []struct { + name string + tp *TrustPolicy + wantErr bool + }{{ + name: "valid literals", + tp: &TrustPolicy{ + Issuer: "https://example.com", + Subject: "subject", + }, + wantErr: false, + }, { + name: "valid patterns", + tp: &TrustPolicy{ + IssuerPattern: "https://(example|google)\\.com", + SubjectPattern: "[0-9]{10}", + ClaimPattern: map[string]string{ + "email": ".*@example.com", + }, + }, + wantErr: false, + }, { + name: "multiple issuers", + tp: &TrustPolicy{ + Issuer: "https://example.com", + IssuerPattern: ".*", + Subject: "asdf", + }, + wantErr: true, + }, { + name: "multiple subjects", + tp: &TrustPolicy{ + Issuer: "https://example.com", + Subject: "subject", + SubjectPattern: ".*", + }, + wantErr: true, + }, { + name: "invalid issuer pattern", + tp: &TrustPolicy{ + IssuerPattern: ")(", + Subject: "asdf", + }, + wantErr: true, + }, { + name: "invalid subject pattern", + tp: &TrustPolicy{ + Issuer: "https://examples.com", + SubjectPattern: ")(", + }, + wantErr: true, + }, { + name: "invalid claim pattern", + tp: &TrustPolicy{ + Issuer: "https://example.com", + Subject: "subject", + ClaimPattern: map[string]string{ + "claim": ")()", + }, + }, + wantErr: true, + }, { + name: "missing issuer", + tp: &TrustPolicy{ + Subject: "subject", + }, + wantErr: true, + }, { + name: "missing subject", + tp: &TrustPolicy{ + Issuer: "https://example.com", + }, + wantErr: true, + }} + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if err := tt.tp.Compile(); (err != nil) != tt.wantErr { + t.Errorf("Compile() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} + +func TestCheckToken(t *testing.T) { + tests := []struct { + name string + tp *TrustPolicy + token *oidc.IDToken + wantErr bool + }{{ + name: "valid token", + tp: &TrustPolicy{ + Issuer: "https://example.com", + Subject: "subject", + }, + token: &oidc.IDToken{ + Issuer: "https://example.com", + Subject: "subject", + }, + wantErr: false, + }, { + name: "invalid issuer", + tp: &TrustPolicy{ + Issuer: "https://example.com", + Subject: "subject", + }, + token: &oidc.IDToken{ + Issuer: "https://example.org", + Subject: "subject", + }, + wantErr: true, + }, { + name: "invalid subject", + tp: &TrustPolicy{ + Issuer: "https://example.com", + Subject: "subject", + }, + token: &oidc.IDToken{ + Issuer: "https://example.com", + Subject: "asdf", + }, + wantErr: true, + }, { + name: "valid patterns", + tp: &TrustPolicy{ + IssuerPattern: "https://(example|google)\\.com", + SubjectPattern: "[0-9]{10}", + }, + token: &oidc.IDToken{ + Issuer: "https://example.com", + Subject: "1234567890", + }, + wantErr: false, + }, { + name: "invalid issuer pattern", + tp: &TrustPolicy{ + IssuerPattern: "https://(example|google)\\.com", + Subject: "blah", + }, + token: &oidc.IDToken{ + Issuer: "https://example.org", + Subject: "blah", + }, + wantErr: true, + }, { + name: "invalid subject pattern", + tp: &TrustPolicy{ + Issuer: "https://example.com", + SubjectPattern: "[0-9]{10}", + }, + token: &oidc.IDToken{ + Issuer: "https://example.com", + Subject: "blah", + }, + wantErr: true, + }, { + name: "missing custom claim", + tp: &TrustPolicy{ + Issuer: "https://example.com", + Subject: "subject", + ClaimPattern: map[string]string{ + "email": ".*@example.com", + }, + }, + token: &oidc.IDToken{ + Issuer: "https://example.com", + Subject: "subject", + // No email claim. + }, + wantErr: true, + }} + + // TODO(mattmoor): Figure out how to test custom claims with IDToken. + // - Test for extra custom claims, + // - Test for matching a custom claim, + // - Test for mismatching a custom claim, + // - Test for matching multiple custom claims, + // - Test for mismatching one of several custom claims. + // - Test for a non-string custom claim. + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if err := tt.tp.Compile(); err != nil { + t.Fatalf("Compile() = %v", err) + } + if err := tt.tp.CheckToken(tt.token); (err != nil) != tt.wantErr { + t.Errorf("CheckToken() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +}