Skip to content

CorsUtils.isCorsRequest throws unhandled IllegalArgumentException and returns 500 Internal Server Error on malfomed Origin header #33682

Closed
@sfc-gh-jzana

Description

@sfc-gh-jzana

Affects: 6.1.13


if a client sends a malformed origin header in a CORS request to a spring boot application that looks like this:

 curl 'http://localhost/sample \
  -X 'OPTIONS' \
  -H 'Origin: https://*@:;' \

The following exception will be thrown:

j.l.IllegalArgumentException: [https://*@:;] is not a valid HTTP URL
	at o.s.w.u.UriComponentsBuilder.checkSchemeAndHost(UriComponentsBuilder.java:309)
	at o.s.w.u.UriComponentsBuilder.fromOriginHeader(UriComponentsBuilder.java:371)
	at o.s.w.cors.CorsUtils.isCorsRequest(CorsUtils.java:46)
	at o.s.w.c.DefaultCorsProcessor.processRequest(DefaultCorsProcessor.java:86)

This exception is not handled, and bubbles out as a 500 Internal Server Error.

I would expect that the framework would handle the invalid input and reject the request with a 403 Forbidden with message "invalid cors request", like it does for many other kinds of invalid input.

The only workaround I have found is to register a custom corsFilter bean, with a custom CorsProcessor that handles the exception and rejects it.

Here's a unit test that fails due to this issue:

package testing;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsProcessor;
import org.springframework.web.cors.DefaultCorsProcessor;

import java.io.IOException;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;

class SafeCorsProcessorTest {
    private final CorsConfiguration corsConfiguration = new CorsConfiguration();
    private final CorsProcessor processor = new DefaultCorsProcessor();
    private final MockHttpServletResponse response = new MockHttpServletResponse();

    @BeforeEach
    void setUp() {
        response.reset();
    }

    @Test
    void processRequest() throws IOException {
        // Given a valid OPTIONS request.
        var request  = new MockHttpServletRequest();
        request.addHeader(HttpHeaders.ORIGIN, "http://localhost");
        request.setMethod(HttpMethod.OPTIONS.name());

        // When processRequest is called
        var result = processor.processRequest(corsConfiguration, request, response);

        // Then the result is true and the status code is 200 OK.
        assertTrue(result);
        assertEquals(HttpStatus.OK.value(), response.getStatus());

    }

    @Test
    void processRequestInvalidOrigin() throws IOException {
        // Given an OPTIONS request with invalid origin header.
        var request = new MockHttpServletRequest();
        request.addHeader(HttpHeaders.ORIGIN, "https://*@:;");
        request.setMethod(HttpMethod.OPTIONS.name());

        // WHen processRequest is called
        var result = processor.processRequest(corsConfiguration, request, response);

        // Then the result is false and status code is 403 Forbidden,
        assertFalse(result);
        assertEquals(HttpStatus.FORBIDDEN.value(), response.getStatus());
    }
}

Metadata

Metadata

Assignees

Labels

in: webIssues in web modules (web, webmvc, webflux, websocket)type: enhancementA general enhancement

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions