From f54f85d07fa6c4b4b0dd8b0e20b69862b36bf49a Mon Sep 17 00:00:00 2001 From: kotakanbe Date: Mon, 16 Mar 2026 13:04:23 +0900 Subject: [PATCH 1/2] deps: remove oauth2 in favor of self-implemented bearerTransport Replace golang.org/x/oauth2 with a minimal bearerTransport (http.RoundTripper) that sets the Authorization header. oauth2 was only used to create an HTTP client with a static Bearer token, which doesn't require the full OAuth2 library. Co-Authored-By: Claude Opus 4.6 --- detector/github.go | 30 +++++++++++++++++++----------- go.mod | 2 +- 2 files changed, 20 insertions(+), 12 deletions(-) diff --git a/detector/github.go b/detector/github.go index 239dd5d538..77182f6f09 100644 --- a/detector/github.go +++ b/detector/github.go @@ -16,17 +16,29 @@ import ( "github.com/future-architect/vuls/errof" "github.com/future-architect/vuls/logging" "github.com/future-architect/vuls/models" - "golang.org/x/oauth2" ) +type bearerTransport struct { + token string + base http.RoundTripper +} + +func (t *bearerTransport) RoundTrip(req *http.Request) (*http.Response, error) { + r := req.Clone(req.Context()) + r.Header.Set("Authorization", "Bearer "+t.token) + return t.base.RoundTrip(r) +} + +func newBearerClient(token string) *http.Client { + return &http.Client{ + Transport: &bearerTransport{token: token, base: http.DefaultTransport}, + } +} + // DetectGitHubSecurityAlerts access to owner/repo on GitHub and fetch security alerts of the repository via GitHub API v4 GraphQL and then set to the given ScanResult. // https://help.github.com/articles/about-security-alerts-for-vulnerable-dependencies/ func DetectGitHubSecurityAlerts(r *models.ScanResult, owner, repo, token string, ignoreDismissed bool) (nCVEs int, err error) { - src := oauth2.StaticTokenSource( - &oauth2.Token{AccessToken: token}, - ) - //TODO Proxy - httpClient := oauth2.NewClient(context.Background(), src) + httpClient := newBearerClient(token) // TODO Use `https://github.com/shurcooL/githubv4` if the tool supports vulnerabilityAlerts Endpoint // Memo : https://developer.github.com/v4/explorer/ @@ -212,11 +224,7 @@ type SecurityAlerts struct { // DetectGitHubDependencyGraph access to owner/repo on GitHub and fetch dependency graph of the repository via GitHub API v4 GraphQL and then set to the given ScanResult. // https://docs.github.com/en/code-security/supply-chain-security/understanding-your-software-supply-chain/about-the-dependency-graph func DetectGitHubDependencyGraph(r *models.ScanResult, owner, repo, token string) (err error) { - src := oauth2.StaticTokenSource( - &oauth2.Token{AccessToken: token}, - ) - //TODO Proxy - httpClient := oauth2.NewClient(context.Background(), src) + httpClient := newBearerClient(token) return fetchDependencyGraph(r, httpClient, owner, repo, "", "", 10, 100) } diff --git a/go.mod b/go.mod index c793fa3a72..f212c63d27 100644 --- a/go.mod +++ b/go.mod @@ -58,7 +58,6 @@ require ( github.com/vulsio/go-msfdb v0.4.4 github.com/vulsio/gost v0.7.2 go.etcd.io/bbolt v1.4.3 - golang.org/x/oauth2 v0.35.0 golang.org/x/sync v0.20.0 golang.org/x/term v0.40.0 golang.org/x/text v0.34.0 @@ -356,6 +355,7 @@ require ( golang.org/x/exp v0.0.0-20251219203646-944ab1f22d93 // indirect golang.org/x/mod v0.33.0 // indirect golang.org/x/net v0.51.0 // indirect + golang.org/x/oauth2 v0.35.0 // indirect golang.org/x/sys v0.41.0 // indirect golang.org/x/time v0.14.0 // indirect golang.org/x/tools v0.42.0 // indirect From a1a9a18db18ae66195e88cf030e296240e31ebad Mon Sep 17 00:00:00 2001 From: kotakanbe Date: Mon, 16 Mar 2026 13:17:11 +0900 Subject: [PATCH 2/2] test: add bearerTransport tests for oauth2 replacement Verify that bearerTransport correctly sets Authorization Bearer header, clones requests without modifying originals, and integrates properly with http.Client. Co-Authored-By: Claude Opus 4.6 --- detector/github_test.go | 106 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 106 insertions(+) create mode 100644 detector/github_test.go diff --git a/detector/github_test.go b/detector/github_test.go new file mode 100644 index 0000000000..775b0fda44 --- /dev/null +++ b/detector/github_test.go @@ -0,0 +1,106 @@ +//go:build !scanner + +package detector + +import ( + "net/http" + "net/http/httptest" + "testing" +) + +func TestBearerTransport_SetsAuthorizationHeader(t *testing.T) { + t.Parallel() + + const token = "ghp_test1234567890" + + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + got := r.Header.Get("Authorization") + want := "Bearer " + token + if got != want { + t.Errorf("Authorization header = %q, want %q", got, want) + } + w.WriteHeader(http.StatusOK) + })) + defer ts.Close() + + client := &http.Client{ + Transport: &bearerTransport{token: token, base: http.DefaultTransport}, + } + + resp, err := client.Get(ts.URL) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + t.Errorf("status = %d, want %d", resp.StatusCode, http.StatusOK) + } +} + +func TestBearerTransport_ClonesRequest(t *testing.T) { + t.Parallel() + + const token = "ghp_clonetest" + + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { + w.WriteHeader(http.StatusOK) + })) + defer ts.Close() + + transport := &bearerTransport{token: token, base: http.DefaultTransport} + + req, err := http.NewRequest(http.MethodGet, ts.URL, nil) + if err != nil { + t.Fatalf("failed to create request: %v", err) + } + req.Header.Set("X-Custom", "original") + + resp, err := transport.RoundTrip(req) + if err != nil { + t.Fatalf("RoundTrip error: %v", err) + } + defer resp.Body.Close() + + // The original request must not have the Authorization header injected. + if got := req.Header.Get("Authorization"); got != "" { + t.Errorf("original request Authorization header = %q, want empty (request was mutated)", got) + } + + // The original X-Custom header should still be present and unchanged. + if got := req.Header.Get("X-Custom"); got != "original" { + t.Errorf("original request X-Custom header = %q, want %q", got, "original") + } +} + +func TestNewBearerClient_ReturnsValidClient(t *testing.T) { + t.Parallel() + + const token = "ghp_integrationtest" + + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + got := r.Header.Get("Authorization") + want := "Bearer " + token + if got != want { + t.Errorf("Authorization header = %q, want %q", got, want) + } + w.WriteHeader(http.StatusOK) + })) + defer ts.Close() + + // newBearerClient uses http.DefaultTransport, so it can reach the test server. + client := newBearerClient(token) + if client == nil { + t.Fatal("newBearerClient returned nil") + } + + resp, err := client.Get(ts.URL) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + t.Errorf("status = %d, want %d", resp.StatusCode, http.StatusOK) + } +}