From 7f906ea665fc7b7907fae086041e0da52ab114d3 Mon Sep 17 00:00:00 2001 From: Jakub Nowak Date: Wed, 21 Oct 2026 06:32:49 +0200 Subject: [PATCH 1/4] fix(build): build with Go 1.27.3 to distrust compromised eMudhra CAs Go 1.27.3 hard-codes distrust of the eight compromised eMudhra (emSign) intermediate CAs (CVE-2026-111943) in crypto/x509, on the Go verifier and on the macOS and Windows platform-verifier paths. Release builds resolved to go1.27.1 (setup-go installed 1.25, then GOTOOLCHAIN=auto switched to the go.mod minimum), which accepts chains through those CAs, so an on-path attacker could impersonate the CRE API, login, Vault gateway or RPC endpoints the CLI dials. - go.mod: go 1.27.1 -> 1.27.3 - release and preview workflows: setup-go reads the version from go.mod - .tool-versions: golang 1.27.3 Co-Authored-By: Claude Opus 5.5 --- .changeset/go-1-27-3-emudhra.md | 5 +++++ .github/workflows/build-and-release.yml | 6 +++--- .github/workflows/preview-build.yml | 6 +++--- .tool-versions | 2 +- go.mod | 2 +- 5 files changed, 13 insertions(+), 8 deletions(-) create mode 100644 .changeset/go-1-27-3-emudhra.md diff --git a/.changeset/go-1-27-3-emudhra.md b/.changeset/go-1-27-3-emudhra.md new file mode 100644 index 00000000..e3e8b7d5 --- /dev/null +++ b/.changeset/go-1-27-3-emudhra.md @@ -0,0 +1,5 @@ +--- +"cre-cli": patch +--- + +Build the CLI with Go 1.27.3, which distrusts the eight compromised eMudhra (emSign) intermediate CAs (CVE-2026-111943). Earlier builds accept TLS certificates issued from those keys, so an attacker on the network path could impersonate the CRE API, login, Vault gateway or RPC endpoints the CLI connects to. Release builds now take the Go version from `go.mod`. diff --git a/.github/workflows/build-and-release.yml b/.github/workflows/build-and-release.yml index 29f2aea4..1c2322fd 100644 --- a/.github/workflows/build-and-release.yml +++ b/.github/workflows/build-and-release.yml @@ -24,7 +24,7 @@ jobs: - name: Set up Go uses: actions/setup-go@3041bf56c941b39c61721a86cd11f3bb1338122a # actions/setup-go@v5.2.0 with: - go-version: "1.25" + go-version-file: go.mod - name: Setup GitHub Token id: setup-github-token @@ -149,7 +149,7 @@ jobs: - name: Set up Go uses: actions/setup-go@3041bf56c941b39c61721a86cd11f3bb1338122a # actions/setup-go@v5.2.0 with: - go-version: "1.25" + go-version-file: go.mod - name: Setup GitHub Token id: setup-github-token @@ -250,7 +250,7 @@ jobs: - name: Set up Go uses: actions/setup-go@3041bf56c941b39c61721a86cd11f3bb1338122a # actions/stup-go@v5.2.0 with: - go-version: "1.25" + go-version-file: go.mod - name: Setup GitHub Token id: setup-github-token diff --git a/.github/workflows/preview-build.yml b/.github/workflows/preview-build.yml index 13b119b6..dee1a094 100644 --- a/.github/workflows/preview-build.yml +++ b/.github/workflows/preview-build.yml @@ -21,7 +21,7 @@ jobs: - name: Set up Go uses: actions/setup-go@3041bf56c941b39c61721a86cd11f3bb1338122a # actions/setup-go@v5.2.0 with: - go-version: "1.25" + go-version-file: go.mod - name: Install Dependencies run: | @@ -72,7 +72,7 @@ jobs: - name: Set up Go uses: actions/setup-go@3041bf56c941b39c61721a86cd11f3bb1338122a # actions/setup-go@v5.2.0 with: - go-version: "1.25" + go-version-file: go.mod - name: Build the Go Binary env: @@ -107,7 +107,7 @@ jobs: - name: Set up Go uses: actions/setup-go@3041bf56c941b39c61721a86cd11f3bb1338122a # stup-go@v5.2.0 with: - go-version: "1.25" + go-version-file: go.mod - name: Install Dependencies shell: pwsh diff --git a/.tool-versions b/.tool-versions index 71d117e2..9eda3ad4 100644 --- a/.tool-versions +++ b/.tool-versions @@ -1,4 +1,4 @@ -golang 1.26.2 +golang 1.27.3 golangci-lint 2.11.2 goreleaser 2.0.1 python 3.10.5 diff --git a/go.mod b/go.mod index e62a4fa0..6866e5be 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/smartcontractkit/cre-cli -go 1.27.1 +go 1.27.3 require ( github.com/BurntSushi/toml v1.6.0 From f4ae9d4f85bb526b7360a6771c27080f96aa0efc Mon Sep 17 00:00:00 2001 From: Jakub Nowak Date: Wed, 21 Oct 2026 06:51:20 +0200 Subject: [PATCH 2/4] ci: bump golangci-lint to v2.15.0 for Go 1.27.3 golangci-lint v2.13.1 cannot read Go 1.27.3 export data ("export data version 5 is greater than maximum supported version 4"), so ci-lint failed on typecheck. v2.15.0 lints Go 1.27.3 cleanly (same pairing as smartcontractkit/chainlink-common#2481). Co-Authored-By: Claude Opus 5.5 --- .github/workflows/pull-request-main.yml | 2 +- .tool-versions | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/pull-request-main.yml b/.github/workflows/pull-request-main.yml index ad9a6dbd..e17bc029 100644 --- a/.github/workflows/pull-request-main.yml +++ b/.github/workflows/pull-request-main.yml @@ -25,7 +25,7 @@ jobs: uses: smartcontractkit/.github/actions/ci-lint-go@31f7a923a25f7672641b5099cbb85ce4a984fc05 # ci-lint-go@3.1.0 with: only-new-issues: false - golangci-lint-version: v2.13.1 + golangci-lint-version: v2.15.0 aws-region: ${{ secrets.AWS_REGION }} use-gati: "true" aws-role-arn-gati: ${{ secrets.AWS_OIDC_DEV_PLATFORM_READ_REPOS_EXTERNAL_TOKEN_ISSUER_ROLE_ARN }} diff --git a/.tool-versions b/.tool-versions index 9eda3ad4..db771c08 100644 --- a/.tool-versions +++ b/.tool-versions @@ -1,5 +1,5 @@ golang 1.27.3 -golangci-lint 2.11.2 +golangci-lint 2.15.0 goreleaser 2.0.1 python 3.10.5 nodejs 20.13.1 From 2429eadfac156090f6e6c17b4e2a97cbe6a95553 Mon Sep 17 00:00:00 2001 From: Jakub Nowak Date: Wed, 21 Oct 2026 09:08:31 +0200 Subject: [PATCH 3/4] fix(ui): explicitly discard download goroutine error after shutdown --- internal/ui/progress.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/internal/ui/progress.go b/internal/ui/progress.go index bc18fdd6..f1bdcfc1 100644 --- a/internal/ui/progress.go +++ b/internal/ui/progress.go @@ -130,14 +130,15 @@ func DownloadWithProgress(resp io.ReadCloser, contentLength int64, destFile *os. // Bubble Tea itself failed. Close the response body to unblock the // download goroutine, then drain the channel so it can exit. resp.Close() - <-errCh + _ = <-errCh return err } // Close the response body so the download goroutine's io.Copy finishes. // For a completed download this is a no-op; for Ctrl+C it unblocks io.Copy. + // The copy error was already sent to the model as progressErrMsg. resp.Close() - <-errCh + _ = <-errCh // If the user pressed Ctrl+C, the model is not done — treat as cancellation if fm, ok := finalModel.(downloadModel); ok && !fm.done { From 0422cc303f0e8e6ff4846c5a5515d09925e5fd6e Mon Sep 17 00:00:00 2001 From: Jakub Nowak Date: Wed, 21 Oct 2026 09:35:33 +0200 Subject: [PATCH 4/4] fix(ui): signal download goroutine exit with a done channel v2.15.0 lint rejects both forms of discarding the goroutine's error: errcheck flags a bare <-errCh and staticcheck S1005 flags _ = <-errCh. The error is already delivered to the model via p.Send, so the channel only needs to signal that the goroutine has exited; use a closed chan struct{} for that. Co-Authored-By: Claude Opus 5.5 --- internal/ui/progress.go | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/internal/ui/progress.go b/internal/ui/progress.go index f1bdcfc1..780d0208 100644 --- a/internal/ui/progress.go +++ b/internal/ui/progress.go @@ -112,33 +112,33 @@ func DownloadWithProgress(resp io.ReadCloser, contentLength int64, destFile *os. }, } - // Start download in goroutine - errCh := make(chan error, 1) + // Start download in goroutine. The result is reported to the model via + // p.Send; copyDone only signals that the goroutine has exited. + copyDone := make(chan struct{}) go func() { + defer close(copyDone) _, err := io.Copy(pw, resp) if err != nil { p.Send(progressErrMsg{err: err}) } else { p.Send(progressDoneMsg{}) } - errCh <- err }() // Run the UI — blocks until done, error, or Ctrl+C finalModel, err := p.Run() if err != nil { // Bubble Tea itself failed. Close the response body to unblock the - // download goroutine, then drain the channel so it can exit. + // download goroutine, then wait for it to exit. resp.Close() - _ = <-errCh + <-copyDone return err } // Close the response body so the download goroutine's io.Copy finishes. // For a completed download this is a no-op; for Ctrl+C it unblocks io.Copy. - // The copy error was already sent to the model as progressErrMsg. resp.Close() - _ = <-errCh + <-copyDone // If the user pressed Ctrl+C, the model is not done — treat as cancellation if fm, ok := finalModel.(downloadModel); ok && !fm.done {