Browse Source

SONAR-21889 Fix SSF-539

tags/10.5.0.89998
Aurelien Poscia 1 month ago
parent
commit
545155db89

+ 9
- 3
server/sonar-webserver-auth/src/main/java/org/sonar/server/authentication/OAuth2AuthenticationParametersImpl.java View File

import com.google.gson.GsonBuilder; import com.google.gson.GsonBuilder;
import com.google.gson.reflect.TypeToken; import com.google.gson.reflect.TypeToken;
import java.io.UnsupportedEncodingException; import java.io.UnsupportedEncodingException;
import java.nio.file.Path;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.Optional; import java.util.Optional;
return empty(); return empty();
} }


String sanitizedUrl = url.trim();
boolean isValidUrl = VALID_RETURN_TO.matcher(sanitizedUrl).matches();
String trimmedUrl = url.trim();
boolean isValidUrl = VALID_RETURN_TO.matcher(trimmedUrl).matches();
if (!isValidUrl) { if (!isValidUrl) {
return empty(); return empty();
} }


return Optional.of(sanitizedUrl);
Path sanitizedPath = escapePathTraversalChars(trimmedUrl);
return Optional.of(sanitizedPath.toString());
}

private static Path escapePathTraversalChars(String sanitizedUrl) {
return Path.of(sanitizedUrl).normalize();
} }
} }

+ 33
- 4
server/sonar-webserver-auth/src/test/java/org/sonar/server/authentication/OAuth2AuthenticationParametersImplTest.java View File



import com.tngtech.java.junit.dataprovider.DataProvider; import com.tngtech.java.junit.dataprovider.DataProvider;
import com.tngtech.java.junit.dataprovider.DataProviderRunner; import com.tngtech.java.junit.dataprovider.DataProviderRunner;
import com.tngtech.java.junit.dataprovider.UseDataProvider;
import java.util.Optional; import java.util.Optional;
import javax.annotation.Nullable;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;


@Test @Test
public void get_return_to_parameter() { public void get_return_to_parameter() {
when(request.getCookies()).thenReturn(new Cookie[] {wrapCookie(AUTHENTICATION_COOKIE_NAME, "{\"return_to\":\"/admin/settings\"}")});
when(request.getCookies()).thenReturn(new Cookie[]{wrapCookie(AUTHENTICATION_COOKIE_NAME, "{\"return_to\":\"/admin/settings\"}")});


Optional<String> redirection = underTest.getReturnTo(request); Optional<String> redirection = underTest.getReturnTo(request);




@Test @Test
public void get_return_to_is_empty_when_no_cookie() { public void get_return_to_is_empty_when_no_cookie() {
when(request.getCookies()).thenReturn(new Cookie[] {});
when(request.getCookies()).thenReturn(new Cookie[]{});


Optional<String> redirection = underTest.getReturnTo(request); Optional<String> redirection = underTest.getReturnTo(request);




@Test @Test
public void get_return_to_is_empty_when_no_value() { public void get_return_to_is_empty_when_no_value() {
when(request.getCookies()).thenReturn(new Cookie[] {wrapCookie(AUTHENTICATION_COOKIE_NAME, "{}")});
when(request.getCookies()).thenReturn(new Cookie[]{wrapCookie(AUTHENTICATION_COOKIE_NAME, "{}")});


Optional<String> redirection = underTest.getReturnTo(request); Optional<String> redirection = underTest.getReturnTo(request);




@Test @Test
public void delete() { public void delete() {
when(request.getCookies()).thenReturn(new Cookie[] {wrapCookie(AUTHENTICATION_COOKIE_NAME, "{\"return_to\":\"/admin/settings\"}")});
when(request.getCookies()).thenReturn(new Cookie[]{wrapCookie(AUTHENTICATION_COOKIE_NAME, "{\"return_to\":\"/admin/settings\"}")});


underTest.delete(request, response); underTest.delete(request, response);


assertThat(updatedCookie.getMaxAge()).isZero(); assertThat(updatedCookie.getMaxAge()).isZero();
} }


@DataProvider
public static Object[][] payloadToSanitizeAndExpectedOutcome() {
return new Object[][]{
{generatePath("/admin/settings"), "/admin/settings"},
{generatePath("/admin/../../settings"), "/settings"},
{generatePath("/admin/../settings"), "/settings"},
{generatePath("/admin/settings/.."), "/admin"},
{generatePath("/admin/..%2fsettings/"), "/settings"},
{generatePath("/admin/%2e%2e%2fsettings/"), "/settings"},
{generatePath("../admin/settings"), null},
};
}

private static String generatePath(String returnTo) {
return "{\"return_to\":\"" + returnTo + "\"}";
}

@Test
@UseDataProvider("payloadToSanitizeAndExpectedOutcome")
public void getReturnTo_whenContainingPathTraversalCharacters_sanitizeThem(String payload, @Nullable String expectedSanitizedUrl) {
when(request.getCookies()).thenReturn(new Cookie[]{wrapCookie(AUTHENTICATION_COOKIE_NAME, payload)});

Optional<String> redirection = underTest.getReturnTo(request);

assertThat(redirection).isEqualTo(Optional.ofNullable(expectedSanitizedUrl));
}

private JavaxHttpRequest.JavaxCookie wrapCookie(String name, String value) { private JavaxHttpRequest.JavaxCookie wrapCookie(String name, String value) {
return new JavaxHttpRequest.JavaxCookie(new javax.servlet.http.Cookie(name, value)); return new JavaxHttpRequest.JavaxCookie(new javax.servlet.http.Cookie(name, value));
} }

Loading…
Cancel
Save