@@ -1400,6 +1400,36 @@ func TestDo_preservesResponseInHTTPError(t *testing.T) {
14001400 }
14011401}
14021402
1403+ // TestDo_AcceptedError_LargeBodyTruncated verifies that when the API returns a
1404+ // 202 Accepted with a body larger than maxErrorBodySize, the client reads at
1405+ // most maxErrorBodySize bytes into AcceptedError.Raw and does not allocate
1406+ // unbounded memory.
1407+ func TestDo_AcceptedError_LargeBodyTruncated (t * testing.T ) {
1408+ t .Parallel ()
1409+ client , mux , _ := setup (t )
1410+
1411+ // Serve a 202 response whose body exceeds the cap by one byte.
1412+ mux .HandleFunc ("/" , func (w http.ResponseWriter , _ * http.Request ) {
1413+ w .WriteHeader (http .StatusAccepted )
1414+ fmt .Fprint (w , strings .Repeat ("x" , maxErrorBodySize + 1 ))
1415+ })
1416+
1417+ req , _ := client .NewRequest (t .Context (), "GET" , "." , nil )
1418+ _ , err := client .Do (req , nil )
1419+ if err == nil {
1420+ t .Fatal ("Expected AcceptedError, got nil" )
1421+ }
1422+
1423+ var aerr * AcceptedError
1424+ if ! errors .As (err , & aerr ) {
1425+ t .Fatalf ("Expected *AcceptedError, got %T: %v" , err , err )
1426+ }
1427+
1428+ if got , want := len (aerr .Raw ), maxErrorBodySize ; got != want {
1429+ t .Errorf ("AcceptedError.Raw length = %v, want %v (maxErrorBodySize)" , got , want )
1430+ }
1431+ }
1432+
14031433// Test that an error caused by the internal http client's Do() function
14041434// does not leak the client secret.
14051435func TestDo_sanitizeURL (t * testing.T ) {
@@ -2982,6 +3012,38 @@ func TestCheckResponse_unexpectedErrorStructure(t *testing.T) {
29823012 }
29833013}
29843014
3015+ // TestCheckResponse_LargeBodyTruncated verifies that CheckResponse reads at
3016+ // most maxErrorBodySize bytes from an error response body, so that a
3017+ // malicious or buggy server cannot cause the client to allocate unbounded
3018+ // memory.
3019+ func TestCheckResponse_LargeBodyTruncated (t * testing.T ) {
3020+ t .Parallel ()
3021+ // Build a body that is one byte larger than the cap.
3022+ body := strings .Repeat ("x" , maxErrorBodySize + 1 )
3023+ res := & http.Response {
3024+ Request : & http.Request {},
3025+ StatusCode : http .StatusBadRequest ,
3026+ Body : io .NopCloser (strings .NewReader (body )),
3027+ }
3028+
3029+ // CheckResponse should not return an error from the read itself; the HTTP
3030+ // error status is the expected error.
3031+ if err := CheckResponse (res ); err == nil {
3032+ t .Fatal ("Expected error from CheckResponse, got nil" )
3033+ }
3034+
3035+ // After CheckResponse, the body is restored with the (truncated) bytes that
3036+ // were actually read. Verify the restored body is exactly maxErrorBodySize
3037+ // bytes — not the full maxErrorBodySize+1 that the server sent.
3038+ restored , err := io .ReadAll (res .Body )
3039+ if err != nil {
3040+ t .Fatalf ("io.ReadAll on restored body: %v" , err )
3041+ }
3042+ if got , want := len (restored ), maxErrorBodySize ; got != want {
3043+ t .Errorf ("restored body length = %v, want %v (maxErrorBodySize)" , got , want )
3044+ }
3045+ }
3046+
29853047func TestParseBooleanResponse_true (t * testing.T ) {
29863048 t .Parallel ()
29873049 result , err := parseBoolResponse (nil )
0 commit comments