CVE-2024-34079 (published 2024-05-14T15:38Z)

octo-sts is a GitHub App that acts like a Security Token Service (STS) for the Github API. This vulnerability can spike the resource utilization of the STS service, and combined with a significant traffic volume could potentially lead to a denial of service. This vulnerability is fixed in 0.1.0


Weakness information:
cwe_id: NVD-CWE-noinfo
cwe_name: Insufficient Information
description: There is insufficient information about the issue to classify it; details are unkown or unspecified.
extended_description: Insufficient Information

Fix information:
repo_url: https://github.com/octo-sts/app
commit_details: repo_url: https://github.com/octo-sts/app
author: Matt Moore
author_date: 2024-05-10 11:49:11-07:00
msg: 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 <mattmoor@chainguard.dev>
repository: repo_url: https://github.com/octo-sts/app
repo_name: octo-sts/app
description: A GitHub App that acts like a Security Token Service (STS) for the Github API
repo_language: Go
owner: octo-sts
file_changes: filename: maxsize.go
change_type: ModificationType.ADD
diff: @@ -0,0 +1,50 @@
+// Copyright 2024 Chainguard, Inc.
+// SPDX-License-Identifier: Apache-2.0
+
+package maxsize
+
+import (
+	"io"
+	"net/http"
+)
+
+// NewRoundTripper creates a new http.RoundTripper that wraps the given
+// http.RoundTripper and limits the size of the response body to maxSize bytes.
+func NewRoundTripper(maxSize int64, inner http.RoundTripper) http.RoundTripper {
+	return &ms{
+		base:        inner,
+		maxBodySize: maxSize,
+	}
+}
+
+type ms struct {
+	base        http.RoundTripper // The underlying RoundTripper
+	maxBodySize int64             // Maximum allowed response body size in bytes
+}
+
+// RoundTrip implements http.RoundTripper
+func (rt *ms) RoundTrip(req *http.Request) (*http.Response, error) {
+	resp, err := rt.base.RoundTrip(req)
+	if err != nil {
+		return nil, err
+	}
+
+	resp.Body = &lr{
+		LimitedReader: io.LimitedReader{
+			R: resp.Body,
+			N: rt.maxBodySize,
+		},
+		close: resp.Body.Close,
+	}
+	return resp, nil
+}
+
+type lr struct {
+	io.LimitedReader
+	close func() error
+}
+
+// Close implements io.Closer
+func (r *lr) Close() error {
+	return r.close()
+}

programming_language: Go
filename: maxsize_test.go
change_type: ModificationType.ADD
diff: @@ -0,0 +1,49 @@
+// Copyright 2024 Chainguard, Inc.
+// SPDX-License-Identifier: Apache-2.0
+
+package maxsize
+
+import (
+	"context"
+	"net/http"
+	"testing"
+
+	"github.com/coreos/go-oidc/v3/oidc"
+)
+
+func TestCompile(t *testing.T) {
+	tests := []struct {
+		name    string
+		size    int64
+		wantErr bool
+	}{{
+		name:    "large size",
+		size:    1000000, // 1M bytes
+		wantErr: false,
+	}, {
+		name:    "medium size",
+		size:    10000, // 10000 bytes
+		wantErr: false,
+	}, {
+		name:    "tiny size",
+		size:    10, // 10 bytes
+		wantErr: true,
+	}}
+
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			ctx := oidc.ClientContext(context.Background(), &http.Client{
+				Transport: NewRoundTripper(tt.size, http.DefaultTransport),
+			})
+			for _, issuer := range []string{
+				"https://accounts.google.com",
+				"https://token.actions.githubusercontent.com",
+				"https://issuer.enforce.dev",
+			} {
+				if _, err := oidc.NewProvider(ctx, issuer); (err != nil) != tt.wantErr {
+					t.Errorf("constructing %q provider: %v", issuer, err)
+				}
+			}
+		})
+	}
+}

programming_language: Go
filename: provider.go
change_type: ModificationType.MODIFY
diff: @@ -6,12 +6,22 @@ package provider
 import (
 	"context"
 	"fmt"
+	"net/http"
 
 	"github.com/chainguard-dev/clog"
+	"github.com/chainguard-dev/terraform-infra-common/pkg/httpmetrics"
 	"github.com/coreos/go-oidc/v3/oidc"
 	lru "github.com/hashicorp/golang-lru/v2"
+	"github.com/octo-sts/app/pkg/maxsize"
 )
 
+// MaximumResponseSize is the maximum size of allowed responses from
+// OIDC providers.  Some anecdata
+//   - Google: needs around 1KiB
+//   - GitHub: needs around 5KiB
+//   - Chainguard: needs around 2KiB
+const MaximumResponseSize = 100 * 1024 // 100KiB
+
 var (
 	// providers is an LRU cache of recently used providers.
 	providers, _ = lru.New2Q[string, *oidc.Provider](100)
@@ -25,6 +35,10 @@ func Get(ctx context.Context, issuer string) (provider *oidc.Provider, err error
 		return v, nil
 	}
 
+	ctx = oidc.ClientContext(ctx, &http.Client{
+		Transport: maxsize.NewRoundTripper(MaximumResponseSize, httpmetrics.Transport),
+	})
+
 	// Verify the token before we trust anything about it.
 	provider, err = oidc.NewProvider(ctx, issuer)
 	if err != nil {

programming_language: Go