From 10c445ceb2b78be9979765b9bc602474379d2487 Mon Sep 17 00:00:00 2001 From: Nemu-x Date: Wed, 14 Oct 2026 23:21:47 +0300 Subject: [PATCH 1/3] ci: test the citrus branch on push and pull requests Go 1.26 on Linux and Windows with go vet, the minimum Go 1.20 on Linux, and a race detector run. Replaces the upstream matrix of 30 jobs. --- .github/workflows/ci.yml | 48 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 .github/workflows/ci.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..a135800 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,48 @@ +name: CI + +on: + push: + branches: [citrus] + pull_request: + branches: [citrus] + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + +permissions: + contents: read + +jobs: + test: + strategy: + fail-fast: false + matrix: + include: + - { os: ubuntu-latest, go: '1.26' } + - { os: windows-latest, go: '1.26' } + - { os: ubuntu-latest, go: '1.20' } + runs-on: ${{ matrix.os }} + env: + CGO_ENABLED: 0 + GOTOOLCHAIN: local + steps: + - uses: actions/checkout@v5 + - uses: actions/setup-go@v6 + with: + go-version: ${{ matrix.go }} + - name: Vet + if: matrix.go == '1.26' + run: go vet ./... + - name: Test + run: go test -count=1 ./... + + race: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v5 + - uses: actions/setup-go@v6 + with: + go-version: '1.26' + - name: Test with the race detector + run: go test -race -count=1 ./... From 22a85a8d1fcb1f161614d861729420cae5e020b3 Mon Sep 17 00:00:00 2001 From: Nemu-x Date: Wed, 14 Oct 2026 23:29:08 +0300 Subject: [PATCH 2/3] test(httputil): expect a clean query for 10001 parameters only under the limit go.mod says go 1.20, so Go 1.24+ builds the tests with GODEBUG=urlmaxqueryparams=0 and Go 1.20 has no limit at all. net/url then parses every parameter and the proxy forwards all of them, which is consistent with what the hook saw; skip that case when net/url does not enforce the limit. --- httputil/reverseproxy_test.go | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/httputil/reverseproxy_test.go b/httputil/reverseproxy_test.go index c694422..b6f4c0b 100644 --- a/httputil/reverseproxy_test.go +++ b/httputil/reverseproxy_test.go @@ -2094,6 +2094,14 @@ func testReverseProxyQueryParameterSmuggling(t *testing.T, wantCleanQuery bool, rawQuery: strings.Repeat("a=1&", 10000) + "a=1", cleanQuery: "", }} { + if wantCleanQuery && strings.Count(test.rawQuery, "&") >= 10000 && !urlLimitsQueryParams() { + // This fork's go.mod says go 1.20, so Go >= 1.24 runs it with + // GODEBUG=urlmaxqueryparams=0, and Go < 1.24 has no limit at + // all. Without the limit net/url parses every parameter, the + // proxy re-encodes all of them, and nothing is smuggled past + // the hook: the expectation only holds when the limit is on. + continue + } res, err := frontend.Client().Get(frontend.URL + "?" + test.rawQuery) if err != nil { t.Fatalf("Get: %v", err) @@ -2110,6 +2118,13 @@ func testReverseProxyQueryParameterSmuggling(t *testing.T, wantCleanQuery bool, } } +// urlLimitsQueryParams reports whether net/url rejects queries with more than +// 10000 parameters in this build (see testReverseProxyQueryParameterSmuggling). +func urlLimitsQueryParams() bool { + _, err := url.ParseQuery(strings.Repeat("a=1&", 10000) + "a=1") + return err != nil +} + // Issue #72954: We should not call WriteHeader on a ResponseWriter after hijacking // the connection. func TestReverseProxyHijackCopyError(t *testing.T) { From df10c6445be98dd712e3750d622a8b3429453032 Mon Sep 17 00:00:00 2001 From: Nemu-x Date: Wed, 14 Oct 2026 23:29:08 +0300 Subject: [PATCH 3/3] ci: run on main and dev, skip loopclosure in vet, keep LF on Windows --- .github/workflows/ci.yml | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a135800..847173a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -2,9 +2,9 @@ name: CI on: push: - branches: [citrus] + branches: [main, dev] pull_request: - branches: [citrus] + branches: [main, dev] concurrency: group: ${{ github.workflow }}-${{ github.ref }} @@ -27,13 +27,21 @@ jobs: CGO_ENABLED: 0 GOTOOLCHAIN: local steps: + # Keep testdata byte-exact: autocrlf on Windows runners rewrites LF. + - name: Disable CRLF conversion + if: runner.os == 'Windows' + run: | + git config --global core.autocrlf false + git config --global core.eol lf - uses: actions/checkout@v5 - uses: actions/setup-go@v6 with: go-version: ${{ matrix.go }} + # go.mod says go 1.20, so loopclosure flags upstream Go tests that assume + # per-iteration loop variables; the rest of vet stays on. - name: Vet if: matrix.go == '1.26' - run: go vet ./... + run: go vet -loopclosure=false ./... - name: Test run: go test -count=1 ./...