Skip to content

Commit 4dc2025

Browse files
authored
Adding Logic and Tests for Authorization token to only be used if its provided (#227)
1 parent 1035ca1 commit 4dc2025

File tree

7 files changed

+209
-7
lines changed

7 files changed

+209
-7
lines changed

go.mod

+4-3
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,6 @@ go 1.22.6
66
// please place any replace statements here at the top for visibility and add a
77
// comment to it as to when it can be removed
88

9-
replace github.com/imdario/mergo => github.com/imdario/mergo v0.3.16
10-
119
require (
1210
github.com/Azure/go-autorest/autorest v0.11.29
1311
github.com/Azure/go-autorest/autorest/adal v0.9.24
@@ -33,7 +31,8 @@ require (
3331
github.com/aws/aws-sdk-go-v2/service/ecr v1.32.2
3432
github.com/gofri/go-github-ratelimit v1.1.0
3533
github.com/google/go-containerregistry v0.20.2
36-
github.com/google/go-github/v58 v58.0.0
34+
github.com/google/go-github/v62 v62.0.0
35+
github.com/jarcoal/httpmock v1.3.1
3736
github.com/stretchr/testify v1.9.0
3837
)
3938

@@ -127,3 +126,5 @@ require (
127126
sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect
128127
sigs.k8s.io/yaml v1.4.0 // indirect
129128
)
129+
130+
replace github.com/imdario/mergo => github.com/imdario/mergo v0.3.16

go.sum

+6-2
Original file line numberDiff line numberDiff line change
@@ -104,8 +104,8 @@ github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
104104
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
105105
github.com/google/go-containerregistry v0.20.2 h1:B1wPJ1SN/S7pB+ZAimcciVD+r+yV/l/DSArMxlbwseo=
106106
github.com/google/go-containerregistry v0.20.2/go.mod h1:z38EKdKh4h7IP2gSfUUqEvalZBqs6AoLeWfUy34nQC8=
107-
github.com/google/go-github/v58 v58.0.0 h1:Una7GGERlF/37XfkPwpzYJe0Vp4dt2k1kCjlxwjIvzw=
108-
github.com/google/go-github/v58 v58.0.0/go.mod h1:k4hxDKEfoWpSqFlc8LTpGd9fu2KrV1YAa6Hi6FmDNY4=
107+
github.com/google/go-github/v62 v62.0.0 h1:/6mGCaRywZz9MuHyw9gD1CwsbmBX8GWsbFkwMmHdhl4=
108+
github.com/google/go-github/v62 v62.0.0/go.mod h1:EMxeUqGJq2xRu9DYBMwel/mr7kZrzUOfQmmpYrZn2a4=
109109
github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=
110110
github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=
111111
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
@@ -129,6 +129,8 @@ github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4=
129129
github.com/imdario/mergo v0.3.16/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY=
130130
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
131131
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
132+
github.com/jarcoal/httpmock v1.3.1 h1:iUx3whfZWVf3jT01hQTO/Eo5sAYtB2/rqaUuOtpInww=
133+
github.com/jarcoal/httpmock v1.3.1/go.mod h1:3yb8rc4BI7TCBhFY8ng0gjuLKJNquuDNiPaZjnENuYg=
132134
github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg=
133135
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
134136
github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8=
@@ -155,6 +157,8 @@ github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxec
155157
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
156158
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
157159
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
160+
github.com/maxatome/go-testdeep v1.12.0 h1:Ql7Go8Tg0C1D/uMMX59LAoYK7LffeJQ6X2T04nTH68g=
161+
github.com/maxatome/go-testdeep v1.12.0/go.mod h1:lPZc/HAcJMP92l7yI6TRz1aZN5URwUBUAfUNvrclaNM=
158162
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
159163
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
160164
github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0=

pkg/client/client_test.go

+3
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@ func TestFromImageURL(t *testing.T) {
2424
Host: "https://docker.repositories.yourdomain.com",
2525
},
2626
},
27+
GHCR: ghcr.Options{
28+
Token: "test-token",
29+
},
2730
})
2831
if err != nil {
2932
t.Fatal(err)

pkg/client/ghcr/ghcr.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import (
77
"strings"
88

99
"github.com/gofri/go-github-ratelimit/github_ratelimit"
10-
"github.com/google/go-github/v58/github"
10+
"github.com/google/go-github/v62/github"
1111
"github.com/jetstack/version-checker/pkg/api"
1212
)
1313

pkg/client/ghcr/ghcr_test.go

+167
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
package ghcr
2+
3+
import (
4+
"context"
5+
"net/http"
6+
"testing"
7+
8+
"github.com/google/go-github/v62/github"
9+
"github.com/jarcoal/httpmock"
10+
"github.com/stretchr/testify/assert"
11+
)
12+
13+
func setup() {
14+
httpmock.Activate()
15+
}
16+
17+
func teardown() {
18+
httpmock.DeactivateAndReset()
19+
}
20+
21+
func registerCommonResponders() {
22+
httpmock.RegisterResponder("GET", "https://api.github.com/users/test-user-owner",
23+
func(req *http.Request) (*http.Response, error) {
24+
return httpmock.NewStringResponse(200, `{"type":"User"}`), nil
25+
})
26+
httpmock.RegisterResponder("GET", "https://api.github.com/users/test-org-owner",
27+
func(req *http.Request) (*http.Response, error) {
28+
return httpmock.NewStringResponse(200, `{"type":"Organization"}`), nil
29+
})
30+
}
31+
32+
func registerTagResponders() {
33+
httpmock.RegisterResponder("GET", "https://api.github.com/users/test-user-owner/packages/container/test-repo/versions",
34+
func(req *http.Request) (*http.Response, error) {
35+
return httpmock.NewStringResponse(200, `[
36+
{
37+
"name": "sha123",
38+
"metadata": {
39+
"container": {
40+
"tags": ["tag1", "tag2"]
41+
}
42+
},
43+
"created_at": "2023-07-08T12:34:56Z"
44+
}
45+
]`), nil
46+
})
47+
httpmock.RegisterResponder("GET", "https://api.github.com/orgs/test-org-owner/packages/container/test-repo/versions",
48+
func(req *http.Request) (*http.Response, error) {
49+
return httpmock.NewStringResponse(200, `[
50+
{
51+
"name": "sha123",
52+
"metadata": {
53+
"container": {
54+
"tags": ["tag1", "tag2"]
55+
}
56+
},
57+
"created_at": "2023-07-08T12:34:56Z"
58+
}
59+
]`), nil
60+
})
61+
}
62+
63+
func TestClient_Tags(t *testing.T) {
64+
setup()
65+
defer teardown()
66+
67+
ctx := context.Background()
68+
host := "ghcr.io"
69+
70+
t.Run("successful tags fetch", func(t *testing.T) {
71+
httpmock.Reset()
72+
registerCommonResponders()
73+
registerTagResponders()
74+
75+
client := New(Options{})
76+
client.client = github.NewClient(nil) // Use the default HTTP client
77+
78+
tags, err := client.Tags(ctx, host, "test-user-owner", "test-repo")
79+
assert.NoError(t, err)
80+
assert.Len(t, tags, 2)
81+
assert.Equal(t, "tag1", tags[0].Tag)
82+
assert.Equal(t, "tag2", tags[1].Tag)
83+
})
84+
85+
t.Run("failed to fetch owner type", func(t *testing.T) {
86+
httpmock.Reset()
87+
httpmock.RegisterResponder("GET", "https://api.github.com/users/test-user-owner",
88+
func(req *http.Request) (*http.Response, error) {
89+
return httpmock.NewStringResponse(404, `{"message": "Not Found"}`), nil
90+
})
91+
92+
client := New(Options{})
93+
client.client = github.NewClient(nil) // Use the default HTTP client
94+
95+
_, err := client.Tags(ctx, host, "test-user-owner", "test-repo")
96+
assert.Error(t, err)
97+
})
98+
99+
t.Run("token not set, no authorization header", func(t *testing.T) {
100+
httpmock.Reset()
101+
httpmock.RegisterResponder("GET", "https://api.github.com/users/test-user-owner",
102+
func(req *http.Request) (*http.Response, error) {
103+
if req.Header.Get("Authorization") != "" {
104+
t.Errorf("expected no Authorization header, got %s", req.Header.Get("Authorization"))
105+
}
106+
return httpmock.NewStringResponse(200, `{"type":"User"}`), nil
107+
})
108+
registerTagResponders()
109+
110+
client := New(Options{}) // No token provided
111+
client.client = github.NewClient(nil)
112+
113+
_, err := client.Tags(ctx, host, "test-user-owner", "test-repo")
114+
assert.NoError(t, err)
115+
})
116+
117+
t.Run("token set, authorization header sent", func(t *testing.T) {
118+
token := "test-token"
119+
httpmock.Reset()
120+
httpmock.RegisterResponder("GET", "https://api.github.com/users/test-user-owner",
121+
func(req *http.Request) (*http.Response, error) {
122+
authHeader := req.Header.Get("Authorization")
123+
expectedAuthHeader := "Bearer " + token
124+
if authHeader != expectedAuthHeader {
125+
t.Errorf("expected Authorization header %s, got %s", expectedAuthHeader, authHeader)
126+
}
127+
return httpmock.NewStringResponse(200, `{"type":"User"}`), nil
128+
})
129+
130+
registerTagResponders()
131+
132+
client := New(Options{Token: token})
133+
134+
_, err := client.Tags(ctx, host, "test-user-owner", "test-repo")
135+
assert.NoError(t, err)
136+
})
137+
138+
t.Run("ownerType returns user", func(t *testing.T) {
139+
httpmock.Reset()
140+
registerCommonResponders()
141+
registerTagResponders()
142+
143+
client := New(Options{})
144+
client.client = github.NewClient(nil) // Use the default HTTP client
145+
146+
tags, err := client.Tags(ctx, host, "test-user-owner", "test-repo")
147+
assert.NoError(t, err)
148+
assert.Len(t, tags, 2)
149+
assert.Equal(t, "tag1", tags[0].Tag)
150+
assert.Equal(t, "tag2", tags[1].Tag)
151+
})
152+
153+
t.Run("ownerType returns org", func(t *testing.T) {
154+
httpmock.Reset()
155+
registerCommonResponders()
156+
registerTagResponders()
157+
158+
client := New(Options{})
159+
client.client = github.NewClient(nil) // Use the default HTTP client
160+
161+
tags, err := client.Tags(ctx, host, "test-org-owner", "test-repo")
162+
assert.NoError(t, err)
163+
assert.Len(t, tags, 2)
164+
assert.Equal(t, "tag1", tags[0].Tag)
165+
assert.Equal(t, "tag2", tags[1].Tag)
166+
})
167+
}

pkg/client/ghcr/path.go

+5
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,11 @@ import (
55
)
66

77
func (c *Client) IsHost(host string) bool {
8+
// Package API requires Authentication
9+
// This forces the Client to use the fallback method
10+
if c.opts.Token == "" {
11+
return false
12+
}
813
return host == "ghcr.io"
914
}
1015

pkg/client/ghcr/path_test.go

+23-1
Original file line numberDiff line numberDiff line change
@@ -4,34 +4,52 @@ import "testing"
44

55
func TestIsHost(t *testing.T) {
66
tests := map[string]struct {
7+
token string
78
host string
89
expIs bool
910
}{
10-
"an empty host should be false": {
11+
"an empty token should be false": {
12+
token: "test-token",
13+
host: "",
14+
expIs: false,
15+
},
16+
"an empty host and token should be false": {
17+
token: "",
18+
host: "",
19+
expIs: false,
20+
},
21+
"an empty host should be false": {
22+
token: "test-token",
1123
host: "",
1224
expIs: false,
1325
},
1426
"random string should be false": {
27+
token: "test-token",
1528
host: "foobar",
1629
expIs: false,
1730
},
1831
"random string with dots should be false": {
32+
token: "test-token",
1933
host: "foobar.foo",
2034
expIs: false,
2135
},
2236
"just ghcr.io should be true": {
37+
token: "test-token",
2338
host: "ghcr.io",
2439
expIs: true,
2540
},
2641
"gcr.io with random sub domains should be false": {
42+
token: "test-token",
2743
host: "ghcr.gcr.io",
2844
expIs: false,
2945
},
3046
"foodghcr.io should be false": {
47+
token: "test-token",
3148
host: "foodghcr.io",
3249
expIs: false,
3350
},
3451
"ghcr.iofoo should be false": {
52+
token: "test-token",
3553
host: "ghcr.iofoo",
3654
expIs: false,
3755
},
@@ -40,6 +58,9 @@ func TestIsHost(t *testing.T) {
4058
handler := new(Client)
4159
for name, test := range tests {
4260
t.Run(name, func(t *testing.T) {
61+
if test.token != "" {
62+
handler.opts.Token = test.token
63+
}
4364
if isHost := handler.IsHost(test.host); isHost != test.expIs {
4465
t.Errorf("%s: unexpected IsHost, exp=%t got=%t",
4566
test.host, test.expIs, isHost)
@@ -78,6 +99,7 @@ func TestRepoImage(t *testing.T) {
7899
handler := new(Client)
79100
for name, test := range tests {
80101
t.Run(name, func(t *testing.T) {
102+
handler.opts.Token = "fake-token"
81103
repo, image := handler.RepoImageFromPath(test.path)
82104
if repo != test.expRepo && image != test.expImage {
83105
t.Errorf("%s: unexpected repo/image, exp=%s/%s got=%s/%s",

0 commit comments

Comments
 (0)