From 7c83bbd725b3f142ca6050994c2b9dde42fedd18 Mon Sep 17 00:00:00 2001 From: Vu Anh Phung Date: Fri, 23 Oct 2026 19:40:14 +0000 Subject: [PATCH 1/5] fix(security): require Go 1.27.3 Require the patched crypto/x509 toolchain for CVE-2026-111943 and align CI and bot environments. Upgrade the linter and address compatibility findings. Signed-off-by: Vu Anh Phung --- .bot/prompts/engineer-followup/system.md | 2 +- .bot/prompts/engineer-mention/system.md | 2 +- .bot/prompts/engineer/system.md | 2 +- .github/workflows/engineer-bot-followup.yml | 2 +- .github/workflows/engineer-bot-mention.yml | 2 +- .github/workflows/engineer-bot.yml | 2 +- .github/workflows/go.yml | 21 ++++++----------- .github/workflows/nightly-e2e.yml | 4 ++-- .github/workflows/securityScan.yml | 26 +++++++-------------- CHANGELOG.md | 4 ++++ Makefile | 2 +- README.md | 2 ++ go.mod | 2 +- internal/client/client.go | 1 - internal/errors/category_test.go | 4 ++-- osv-scanner.toml | 8 ++----- 16 files changed, 35 insertions(+), 51 deletions(-) diff --git a/.bot/prompts/engineer-followup/system.md b/.bot/prompts/engineer-followup/system.md index 352ddf01..f312f633 100644 --- a/.bot/prompts/engineer-followup/system.md +++ b/.bot/prompts/engineer-followup/system.md @@ -18,7 +18,7 @@ Your job: 4. End with a short summary of what changed. Repo facts you need: - - Go 1.25 `database/sql` driver; `go build ./...` has warmed the module cache on + - Go 1.27.3 `database/sql` driver; `go build ./...` has warmed the module cache on the runner. This follow-up job wires **NO live-warehouse connection env**, so only the pure-Go **`make test`** unit suite (CGO_ENABLED=0) runs here — do NOT run or add the live e2e tests in `driver_e2e_test.go` (they need warehouse diff --git a/.bot/prompts/engineer-mention/system.md b/.bot/prompts/engineer-mention/system.md index b6326448..8dcc4d04 100644 --- a/.bot/prompts/engineer-mention/system.md +++ b/.bot/prompts/engineer-mention/system.md @@ -18,7 +18,7 @@ the relevant source before editing. Never weaken tests or alter fixtures to make a check pass. Keep public API and dependency changes out of scope unless the maintainer explicitly requests them. -The runner provides Go 1.25 and a warmed module/tool cache. Verify Go edits with +The runner provides Go 1.27.3 and a warmed module/tool cache. Verify Go edits with the affected `go test -run ./` tests, then `make test`, `make lint`, and the read-only format check `gofmt -l -s .`. This job runs the pure-Go driver with CGO_ENABLED=0 and GOPROXY=off. It has no live-warehouse diff --git a/.bot/prompts/engineer/system.md b/.bot/prompts/engineer/system.md index 042c6f30..9d7703a6 100644 --- a/.bot/prompts/engineer/system.md +++ b/.bot/prompts/engineer/system.md @@ -11,7 +11,7 @@ prompt covers the repo-specific facts you need to follow it. == THE REPO == A Go `database/sql` driver (module `github.com/databricks/databricks-sql-go`, Go -1.25). Source is the root package `dbsql` (`connection.go`, `connector.go`, +1.27.3). Source is the root package `dbsql` (`connection.go`, `connector.go`, `driver.go`, `statement.go`, `result.go`, `parameters.go`, …) plus subpackages `auth/`, `rows/`, `errors/`, `logger/`, `driverctx/`, `telemetry/`, and `internal/` (client, Thrift protocol, fetcher, etc.). Public API stability diff --git a/.github/workflows/engineer-bot-followup.yml b/.github/workflows/engineer-bot-followup.yml index 984b37cf..8dc8c33e 100644 --- a/.github/workflows/engineer-bot-followup.yml +++ b/.github/workflows/engineer-bot-followup.yml @@ -100,7 +100,7 @@ jobs: - name: Set up Go Toolchain uses: actions/setup-go@924ae3a1cded613372ab5595356fb5720e22ba16 # v6.5.0 with: - go-version: '1.25.x' + go-version: '1.27.3' cache: false - name: Build + warm caches (pure-Go; while JFrog creds are present) diff --git a/.github/workflows/engineer-bot-mention.yml b/.github/workflows/engineer-bot-mention.yml index e8af5072..1a57f50d 100644 --- a/.github/workflows/engineer-bot-mention.yml +++ b/.github/workflows/engineer-bot-mention.yml @@ -103,7 +103,7 @@ jobs: - name: Set up Go Toolchain uses: actions/setup-go@924ae3a1cded613372ab5595356fb5720e22ba16 # v6.5.0 with: - go-version: '1.25.x' + go-version: '1.27.3' cache: false - name: Build and warm Go caches diff --git a/.github/workflows/engineer-bot.yml b/.github/workflows/engineer-bot.yml index 76b8f038..f9bdb3e1 100644 --- a/.github/workflows/engineer-bot.yml +++ b/.github/workflows/engineer-bot.yml @@ -75,7 +75,7 @@ jobs: - name: Set up Go Toolchain uses: actions/setup-go@924ae3a1cded613372ab5595356fb5720e22ba16 # v6.5.0 with: - go-version: '1.25.x' + go-version: '1.27.3' cache: false - name: Build driver + warm caches (pure-Go; while JFrog creds are present) diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index e60f5e76..b57aad6a 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -33,28 +33,21 @@ jobs: - name: Set up Go Toolchain uses: actions/setup-go@924ae3a1cded613372ab5595356fb5720e22ba16 # v6.5.0 with: - go-version: '1.25.x' + go-version: '1.27.3' cache: false - name: Lint uses: golangci/golangci-lint-action@db582008a42febd596419635a5abc9d9815daa9c # v9.2.1 with: - # v2.x is built with go 1.25+, which is required so the - # linter can read Go 1.25 stdlib export data. The v1 family - # was built with go ≤1.23 and produces "could not import - # sync/atomic" typecheck errors against our `go 1.25.0` - # directive. - version: 'v2.12' + # Build the linter with the same Go toolchain as the driver. + version: 'v2.15.0' + install-mode: goinstall build-and-test: name: Test and Build strategy: - # Matches Go's release support window. Per go.dev/doc/devel/release, - # only the latest two major releases receive security patches. - # As of 2026-05 that's 1.25 (Active LTS) and 1.26. The protected - # runners pin GOTOOLCHAIN=local so we can't include 1.24 — Go would - # refuse to satisfy the `go 1.25.0` directive without auto-downloading. + # Match the minimum patched toolchain declared in go.mod. matrix: - go-version: ['1.25.x', '1.26.x'] + go-version: ['1.27.3'] runs-on: group: databricks-protected-runner-group labels: linux-ubuntu-latest @@ -171,7 +164,7 @@ jobs: - name: Set up Go Toolchain uses: actions/setup-go@924ae3a1cded613372ab5595356fb5720e22ba16 # v6.5.0 with: - go-version: '1.25.x' + go-version: '1.27.3' cache: false # Install the exact toolchain the kernel .a is built with. rust-toolchain.toml diff --git a/.github/workflows/nightly-e2e.yml b/.github/workflows/nightly-e2e.yml index 569d9e86..096a0240 100644 --- a/.github/workflows/nightly-e2e.yml +++ b/.github/workflows/nightly-e2e.yml @@ -49,7 +49,7 @@ jobs: - name: Set up Go Toolchain uses: actions/setup-go@924ae3a1cded613372ab5595356fb5720e22ba16 # v6.5.0 with: - go-version: '1.25.x' + go-version: '1.27.3' cache: false - name: Get dependencies @@ -141,7 +141,7 @@ jobs: - name: Set up Go Toolchain uses: actions/setup-go@924ae3a1cded613372ab5595356fb5720e22ba16 # v6.5.0 with: - go-version: '1.25.x' + go-version: '1.27.3' cache: false # Keep in lockstep with rust-toolchain.toml's channel (it governs the archive diff --git a/.github/workflows/securityScan.yml b/.github/workflows/securityScan.yml index 8aca3af7..a4ff217a 100644 --- a/.github/workflows/securityScan.yml +++ b/.github/workflows/securityScan.yml @@ -56,13 +56,8 @@ jobs: - name: Set up Go Toolchain uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 with: - # Must be >= the go.mod directive (1.25.0). A floating '1.25.x' - # resolves to the runner's cached latest 1.25 patch, which always - # satisfies the 1.25.0 floor. osv-scanner shells out to - # govulncheck, which loads the module with this toolchain; keeping - # the directive at the honest 1.25.0 floor (not an inflated patch) - # means '1.25.x' never falls below it. - go-version: '1.25.x' + # Match the minimum patched toolchain declared in go.mod. + go-version: '1.27.3' cache: false - name: Install osv-scanner @@ -151,14 +146,10 @@ jobs: # - Third-party UNKNOWN -> BLOCKING (fail closed). A scoreless # GHSA-only or MAL-* malware advisory on a dep we ship must not # slip the gate. - # - stdlib UNKNOWN -> report-only (NON-blocking). These are - # Go stdlib advisories flagged against the go.mod directive - # floor (e.g. `go 1.25.0`); they are delivered to consumers via - # GOTOOLCHAIN patch auto-download and are not a real exposure of - # the module itself. Inflating the directive to the latest patch - # purely to silence them would tighten the customer floor for no - # security gain, so we surface them (visible in the report) but - # do not block. Each finding carries `is_stdlib` for this split. + # - stdlib UNKNOWN -> report-only. Findings use the go.mod floor; + # actual exposure depends on the consumer's build toolchain. + # Consumers must rebuild with patched Go; GOTOOLCHAIN does not + # update an otherwise sufficient toolchain automatically. # # NOTE ON jq NUMBER PARSING: use `try (x|tonumber) catch null`, # NOT `x|tonumber?`. On a non-numeric string, `tonumber?` yields @@ -196,8 +187,7 @@ jobs: TOTAL_FINDINGS=$(echo "$ALL_FINDINGS" | jq 'length') # Scoreless (UNKNOWN) findings, split by stdlib vs third-party. - # stdlib UNKNOWNs are report-only (delivered via GOTOOLCHAIN, not a - # real module exposure); third-party UNKNOWNs are blocking. + # stdlib UNKNOWNs are report-only; third-party UNKNOWNs are blocking. UNKNOWN_COUNT=$(echo "$ALL_FINDINGS" | jq '[.[] | select(.severity == "UNKNOWN")] | length') UNKNOWN_STDLIB_COUNT=$(echo "$ALL_FINDINGS" | jq '[.[] | select(.severity == "UNKNOWN" and .is_stdlib)] | length') @@ -239,7 +229,7 @@ jobs: echo "" echo "- Total findings (any severity): \`$TOTAL_FINDINGS\`" echo "- Blocking findings (CVSS >= 7, or unscored non-stdlib; PR-blocking): \`$HIGH_COUNT\`" - echo "- Unscored/UNKNOWN findings: \`$UNKNOWN_COUNT\` total (of which \`$UNKNOWN_STDLIB_COUNT\` are stdlib — report-only, delivered via GOTOOLCHAIN)" + echo "- Unscored/UNKNOWN findings: \`$UNKNOWN_COUNT\` total (of which \`$UNKNOWN_STDLIB_COUNT\` are stdlib — report-only; rebuild with patched Go)" if [ "$TOTAL_FINDINGS" -gt 0 ]; then echo "" echo "All findings (sorted by severity desc):" diff --git a/CHANGELOG.md b/CHANGELOG.md index dbae70fa..61bd6c0d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Release History +## Unreleased + +- Require Go 1.27.3 or newer to include the emSign certificate distrust fix (CVE-2026-111943). + ## v1.17.0 (2026-10-20) - Upgrade the SQL kernel bindings to v1.2.0. - Add opt-in GEOMETRY / GEOGRAPHY support on the kernel backend with `WithEnableGeoSpatialSupport` or `enableGeoSpatialSupport=true`. Enabled mode returns EWKB bytes with per-row SRIDs and geo column metadata; the default remains WKT / EWKT strings. Arrow batches retain the `{srid,wkb}` struct in enabled mode. diff --git a/Makefile b/Makefile index 9b7327b9..07fbaa33 100644 --- a/Makefile +++ b/Makefile @@ -35,7 +35,7 @@ help: ## Show this help. all: gen fmt lint test coverage ## format and test everything bin/golangci-lint: go.mod go.sum - GOBIN=$(CURDIR)/bin go install github.com/golangci/golangci-lint/v2/cmd/golangci-lint@v2.12.2 + GOBIN=$(CURDIR)/bin go install github.com/golangci/golangci-lint/v2/cmd/golangci-lint@v2.15.0 bin/gotestsum: go.mod go.sum @mkdir -p bin/ diff --git a/README.md b/README.md index d59054af..3fd292f8 100644 --- a/README.md +++ b/README.md @@ -97,6 +97,8 @@ parameter, whether it applies to **Both**, **Thrift-only**, or **SEA-only**. ## Building +Requires Go 1.27.3 or newer. + The two backends differ **at build time**, not just at connect. ### Thrift (default) — pure Go, no extra step diff --git a/go.mod b/go.mod index 78938e19..b59668be 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/databricks/databricks-sql-go -go 1.25.0 +go 1.27.3 require ( github.com/apache/arrow/go/v12 v12.0.1 diff --git a/internal/client/client.go b/internal/client/client.go index fec632cd..351d1f73 100644 --- a/internal/client/client.go +++ b/internal/client/client.go @@ -603,7 +603,6 @@ func PooledTransport(cfg *config.Config) *http.Transport { DialContext: (&net.Dialer{ Timeout: 30 * time.Second, KeepAlive: 30 * time.Second, - DualStack: true, }).DialContext, TLSClientConfig: tlsConfig, ForceAttemptHTTP2: true, diff --git a/internal/errors/category_test.go b/internal/errors/category_test.go index e2e54aba..2a1fc319 100644 --- a/internal/errors/category_test.go +++ b/internal/errors/category_test.go @@ -62,7 +62,7 @@ func TestCategoryFromError(t *testing.T) { t.Run("finds the tag through a fmt.Errorf %w wrapper", func(t *testing.T) { inner := NewRequestError(context.TODO(), "inner", errors.New("cause")). WithCategory(CategoryResultSet) - wrapped := fmt.Errorf("outer: %w", inner) + wrapped := fmt.Errorf("outer: %w", *inner) assert.Equal(t, CategoryResultSet, CategoryFromError(wrapped)) }) @@ -72,7 +72,7 @@ func TestCategoryFromError(t *testing.T) { joined := stderrors.Join(errors.New("sibling"), inner) assert.Equal(t, CategoryResultSet, CategoryFromError(joined)) - multiWrap := fmt.Errorf("a: %w, b: %w", errors.New("sibling"), inner) + multiWrap := fmt.Errorf("a: %w, b: %w", errors.New("sibling"), *inner) assert.Equal(t, CategoryResultSet, CategoryFromError(multiWrap)) }) } diff --git a/osv-scanner.toml b/osv-scanner.toml index 92401615..65e06c2a 100644 --- a/osv-scanner.toml +++ b/osv-scanner.toml @@ -42,9 +42,7 @@ reason = "golang.org/x/crypto/openpgp not imported by this driver (verified via id = "GO-2026-6354" # CVE-2026-78662 is a denial of service in golang.org/x/crypto/ssh. # The driver does not import ssh: `go list -buildvcs=false -deps -test ./...` -# resolves only golang.org/x/crypto/pbkdf2. There is no official v0.55.x -# backport; the first fixed release (v0.56.0) requires Go 1.26 and would raise -# the supported Go floor for code that cannot reach the vulnerable package. +# resolves only golang.org/x/crypto/pbkdf2; the vulnerable package is unreachable. ignoreUntil = 2027-03-09 reason = "golang.org/x/crypto/ssh is not imported by this driver or its tests (verified via go list -deps -test); only x/crypto/pbkdf2 is reachable. Re-review on expiry: confirm ssh remains unimported before renewing." @@ -52,8 +50,6 @@ reason = "golang.org/x/crypto/ssh is not imported by this driver or its tests (v id = "GO-2026-6355" # CVE-2026-56855 is a denial of service in golang.org/x/crypto/ssh. # The driver does not import ssh: `go list -buildvcs=false -deps -test ./...` -# resolves only golang.org/x/crypto/pbkdf2. There is no official v0.55.x -# backport; the first fixed release (v0.56.0) requires Go 1.26 and would raise -# the supported Go floor for code that cannot reach the vulnerable package. +# resolves only golang.org/x/crypto/pbkdf2; the vulnerable package is unreachable. ignoreUntil = 2027-03-09 reason = "golang.org/x/crypto/ssh is not imported by this driver or its tests (verified via go list -deps -test); only x/crypto/pbkdf2 is reachable. Re-review on expiry: confirm ssh remains unimported before renewing." From 7f1bc124972951c2ba33cc7bbfaae90ebb40bfa1 Mon Sep 17 00:00:00 2001 From: Vu Anh Phung Date: Fri, 23 Oct 2026 19:52:14 +0000 Subject: [PATCH 2/5] test: preserve request error pointers through wrapping Keep the constructor result as an error interface to retain its dynamic pointer type without triggering Go 1.27 printf vet. Assert pointer identity and errors.Is through wrapping. Signed-off-by: Vu Anh Phung --- internal/errors/category_test.go | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/internal/errors/category_test.go b/internal/errors/category_test.go index 2a1fc319..2441c16d 100644 --- a/internal/errors/category_test.go +++ b/internal/errors/category_test.go @@ -60,19 +60,23 @@ func TestCategoryFromError(t *testing.T) { }) t.Run("finds the tag through a fmt.Errorf %w wrapper", func(t *testing.T) { - inner := NewRequestError(context.TODO(), "inner", errors.New("cause")). + var inner error = NewRequestError(context.TODO(), "inner", errors.New("cause")). WithCategory(CategoryResultSet) - wrapped := fmt.Errorf("outer: %w", *inner) + wrapped := fmt.Errorf("outer: %w", inner) + assert.Same(t, inner, stderrors.Unwrap(wrapped)) + assert.ErrorIs(t, wrapped, inner) assert.Equal(t, CategoryResultSet, CategoryFromError(wrapped)) }) t.Run("finds the tag inside a tree-shaped chain", func(t *testing.T) { - inner := NewRequestError(context.TODO(), "inner", errors.New("cause")). + var inner error = NewRequestError(context.TODO(), "inner", errors.New("cause")). WithCategory(CategoryResultSet) joined := stderrors.Join(errors.New("sibling"), inner) + assert.ErrorIs(t, joined, inner) assert.Equal(t, CategoryResultSet, CategoryFromError(joined)) - multiWrap := fmt.Errorf("a: %w, b: %w", errors.New("sibling"), *inner) + multiWrap := fmt.Errorf("a: %w, b: %w", errors.New("sibling"), inner) + assert.ErrorIs(t, multiWrap, inner) assert.Equal(t, CategoryResultSet, CategoryFromError(multiWrap)) }) } From 4277c844df782dd57f5614a1aaf1932fa5386784 Mon Sep 17 00:00:00 2001 From: Vu Anh Phung Date: Fri, 23 Oct 2026 20:37:05 +0000 Subject: [PATCH 3/5] ci: test the latest Go 1.27 patch alongside the minimum Signed-off-by: Vu Anh Phung --- .github/workflows/go.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index b57aad6a..92e32bb6 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -45,9 +45,9 @@ jobs: build-and-test: name: Test and Build strategy: - # Match the minimum patched toolchain declared in go.mod. + # Test the minimum patched toolchain and the latest 1.27 patch. matrix: - go-version: ['1.27.3'] + go-version: ['1.27.3', '1.27.x'] runs-on: group: databricks-protected-runner-group labels: linux-ubuntu-latest @@ -63,6 +63,7 @@ jobs: uses: actions/setup-go@924ae3a1cded613372ab5595356fb5720e22ba16 # v6.5.0 with: go-version: ${{ matrix.go-version }} + check-latest: true cache: false - name: Cache Go artifacts From 67a1aa81108769fcbc4fd27e1143e6586064cf56 Mon Sep 17 00:00:00 2001 From: Vu Anh Phung Date: Fri, 23 Oct 2026 20:48:35 +0000 Subject: [PATCH 4/5] ci: build OSV scanner with the selected Go toolchain Signed-off-by: Vu Anh Phung --- .github/workflows/securityScan.yml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/.github/workflows/securityScan.yml b/.github/workflows/securityScan.yml index a4ff217a..6e68d059 100644 --- a/.github/workflows/securityScan.yml +++ b/.github/workflows/securityScan.yml @@ -63,9 +63,8 @@ jobs: - name: Install osv-scanner run: | set -euo pipefail - curl -fsSL -o /tmp/osv-scanner \ - https://github.com/google/osv-scanner/releases/download/v2.3.8/osv-scanner_linux_amd64 - chmod +x /tmp/osv-scanner + # Build govulncheck with the same Go version it uses to load packages. + GOBIN=/tmp GOTOOLCHAIN=local go install github.com/google/osv-scanner/v2/cmd/osv-scanner@v2.3.8 /tmp/osv-scanner --version - name: Run OSV-Scanner From d0ebb2959b634e37d84ecfd220636c2987559d50 Mon Sep 17 00:00:00 2001 From: Vu Anh Phung Date: Fri, 23 Oct 2026 20:54:53 +0000 Subject: [PATCH 5/5] ci: use the Go 1.27-compatible OSV scanner release Signed-off-by: Vu Anh Phung --- .github/workflows/securityScan.yml | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/.github/workflows/securityScan.yml b/.github/workflows/securityScan.yml index 6e68d059..07ebd103 100644 --- a/.github/workflows/securityScan.yml +++ b/.github/workflows/securityScan.yml @@ -16,13 +16,12 @@ name: Security Scan # # - workflow_dispatch: behaves like the cron run (full reporting). # -# Scanner: OSV-Scanner v2.3.8 (purl-based via OSV.dev; federates GHSA, +# Scanner: OSV-Scanner v2.7.0 (purl-based via OSV.dev; federates GHSA, # NVD, Go vuln DB, RustSec, PyPA). Reads `go.mod`/`go.sum` natively -- # no separate SBOM tool needed. # # Suppressions live in `osv-scanner.toml` as [[IgnoredVulns]] entries -# (CVE-id global; OSV-Scanner v2.3.8 doesn't support per-package CVE -# scoping). Each entry has a justification comment. +# (CVE-id global). Each entry has a justification comment. on: pull_request: @@ -46,9 +45,8 @@ jobs: - name: Checkout repository uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - # JFrog OIDC + GOPROXY: skipped on fork PRs (no OIDC token from - # GitHub's perspective). Fork PRs still work because OSV-Scanner - # reads `go.mod` directly without needing to download modules. + # Fork PRs cannot obtain the JFrog OIDC token. Download the scanner + # from GitHub Releases so installation does not require GOPROXY. - name: Setup JFrog if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository uses: ./.github/actions/setup-jfrog @@ -63,8 +61,9 @@ jobs: - name: Install osv-scanner run: | set -euo pipefail - # Build govulncheck with the same Go version it uses to load packages. - GOBIN=/tmp GOTOOLCHAIN=local go install github.com/google/osv-scanner/v2/cmd/osv-scanner@v2.3.8 + curl -fsSL -o /tmp/osv-scanner \ + https://github.com/google/osv-scanner/releases/download/v2.7.0/osv-scanner_linux_amd64 + chmod +x /tmp/osv-scanner /tmp/osv-scanner --version - name: Run OSV-Scanner