CVE-2024-34084 (Go, published 2024-05-07T15:15Z)
Description: value: Minder's `HandleGithubWebhook` is susceptible to a denial of service attack from an untrusted HTTP request. The vulnerability exists before the request has been validated, and as such the request is still untrusted at the point of failure. This allows an attacker with the ability to send requests to `HandleGithubWebhook` to crash the Minder controlplane and deny other users from using it. This vulnerability is fixed in 0.0.48.
Severity: nan (CVSS v3 base score: )
Weakness: NVD-CWE-noinfo \u2014 Insufficient Information
There is insufficient information about the issue to classify it; details are unkown or unspecified.
Repository: https://github.com/stacklok/minder
Files changed:
internal/controlplane/handlers_githubwebhooks_test.go
internal/controlplane/server.go
Commit message: Merge pull request from GHSA-9c5w-9q3f-3hv7

Signed-off-by: Juan Antonio Osorio <ozz@stacklok.com>

Vulnerable code:
	mux.Handle("/", s.handlerWithHTTPMiddleware(gwmux))
	mux.Handle("/api/v1/webhook/", mw(s.HandleGitHubWebHook()))
	mux.Handle("/api/v1/ghapp/", mw(s.HandleGitHubAppWebhook()))
	mux.Handle("/api/v1/gh-marketplace/", mw(s.NoopWebhookHandler()))

Fixed code:
	"io"
	"strings"
func (s *UnitTestSuite) TestHandleWebHookWithTooLargeRequest() {
	t := s.T()
	t.Parallel()

	ctrl := gomock.NewController(t)
	defer ctrl.Finish()

	mockStore := mockdb.NewMockStore(ctrl)
	srv, evt := newDefaultServer(t, mockStore, nil)
	defer evt.Close()

	pq := testqueue.NewPassthroughQueue(t)
	queued := pq.GetQueue()

	evt.Register(events.TopicQueueEntityEvaluate, pq.Pass)

	go func() {
		err := evt.Run(context.Background())
		require.NoError(t, err, "failed to run eventer")
	}()

	<-evt.Running()

	hook := withMaxSizeMiddleware(srv.HandleGitHubWebHook())
	port, err := rand.GetRandomPort()
	if err != nil {
		t.Fatal(err)
	}
	addr := fmt.Sprintf("localhost:%d", port)
	server := &http.Server{
		Addr:              fmt.Sprintf(":%d", port),
		Handler:           hook,
		ReadHeaderTimeout: 1 * time.Second,
	}
	go server.ListenAndServe()

	event := github.PackageEvent{
		Action: github.String("published"),
		Repo: &github.Repository{
			ID:   github.Int64(12345),
			Name: github.String("stacklok/minder"),
		},
		Org: &github.Organization{
			Login: github.String("stacklok"),
		},
	}
	packageJson, err := json.Marshal(event)
	require.NoError(t, err, "failed to marshal package event")

	maliciousBody := strings.NewReader(strings.Repeat("1337", 1000000000))
	maliciousBodyReader := io.MultiReader(maliciousBody, maliciousBody, maliciousBody, maliciousBody, maliciousBody)
	_ = packageJson

	client := &http.Client{}
	req, err := http.NewRequest("POST", fmt.Sprintf("http://%s", addr), maliciousBodyReader)
	require.NoError(t, err, "failed to create request")

	req.Header.Add("X-GitHub-Event", "meta")
	req.Header.Add("X-GitHub-Delivery", "12345")
	req.Header.Add("Content-Type", "application/json")
	resp, err := httpDoWithRetry(client, req)
	require.NoError(t, err, "failed to make request")
	// We expect OK since we don't want to leak information about registered repositories
	require.Equal(t, http.StatusBadRequest, resp.StatusCode, "unexpected status code")
	assert.Len(t, queued, 0)
}


	// RequestBodyMaxBytes is the maximum number of bytes that can be read from a request body
	// We limit to 2MB for now
	RequestBodyMaxBytes int64 = 2 << 20
	mux.Handle("/", withMaxSizeMiddleware(s.handlerWithHTTPMiddleware(gwmux)))
	mux.Handle("/api/v1/webhook/", mw(withMaxSizeMiddleware(s.HandleGitHubWebHook())))
	mux.Handle("/api/v1/ghapp/", mw(withMaxSizeMiddleware(s.HandleGitHubAppWebhook())))
	mux.Handle("/api/v1/gh-marketplace/", mw(withMaxSizeMiddleware(s.NoopWebhookHandler())))

func withMaxSizeMiddleware(h http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		r.Body = http.MaxBytesReader(w, r.Body, RequestBodyMaxBytes)
		h.ServeHTTP(w, r)
	})
}