Skip to content

Commit 74ba874

Browse files
authored
Merge pull request from GHSA-75r6-6jg8-pfcq
This change adds some logic to limit the size of the HTTP responses allowed when performing OIDC discovery on a new provider. Prior to this, Octo STS could have been manipulated into reading arbitrary amounts of data from the provided issuer endpoint. Signed-off-by: Matt Moore <[email protected]>
1 parent 1fc549c commit 74ba874

File tree

3 files changed

+113
-0
lines changed

3 files changed

+113
-0
lines changed

pkg/maxsize/maxsize.go

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
// Copyright 2024 Chainguard, Inc.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
package maxsize
5+
6+
import (
7+
"io"
8+
"net/http"
9+
)
10+
11+
// NewRoundTripper creates a new http.RoundTripper that wraps the given
12+
// http.RoundTripper and limits the size of the response body to maxSize bytes.
13+
func NewRoundTripper(maxSize int64, inner http.RoundTripper) http.RoundTripper {
14+
return &ms{
15+
base: inner,
16+
maxBodySize: maxSize,
17+
}
18+
}
19+
20+
type ms struct {
21+
base http.RoundTripper // The underlying RoundTripper
22+
maxBodySize int64 // Maximum allowed response body size in bytes
23+
}
24+
25+
// RoundTrip implements http.RoundTripper
26+
func (rt *ms) RoundTrip(req *http.Request) (*http.Response, error) {
27+
resp, err := rt.base.RoundTrip(req)
28+
if err != nil {
29+
return nil, err
30+
}
31+
32+
resp.Body = &lr{
33+
LimitedReader: io.LimitedReader{
34+
R: resp.Body,
35+
N: rt.maxBodySize,
36+
},
37+
close: resp.Body.Close,
38+
}
39+
return resp, nil
40+
}
41+
42+
type lr struct {
43+
io.LimitedReader
44+
close func() error
45+
}
46+
47+
// Close implements io.Closer
48+
func (r *lr) Close() error {
49+
return r.close()
50+
}

pkg/maxsize/maxsize_test.go

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
// Copyright 2024 Chainguard, Inc.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
package maxsize
5+
6+
import (
7+
"context"
8+
"net/http"
9+
"testing"
10+
11+
"github.com/coreos/go-oidc/v3/oidc"
12+
)
13+
14+
func TestCompile(t *testing.T) {
15+
tests := []struct {
16+
name string
17+
size int64
18+
wantErr bool
19+
}{{
20+
name: "large size",
21+
size: 1000000, // 1M bytes
22+
wantErr: false,
23+
}, {
24+
name: "medium size",
25+
size: 10000, // 10000 bytes
26+
wantErr: false,
27+
}, {
28+
name: "tiny size",
29+
size: 10, // 10 bytes
30+
wantErr: true,
31+
}}
32+
33+
for _, tt := range tests {
34+
t.Run(tt.name, func(t *testing.T) {
35+
ctx := oidc.ClientContext(context.Background(), &http.Client{
36+
Transport: NewRoundTripper(tt.size, http.DefaultTransport),
37+
})
38+
for _, issuer := range []string{
39+
"https://accounts.google.com",
40+
"https://token.actions.githubusercontent.com",
41+
"https://issuer.enforce.dev",
42+
} {
43+
if _, err := oidc.NewProvider(ctx, issuer); (err != nil) != tt.wantErr {
44+
t.Errorf("constructing %q provider: %v", issuer, err)
45+
}
46+
}
47+
})
48+
}
49+
}

pkg/provider/provider.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,22 @@ package provider
66
import (
77
"context"
88
"fmt"
9+
"net/http"
910

1011
"github.com/chainguard-dev/clog"
12+
"github.com/chainguard-dev/terraform-infra-common/pkg/httpmetrics"
1113
"github.com/coreos/go-oidc/v3/oidc"
1214
lru "github.com/hashicorp/golang-lru/v2"
15+
"github.com/octo-sts/app/pkg/maxsize"
1316
)
1417

18+
// MaximumResponseSize is the maximum size of allowed responses from
19+
// OIDC providers. Some anecdata
20+
// - Google: needs around 1KiB
21+
// - GitHub: needs around 5KiB
22+
// - Chainguard: needs around 2KiB
23+
const MaximumResponseSize = 100 * 1024 // 100KiB
24+
1525
var (
1626
// providers is an LRU cache of recently used providers.
1727
providers, _ = lru.New2Q[string, *oidc.Provider](100)
@@ -25,6 +35,10 @@ func Get(ctx context.Context, issuer string) (provider *oidc.Provider, err error
2535
return v, nil
2636
}
2737

38+
ctx = oidc.ClientContext(ctx, &http.Client{
39+
Transport: maxsize.NewRoundTripper(MaximumResponseSize, httpmetrics.Transport),
40+
})
41+
2842
// Verify the token before we trust anything about it.
2943
provider, err = oidc.NewProvider(ctx, issuer)
3044
if err != nil {

0 commit comments

Comments
 (0)