diff --git a/.github/workflows/agent-image.yml b/.github/workflows/agent-image.yml index 3491ebe..d27805c 100644 --- a/.github/workflows/agent-image.yml +++ b/.github/workflows/agent-image.yml @@ -69,7 +69,8 @@ jobs: contents: write pull-requests: write steps: - - uses: actions/checkout@v7.0.1 + # The job pushes the branch of its pull request with these credentials. + - uses: actions/checkout@v7.0.1 # zizmor: ignore[artipacked] - name: Open a pull request pinning the new digests env: GH_TOKEN: ${{ github.token }} diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0697d39..6459a2a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -31,6 +31,8 @@ jobs: - run: go vet ./... # The evals call real models and never run here, so vetting is what keeps them compiling. - run: go vet -tags evals ./... + - run: go tool staticcheck ./... + - run: go tool govulncheck ./... - run: go tool gotestsum --format github-actions -- -race -count=1 ./... # release.yml only runs on tags, so a stale lockfile entry would otherwise break the next release. @@ -48,6 +50,17 @@ jobs: env: GH_TOKEN: ${{ github.token }} + zizmor: + runs-on: ubuntu-latest + timeout-minutes: 5 + steps: + - uses: actions/checkout@v7.0.1 + with: + persist-credentials: false + - run: pipx run zizmor==1.29.0 --format github . + env: + GH_TOKEN: ${{ github.token }} + fuzz: runs-on: ubuntu-latest timeout-minutes: 10 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 3a10b42..0219508 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -22,6 +22,8 @@ jobs: - uses: actions/setup-go@v7.0.0 with: go-version-file: go.mod + # A Go cache restored from another workflow could end up in the attested binaries. + cache: false - name: Build env: CGO_ENABLED: "0" diff --git a/.github/zizmor.yml b/.github/zizmor.yml new file mode 100644 index 0000000..3b44c7c --- /dev/null +++ b/.github/zizmor.yml @@ -0,0 +1,7 @@ +rules: + # actions.lock pins these workflows' actions to commits, which zizmor does not read. + unpinned-uses: + ignore: + - agent-image.yml + - ci.yml + - release.yml diff --git a/docs/development.md b/docs/development.md index 6071d60..fcf4454 100644 --- a/docs/development.md +++ b/docs/development.md @@ -23,7 +23,7 @@ go test ./internal/report -run '^$' -fuzz '^FuzzText$' -fuzztime 60s The provider protocol filter is fuzzed in harness, with its model proxy. -On each push, CI checks formatting and `go mod tidy`, then runs `go vet`, the tests, the three fuzz targets and the Docker isolation tests of both agent images. Make both isolation jobs required release checks. +On each push, CI checks formatting and `go mod tidy`, then runs `go vet`, `staticcheck`, `govulncheck`, `zizmor` on the workflows and the action, the tests, the three fuzz targets and the Docker isolation tests of both agent images. Make both isolation jobs required release checks. ## Isolation tests diff --git a/go.mod b/go.mod index 77e3128..2da55bc 100644 --- a/go.mod +++ b/go.mod @@ -14,6 +14,7 @@ require ( ) require ( + github.com/BurntSushi/toml v1.4.1-0.20240526193622-a339e1f7089c // indirect github.com/bahlo/generic-list-go v0.2.0 // indirect github.com/bitfield/gotestdox v0.2.3 // indirect github.com/buger/jsonparser v1.6.1 // indirect @@ -41,12 +42,20 @@ require ( go.opentelemetry.io/otel/log v1.47.0 // indirect go.opentelemetry.io/otel/metric v1.47.0 // indirect go.opentelemetry.io/otel/trace v1.47.0 // indirect + golang.org/x/exp/typeparams v0.0.0-20231108232855-2478ac86f678 // indirect golang.org/x/mod v0.42.0 // indirect golang.org/x/sys v0.49.0 // indirect + golang.org/x/telemetry v0.0.0-20261010193011-f71dc92da45e // indirect golang.org/x/term v0.47.0 // indirect golang.org/x/text v0.43.0 // indirect golang.org/x/tools v0.52.0 // indirect + golang.org/x/vuln v1.9.0 // indirect gotest.tools/gotestsum v1.13.0 // indirect + honnef.co/go/tools v0.8.2 // indirect ) -tool gotest.tools/gotestsum +tool ( + golang.org/x/vuln/cmd/govulncheck + gotest.tools/gotestsum + honnef.co/go/tools/cmd/staticcheck +) diff --git a/go.sum b/go.sum index cff33b9..5b4807c 100644 --- a/go.sum +++ b/go.sum @@ -8,6 +8,8 @@ github.com/Azure/azure-sdk-for-go/sdk/azcore v1.23.2 h1:utpeoEeZjd+A8J41zvoLsOOr github.com/Azure/azure-sdk-for-go/sdk/azcore v1.23.2/go.mod h1:iptorS+VYKFL2N6PnebpS91dubG35eAOEERnT4PJbQU= github.com/Azure/azure-sdk-for-go/sdk/internal v1.12.0 h1:fhqpLE3UEXi9lPaBRpQ6XuRW0nU7hgg4zlmZZa+a9q4= github.com/Azure/azure-sdk-for-go/sdk/internal v1.12.0/go.mod h1:7dCRMLwisfRH3dBupKeNCioWYUZ4SS09Z14H+7i8ZoY= +github.com/BurntSushi/toml v1.4.1-0.20240526193622-a339e1f7089c h1:pxW6RcqyfI9/kWtOwnv/G+AzdKuy2ZrqINhenH4HyNs= +github.com/BurntSushi/toml v1.4.1-0.20240526193622-a339e1f7089c/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= github.com/alpha-omega-security/harness v0.1.19 h1:X/EI0++ki34CFCROOCqfmiUSzodumqlJ0LLJks7N2Ws= github.com/alpha-omega-security/harness v0.1.19/go.mod h1:4JI0CxVRwiWQ9zAtSTnM5p8l0A79bwREtps9BA9oLXY= github.com/anthropics/anthropic-sdk-go v1.84.0 h1:mnAhggc5H5A98JYmhQELT+j4J89PYBFKnnSvUTNwNd8= @@ -75,10 +77,14 @@ github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= +github.com/google/go-cmdtest v0.4.1-0.20220921163831-55ab3332a786 h1:rcv+Ippz6RAtvaGgKxc+8FQIpxHgsF+HBzPyYL2cyVU= +github.com/google/go-cmdtest v0.4.1-0.20220921163831-55ab3332a786/go.mod h1:apVn/GCasLZUVpAJ6oWAuyP7Ne7CEsQbTnc0plM3m+o= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/jsonschema-go v0.4.2 h1:tmrUohrwoLZZS/P3x7ex0WAVknEkBZM46iALbcqoRA8= github.com/google/jsonschema-go v0.4.2/go.mod h1:r5quNTdLOYEz95Ru18zA0ydNbBuYoo9tgaYcxEYhJVE= +github.com/google/renameio v0.1.0 h1:GOZbcHa3HfsPKPlmyPyN2KEohoMXOhdMbHrvbpl2QaA= +github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/s2a-go v0.1.7 h1:60BLSyTrOV4/haCDW4zb1guZItoSq8foHCXrAnjBo/o= github.com/google/s2a-go v0.1.7/go.mod h1:50CgR4k1jNlWBu4UfS4AcfhVe1r6pdZPygJ3R8F0Qdw= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4= @@ -146,6 +152,8 @@ go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= golang.org/x/crypto v0.57.0 h1:3ZVCjf8Ggz7zneR/EHRVx68Ctf+2pmIMP2UFhh9cC6M= golang.org/x/crypto v0.57.0/go.mod h1:Fdz0i5U6CoizGwLda9DttjSk6qlZo25zYNtR+ycvuZA= +golang.org/x/exp/typeparams v0.0.0-20231108232855-2478ac86f678 h1:1P7xPZEwZMoBoz0Yze5Nx2/4pxj6nw9ZqHWXqP0iRgQ= +golang.org/x/exp/typeparams v0.0.0-20231108232855-2478ac86f678/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk= golang.org/x/mod v0.42.0 h1:ICeL3t2L30ljSPHHxDnR4fqVcBt/zR2zJAb9juavmQg= golang.org/x/mod v0.42.0/go.mod h1:u5fzIrRInkL8MaO1cbRkUHT64v303YIT5KEqpNr7jRs= golang.org/x/net v0.61.0 h1:RR+6j/BTBCrggOLugxa+66R8FvoN6MHlzDlZIbN9YFw= @@ -156,6 +164,8 @@ golang.org/x/sync v0.24.0 h1:iqiDrGoDgyuvAKrhLQthUEzU/R378N78gXbBjxed7oA= golang.org/x/sync v0.24.0/go.mod h1:sUUOizhqBxiL6pEWpqNLUiaJn1ShEbZ6BBqskPbjZm0= golang.org/x/sys v0.49.0 h1:XbzkgYJHdqh/8m2Uu0W/dQv8nktxx4BFHp1M0gROTXA= golang.org/x/sys v0.49.0/go.mod h1:hNLxWAXmnKAxqDtdwIYC4bM9oQPEecfsnNMuSxOs3og= +golang.org/x/telemetry v0.0.0-20261010193011-f71dc92da45e h1:P6NGsugec4oASJ+yKMNgTndeo0h2CbixZLvQIe9h5R4= +golang.org/x/telemetry v0.0.0-20261010193011-f71dc92da45e/go.mod h1:GNu+nb+E5axfGmP+Uu3ZVQu5lE/Xk42MjZOHWUD94/o= golang.org/x/term v0.47.0 h1:cpyOwQkr2Nzxe2p6KnrTG7Ei2wtzjsuSjhpXXepZ/BM= golang.org/x/term v0.47.0/go.mod h1:efAfAuG6RKABvg1ArGebRkvX6LXul14SM67o2e4mUCY= golang.org/x/text v0.43.0 h1:1QivrlhwAsnMOcqgOdl9my7s9OyZl8O5/UGUTJVXoWc= @@ -164,6 +174,12 @@ golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.52.0 h1:ryxg4U63oaUYk7uOLTF37n43f4qgtGvqV4Ioit1BaEs= golang.org/x/tools v0.52.0/go.mod h1:ItcP5UNaVGG+Yc4eIYmaFLNJ4soEopkx5Y2d4lcEurs= +golang.org/x/tools/go/expect v0.1.1-deprecated h1:jpBZDwmgPhXsKZC6WhL20P4b/wmnpsEAGHaNy0n/rJM= +golang.org/x/tools/go/expect v0.1.1-deprecated/go.mod h1:eihoPOH+FgIqa3FpoTwguz/bVUSGBlGQU67vpBeOrBY= +golang.org/x/tools/go/packages/packagestest v0.1.1-deprecated h1:1h2MnaIAIXISqTFKdENegdpAgUXz6NrPEsbIeWaBRvM= +golang.org/x/tools/go/packages/packagestest v0.1.1-deprecated/go.mod h1:RVAQXBGNv1ib0J382/DPCRS/BPnsGebyM1Gj5VSDpG8= +golang.org/x/vuln v1.9.0 h1:12VaqnI9kJfqwRoPptxoqsUhQtdfrSbdksT7X/KY/sc= +golang.org/x/vuln v1.9.0/go.mod h1:E/Frwm0xp49fTyBW+6CQHdkvobknbwUM49yEpMGUauw= google.golang.org/api v0.189.0 h1:equMo30LypAkdkLMBqfeIqtyAnlyig1JSZArl4XPwdI= google.golang.org/api v0.189.0/go.mod h1:FLWGJKb0hb+pU2j+rJqwbnsF+ym+fQs73rbJ+KAUgy8= google.golang.org/genproto/googleapis/rpc v0.0.0-20240722135656-d784300faade h1:oCRSWfwGXQsqlVdErcyTt4A93Y8fo0/9D4b1gnI++qo= @@ -178,3 +194,5 @@ gotest.tools/gotestsum v1.13.0 h1:+Lh454O9mu9AMG1APV4o0y7oDYKyik/3kBOiCqiEpRo= gotest.tools/gotestsum v1.13.0/go.mod h1:7f0NS5hFb0dWr4NtcsAsF0y1kzjEFfAil0HiBQJE03Q= gotest.tools/v3 v3.5.2 h1:7koQfIKdy+I8UTetycgUqXWSDwpgv193Ka+qRsmBY8Q= gotest.tools/v3 v3.5.2/go.mod h1:LtdLGcnqToBH83WByAAi/wiwSFCArdFIUV/xxN4pcjA= +honnef.co/go/tools v0.8.2 h1:+jPbCzuYxYrcoOvCecYUFDj+XJQRmzIhJmlbpps21Zk= +honnef.co/go/tools v0.8.2/go.mod h1:xG1Q4n+mg3bKZrO6RWFA9jFlkqplflmDRHGeuexiLDs= diff --git a/internal/board/board_test.go b/internal/board/board_test.go index 07c538a..20fc160 100644 --- a/internal/board/board_test.go +++ b/internal/board/board_test.go @@ -390,7 +390,7 @@ func TestPutCreatesAPrivateIssue(t *testing.T) { require.NotNil(t, issue, "the item is a draft") require.Len(t, p.Issues, 1, "a second put created another issue") require.Equal(t, "acme/private-issues", issue.Repo) - require.Equal(t, "Overflow found by @​someone", issue.Title, "a mention in the title would notify") + require.Equal(t, "Overflow found by @\u200bsomeone", issue.Title, "a mention in the title would notify") require.Equal(t, "https://x/GHSA-new\n\nthe comment\n\n", issue.Body) require.Equal(t, "open", issue.State) require.Empty(t, p.Titles, "a draft was created next to the issue") diff --git a/internal/github/client_test.go b/internal/github/client_test.go index fc75320..8f76be0 100644 --- a/internal/github/client_test.go +++ b/internal/github/client_test.go @@ -264,6 +264,7 @@ func TestProjectErrors(t *testing.T) { }) _, err := c.CreateDraft(context.Background(), "php", 16, "t", "b") + require.Error(t, err) require.Error(t, c.UpdateDraft(context.Background(), "DI_6", "b")) _, err = c.GetRepository(context.Background(), "acme/private-issues") require.Error(t, err)