From 9e39dcb5fad14babea5a327816c67bb1bad8a46a Mon Sep 17 00:00:00 2001 From: Qiu Yuzhou Date: Tue, 20 Oct 2026 00:12:44 +0800 Subject: [PATCH 1/8] fix: resolve errcheck and staticcheck findings - handle unchecked error returns in transport tests - drop untrappable os.Kill from signal.Notify - use protoiface.MessageV1 instead of deprecated golang/protobuf import (golang/protobuf becomes an indirect dependency) --- go.mod | 2 +- main.go | 8 +++++--- transport_test.go | 31 ++++++++++++++++++------------- 3 files changed, 24 insertions(+), 17 deletions(-) diff --git a/go.mod b/go.mod index fa69b04c..22d3bda5 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,6 @@ module github.com/shadowsocks/v2ray-plugin go 1.25.5 require ( - github.com/golang/protobuf v1.5.4 github.com/v2fly/v2ray-core/v5 v5.53.0 google.golang.org/protobuf v1.36.11 ) @@ -12,6 +11,7 @@ require ( github.com/adrg/xdg v0.5.3 // indirect github.com/apernet/quic-go v0.59.1-0.20260217092621-db4786c77a22 // indirect github.com/davecgh/go-spew v1.1.1 // indirect + github.com/golang/protobuf v1.5.4 // indirect github.com/gorilla/websocket v1.5.3 // indirect github.com/kr/text v0.2.0 // indirect github.com/miekg/dns v1.1.72 // indirect diff --git a/main.go b/main.go index 1d9f8e92..9c7b139e 100644 --- a/main.go +++ b/main.go @@ -12,7 +12,7 @@ import ( "strings" "syscall" - "github.com/golang/protobuf/proto" + "google.golang.org/protobuf/runtime/protoiface" "google.golang.org/protobuf/types/known/anypb" _ "github.com/v2fly/v2ray-core/v5/app/proxyman/inbound" @@ -152,7 +152,9 @@ func generateConfig() (*core.Config, error) { }, }) - var transportSettings proto.Message + // serial.ToTypedMessage takes the legacy v1 message interface; use it + // directly instead of the deprecated github.com/golang/protobuf import. + var transportSettings protoiface.MessageV1 var connectionReuse bool switch *mode { case "websocket": @@ -446,7 +448,7 @@ func main() { { osSignals := make(chan os.Signal, 1) - signal.Notify(osSignals, os.Interrupt, os.Kill, syscall.SIGTERM) + signal.Notify(osSignals, os.Interrupt, syscall.SIGTERM) <-osSignals } } diff --git a/transport_test.go b/transport_test.go index 0c10e0c4..8e2630bb 100644 --- a/transport_test.go +++ b/transport_test.go @@ -30,11 +30,11 @@ func TestMain(m *testing.M) { cmd := exec.Command("go", "build", "-o", testBinary, ".") cmd.Stdout, cmd.Stderr = os.Stdout, os.Stderr if err := cmd.Run(); err != nil { - os.RemoveAll(dir) + _ = os.RemoveAll(dir) os.Exit(1) } code := m.Run() - os.RemoveAll(dir) + _ = os.RemoveAll(dir) os.Exit(code) } @@ -71,14 +71,14 @@ func unusedPort(t *testing.T, network string) string { if err != nil { t.Fatal(err) } - defer c.Close() + defer func() { _ = c.Close() }() return fmt.Sprint(c.LocalAddr().(*net.UDPAddr).Port) } l, err := net.Listen("tcp", "127.0.0.1:0") if err != nil { t.Fatal(err) } - defer l.Close() + defer func() { _ = l.Close() }() return fmt.Sprint(l.Addr().(*net.TCPAddr).Port) } @@ -102,17 +102,19 @@ func startPlugin(t *testing.T, args []string, env []string) { done := make(chan error, 1) go func() { done <- cmd.Wait() }() t.Cleanup(func() { - cmd.Process.Signal(os.Interrupt) + _ = cmd.Process.Signal(os.Interrupt) select { case err := <-done: if err != nil { t.Errorf("plugin exited unexpectedly: %v", err) } case <-time.After(3 * time.Second): - cmd.Process.Kill() - <-done + _ = cmd.Process.Kill() + if err := <-done; err != nil { + t.Logf("plugin did not exit after kill: %v", err) + } } - logFile.Close() + _ = logFile.Close() if t.Failed() { data, _ := os.ReadFile(logFile.Name()) t.Logf("plugin output:\n%s", data) @@ -126,8 +128,8 @@ func echoThroughPlugin(t *testing.T, addr string) error { if err != nil { return err } - defer conn.Close() - conn.SetDeadline(time.Now().Add(2 * time.Second)) + defer func() { _ = conn.Close() }() + _ = conn.SetDeadline(time.Now().Add(2 * time.Second)) payload := "Shadowsocks transport round trip\x00\xff" if _, err := io.WriteString(conn, payload); err != nil { return err @@ -181,14 +183,17 @@ func testTransport(t *testing.T, transport, serverPassword, clientPassword, clie if err != nil { t.Fatal(err) } - t.Cleanup(func() { echo.Close() }) + t.Cleanup(func() { _ = echo.Close() }) go func() { for { conn, err := echo.Accept() if err != nil { return } - go func() { defer conn.Close(); io.Copy(conn, conn) }() + go func() { + defer func() { _ = conn.Close() }() + _, _ = io.Copy(conn, conn) + }() } }() network := "udp" @@ -240,7 +245,7 @@ func waitForListener(t *testing.T, addr string) { for time.Now().Before(deadline) { conn, err := net.DialTimeout("tcp", addr, 100*time.Millisecond) if err == nil { - conn.Close() + _ = conn.Close() return } time.Sleep(20 * time.Millisecond) From 00612fd95b212c8e4c738e574a27fdb2ef506e01 Mon Sep 17 00:00:00 2001 From: Qiu Yuzhou Date: Tue, 20 Oct 2026 00:12:50 +0800 Subject: [PATCH 2/8] chore: add golangci-lint configuration Exclude the three legacy unused helpers in args.go by exact text; all other default linters stay enabled. --- .golangci.yml | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 .golangci.yml diff --git a/.golangci.yml b/.golangci.yml new file mode 100644 index 00000000..40e8ac80 --- /dev/null +++ b/.golangci.yml @@ -0,0 +1,8 @@ +version: "2" + +linters: + exclusions: + rules: + # Legacy helpers kept for reference; tracked for eventual removal. + - linters: [unused] + text: "func (isIPv6|backslashEscape|encodeSmethodArgs) is unused" From a1be6c612f5e31afbf9e6b003c99b4c3fad4a87e Mon Sep 17 00:00:00 2001 From: Qiu Yuzhou Date: Tue, 20 Oct 2026 00:12:50 +0800 Subject: [PATCH 3/8] ci: add test, lint, and security workflows - tests: Go 1.25/1.26/stable matrix with race detection, coverage upload, and go mod tidy verification - lint: go vet plus golangci-lint on every PR - security: govulncheck, gosec (SARIF), and CodeQL analysis - codecov: compare project coverage against base instead of a fixed 80% target (current coverage is ~6%) --- .github/codeql/codeql-config.yml | 8 ++++ .github/workflows/lint.yml | 31 ++++++++++++++++ .github/workflows/security.yml | 63 ++++++++++++++++++++++++++++++++ .github/workflows/test.yml | 53 +++++++++++++++++++++++++++ codecov.yml | 9 +++++ 5 files changed, 164 insertions(+) create mode 100644 .github/codeql/codeql-config.yml create mode 100644 .github/workflows/lint.yml create mode 100644 .github/workflows/security.yml create mode 100644 .github/workflows/test.yml create mode 100644 codecov.yml diff --git a/.github/codeql/codeql-config.yml b/.github/codeql/codeql-config.yml new file mode 100644 index 00000000..e55c3b8a --- /dev/null +++ b/.github/codeql/codeql-config.yml @@ -0,0 +1,8 @@ +name: "CodeQL config" + +queries: + - uses: security-and-quality + +query-filters: + - exclude: + id: go/unused-result diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml new file mode 100644 index 00000000..c82106fa --- /dev/null +++ b/.github/workflows/lint.yml @@ -0,0 +1,31 @@ +name: Lint + +on: + push: + branches: [master] + pull_request: + +permissions: + contents: read + +jobs: + lint: + name: Lint + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v6 + + - name: Set up Go + uses: actions/setup-go@v6 + with: + go-version: stable + + - name: Run go vet + run: go vet ./... + + - name: golangci-lint + uses: golangci/golangci-lint-action@v9 + with: + version: latest + args: --timeout 5m diff --git a/.github/workflows/security.yml b/.github/workflows/security.yml new file mode 100644 index 00000000..4836192b --- /dev/null +++ b/.github/workflows/security.yml @@ -0,0 +1,63 @@ +name: Security + +on: + push: + branches: [master] + pull_request: + +permissions: + contents: read + security-events: write + +jobs: + govulncheck: + name: Vulnerability Check + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v6 + + - name: Set up Go + uses: actions/setup-go@v6 + with: + go-version: stable + + - name: Run govulncheck + uses: golang/govulncheck-action@v1 + + gosec: + name: gosec + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v6 + + - name: Run gosec + uses: securego/gosec@v2 + with: + args: -no-fail -fmt sarif -out gosec-results.sarif ./... + + - name: Upload gosec results + if: always() + uses: github/codeql-action/upload-sarif@v4 + with: + sarif_file: gosec-results.sarif + + codeql: + name: CodeQL + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v6 + + - name: Initialize CodeQL + uses: github/codeql-action/init@v4 + with: + languages: go + config-file: .github/codeql/codeql-config.yml + + - name: Autobuild + uses: github/codeql-action/autobuild@v4 + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v4 diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 00000000..28c1e202 --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,53 @@ +name: Tests + +on: + push: + branches: [master] + pull_request: + +permissions: + contents: read + +jobs: + test: + name: Test (Go ${{ matrix.go }}) + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + go: + - "1.25" + - "1.26" + - "stable" + + steps: + - uses: actions/checkout@v6 + + - name: Set up Go + uses: actions/setup-go@v6 + with: + go-version: ${{ matrix.go }} + + - name: Verify dependencies + run: | + go mod verify + go mod download + + - name: Check go mod tidy + run: | + go mod tidy + git diff --exit-code go.mod go.sum + + - name: Build + run: go build ./... + + - name: Run tests + run: go test -v -race -shuffle=on -coverprofile=coverage.out ./... + + - name: Upload coverage + if: matrix.go == 'stable' + uses: codecov/codecov-action@v5 + with: + files: ./coverage.out + fail_ci_if_error: false + token: ${{ secrets.CODECOV_TOKEN }} diff --git a/codecov.yml b/codecov.yml new file mode 100644 index 00000000..1b4e04e1 --- /dev/null +++ b/codecov.yml @@ -0,0 +1,9 @@ +coverage: + status: + project: + default: + target: auto + threshold: 1% + patch: + default: + target: 80% From 29d4605223f9c352bbccfb440b59f0241f6f540b Mon Sep 17 00:00:00 2001 From: Qiu Yuzhou Date: Tue, 20 Oct 2026 00:12:57 +0800 Subject: [PATCH 4/8] chore: add Dependabot configuration Weekly updates for Go modules and GitHub Actions, with minor/patch and actions updates grouped into single PRs. --- .github/dependabot.yml | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 .github/dependabot.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 00000000..af9a81df --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,23 @@ +version: 2 +updates: + # Go modules + - package-ecosystem: gomod + directory: / + schedule: + interval: weekly + day: monday + labels: ["dependencies", "go"] + open-pull-requests-limit: 10 + groups: + go-minor-patch: + update-types: [minor, patch] + + # GitHub Actions + - package-ecosystem: github-actions + directory: / + schedule: + interval: weekly + labels: ["dependencies", "ci"] + groups: + actions: + patterns: ["*"] From 0c71fc677a99af789406b4e23c5255ba74a59db6 Mon Sep 17 00:00:00 2001 From: Qiu Yuzhou Date: Tue, 20 Oct 2026 00:16:07 +0800 Subject: [PATCH 5/8] ci: add workflow_dispatch triggers Lets CI run manually on feature branches, where neither the master push trigger nor pull_request applies. --- .github/workflows/lint.yml | 1 + .github/workflows/security.yml | 1 + .github/workflows/test.yml | 1 + 3 files changed, 3 insertions(+) diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index c82106fa..24dc8bfc 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -4,6 +4,7 @@ on: push: branches: [master] pull_request: + workflow_dispatch: permissions: contents: read diff --git a/.github/workflows/security.yml b/.github/workflows/security.yml index 4836192b..05b6fd58 100644 --- a/.github/workflows/security.yml +++ b/.github/workflows/security.yml @@ -4,6 +4,7 @@ on: push: branches: [master] pull_request: + workflow_dispatch: permissions: contents: read diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 28c1e202..6078bd31 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -4,6 +4,7 @@ on: push: branches: [master] pull_request: + workflow_dispatch: permissions: contents: read From fa77e14743dd97c7b51aba9aabee05af67a40012 Mon Sep 17 00:00:00 2001 From: Qiu Yuzhou Date: Tue, 20 Oct 2026 21:39:52 +0800 Subject: [PATCH 6/8] ci: re-trigger workflow registration check From b986bd051734142294c29f5943d164b3d747ed64 Mon Sep 17 00:00:00 2001 From: Qiu Yuzhou Date: Tue, 20 Oct 2026 21:45:16 +0800 Subject: [PATCH 7/8] fix(ci): pin gosec action to a resolvable version securego/gosec publishes no floating v2 tag; use the current release v2.29.0 (Dependabot keeps it up to date). --- .github/workflows/security.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/security.yml b/.github/workflows/security.yml index 05b6fd58..dc3ce2b2 100644 --- a/.github/workflows/security.yml +++ b/.github/workflows/security.yml @@ -34,7 +34,8 @@ jobs: - uses: actions/checkout@v6 - name: Run gosec - uses: securego/gosec@v2 + # gosec publishes no floating major tag; Dependabot keeps this current. + uses: securego/gosec@v2.29.0 with: args: -no-fail -fmt sarif -out gosec-results.sarif ./... From 4446310a56100b85c22ef244b87664fccadfb86f Mon Sep 17 00:00:00 2001 From: Qiu Yuzhou Date: Tue, 20 Oct 2026 21:52:17 +0800 Subject: [PATCH 8/8] chore: retire CircleCI in favor of GitHub Actions The CircleCI build has been failing on master since before this branch (last green build: #148). Its go test/go vet steps are now covered by the GitHub Actions workflows; cross-compiled release binaries remain available via build-release.sh. Also drop the CircleCI and Go Report Card badges, and the nightly artifacts note that pointed at CircleCI. --- .circleci/config.yml | 23 ----------------------- README.md | 3 --- 2 files changed, 26 deletions(-) delete mode 100644 .circleci/config.yml diff --git a/.circleci/config.yml b/.circleci/config.yml deleted file mode 100644 index b3b23ee0..00000000 --- a/.circleci/config.yml +++ /dev/null @@ -1,23 +0,0 @@ -version: '2.1' -orbs: - go: circleci/go@1.11.0 -jobs: - build: - executor: - name: go/default - tag: '1.26.9' - steps: - - checkout - - run: sudo apt-get update && sudo apt-get install -y upx - - go/load-cache - - run: go test ./... - - run: go vet ./... - - run: ./build-release.sh - - go/save-cache - - store_artifacts: - path: bin - destination: bin -workflows: - main: - jobs: - - build diff --git a/README.md b/README.md index f158ef44..62190796 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,7 @@ ## Yet another SIP003 plugin for shadowsocks, based on [v2ray](https://github.com/v2fly/v2ray-core) -[![CircleCI](https://circleci.com/gh/shadowsocks/v2ray-plugin.svg?style=shield)](https://circleci.com/gh/shadowsocks/v2ray-plugin) [![Releases](https://img.shields.io/github/downloads/shadowsocks/v2ray-plugin/total.svg)](https://github.com/shadowsocks/v2ray-plugin/releases) [![Language: Go](https://img.shields.io/badge/go-1.25.5+-blue.svg)](https://github.com/shadowsocks/v2ray-plugin/search?l=go) -[![Go Report Card](https://goreportcard.com/badge/github.com/shadowsocks/v2ray-plugin)](https://goreportcard.com/report/github.com/shadowsocks/v2ray-plugin) [![License](https://img.shields.io/github/license/shadowsocks/v2ray-plugin.svg)](LICENSE) ## Build @@ -12,7 +10,6 @@ Requires Go 1.25.5 or later. This plugin uses V2Ray core v5.53.0. * `go build` * `go test ./...` runs integration tests using local TCP and UDP sockets. -* Alternatively, you can grab the latest nightly from Circle CI by logging into Circle CI or adding `#artifacts` at the end of URL like such: https://circleci.com/gh/shadowsocks/v2ray-plugin/20#artifacts ## Usage