mirror of
https://github.com/google/nomulus.git
synced 2025-05-28 12:31:08 +02:00
Remove unnecessary SecureRandom from UrlFetchUtils
We're only using it for generating multiparty boundaries, and there's no real need for the random boundary values to be cryptographically secure. The point of the randomness is just to make collisions with content in the payload sufficiently unlikely. The app itself controls the payload contents, and while it might be derived from user-submitted content, in practice it would be nearly infeasible to get the payload to contain arbitrary boundary values even if the RNG-produced boundaries could be determined in advance. To further insulate against this, I've increased the boundary size (from 40 bits to 192) and added an actual check that the boundary isn't present in the input data, so that in the extremely unlikely event of a collision, we fail rather than producing an invalid multipart request. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=142784289
This commit is contained in:
parent
9d9c527917
commit
0405a427f1
2 changed files with 54 additions and 51 deletions
|
@ -17,27 +17,30 @@ package google.registry.util;
|
|||
import static com.google.common.net.HttpHeaders.CONTENT_LENGTH;
|
||||
import static com.google.common.net.HttpHeaders.CONTENT_TYPE;
|
||||
import static com.google.common.net.MediaType.CSV_UTF_8;
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static google.registry.util.UrlFetchUtils.setPayloadMultipart;
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
import static org.mockito.Matchers.any;
|
||||
import static org.mockito.Matchers.argThat;
|
||||
import static org.mockito.Mockito.doAnswer;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.verifyNoMoreInteractions;
|
||||
|
||||
import com.google.appengine.api.urlfetch.HTTPHeader;
|
||||
import com.google.appengine.api.urlfetch.HTTPRequest;
|
||||
import google.registry.testing.AppEngineRule;
|
||||
import google.registry.testing.ExceptionRule;
|
||||
import google.registry.testing.InjectRule;
|
||||
import java.security.SecureRandom;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.JUnit4;
|
||||
import org.mockito.ArgumentMatcher;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.invocation.InvocationOnMock;
|
||||
import org.mockito.stubbing.Answer;
|
||||
|
||||
|
@ -49,55 +52,57 @@ public class UrlFetchUtilsTest {
|
|||
public final AppEngineRule appEngine = AppEngineRule.builder()
|
||||
.build();
|
||||
|
||||
@Rule
|
||||
public final ExceptionRule thrown = new ExceptionRule();
|
||||
|
||||
@Rule
|
||||
public final InjectRule inject = new InjectRule();
|
||||
|
||||
@Before
|
||||
public void setupRandomZeroes() throws Exception {
|
||||
SecureRandom secureRandom = mock(SecureRandom.class);
|
||||
inject.setStaticField(UrlFetchUtils.class, "secureRandom", secureRandom);
|
||||
Random random = mock(Random.class);
|
||||
inject.setStaticField(UrlFetchUtils.class, "random", random);
|
||||
doAnswer(new Answer<Void>() {
|
||||
@Override
|
||||
public Void answer(InvocationOnMock info) throws Throwable {
|
||||
byte[] bytes = (byte[]) info.getArguments()[0];
|
||||
Arrays.fill(bytes, (byte) 0);
|
||||
Arrays.fill((byte[]) info.getArguments()[0], (byte) 0);
|
||||
return null;
|
||||
}}).when(secureRandom).nextBytes(any(byte[].class));
|
||||
}}).when(random).nextBytes(any(byte[].class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetPayloadMultipart() throws Exception {
|
||||
String payload = "--------------------------------AAAAAAAA\r\n"
|
||||
HTTPRequest request = mock(HTTPRequest.class);
|
||||
setPayloadMultipart(
|
||||
request, "lol", "cat", CSV_UTF_8, "The nice people at the store say hello. ヘ(◕。◕ヘ)");
|
||||
ArgumentCaptor<HTTPHeader> headerCaptor = ArgumentCaptor.forClass(HTTPHeader.class);
|
||||
verify(request, times(2)).addHeader(headerCaptor.capture());
|
||||
List<HTTPHeader> addedHeaders = headerCaptor.getAllValues();
|
||||
assertThat(addedHeaders.get(0).getName()).isEqualTo(CONTENT_TYPE);
|
||||
assertThat(addedHeaders.get(0).getValue())
|
||||
.isEqualTo(
|
||||
"multipart/form-data; "
|
||||
+ "boundary=\"------------------------------AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\"");
|
||||
assertThat(addedHeaders.get(1).getName()).isEqualTo(CONTENT_LENGTH);
|
||||
assertThat(addedHeaders.get(1).getValue()).isEqualTo("292");
|
||||
String payload = "--------------------------------AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\r\n"
|
||||
+ "Content-Disposition: form-data; name=\"lol\"; filename=\"cat\"\r\n"
|
||||
+ "Content-Type: text/csv; charset=utf-8\r\n"
|
||||
+ "\r\n"
|
||||
+ "The nice people at the store say hello. ヘ(◕。◕ヘ)\r\n"
|
||||
+ "--------------------------------AAAAAAAA--";
|
||||
HTTPRequest request = mock(HTTPRequest.class);
|
||||
setPayloadMultipart(
|
||||
request, "lol", "cat", CSV_UTF_8, "The nice people at the store say hello. ヘ(◕。◕ヘ)");
|
||||
verify(request).addHeader(argThat(new HTTPHeaderMatcher(
|
||||
CONTENT_TYPE, "multipart/form-data; boundary=------------------------------AAAAAAAA")));
|
||||
verify(request).addHeader(argThat(new HTTPHeaderMatcher(CONTENT_LENGTH, "244")));
|
||||
+ "--------------------------------AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA--";
|
||||
verify(request).setPayload(payload.getBytes(UTF_8));
|
||||
verifyNoMoreInteractions(request);
|
||||
}
|
||||
|
||||
/** Mockito matcher for {@link HTTPHeader}. */
|
||||
public static class HTTPHeaderMatcher extends ArgumentMatcher<HTTPHeader> {
|
||||
private final String name;
|
||||
private final String value;
|
||||
|
||||
public HTTPHeaderMatcher(String name, String value) {
|
||||
this.name = name;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean matches(Object arg) {
|
||||
HTTPHeader header = (HTTPHeader) arg;
|
||||
return name.equals(header.getName())
|
||||
&& value.equals(header.getValue());
|
||||
}
|
||||
@Test
|
||||
public void testSetPayloadMultipart_boundaryInPayload() throws Exception {
|
||||
HTTPRequest request = mock(HTTPRequest.class);
|
||||
String payload = "I screamed------------------------------AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHHH";
|
||||
thrown.expect(
|
||||
IllegalStateException.class,
|
||||
"Multipart data contains autogenerated boundary: "
|
||||
+ "------------------------------AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA");
|
||||
setPayloadMultipart(request, "lol", "cat", CSV_UTF_8, payload);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue