Add an "excessively large object" safeguard.

This commit is contained in:
Jon Chambers 2021-05-18 14:18:39 -04:00 committed by Jon Chambers
parent fbaf4a09e2
commit 28e3b23e8c
4 changed files with 67 additions and 3 deletions

View File

@ -25,6 +25,9 @@ public class TorExitNodeConfiguration {
@NotBlank
private String objectKey;
@JsonProperty
private long maxSize = 16 * 1024 * 1024;
@JsonProperty
private Duration refreshInterval = Duration.ofMinutes(5);
@ -45,6 +48,15 @@ public class TorExitNodeConfiguration {
return objectKey;
}
public long getMaxSize() {
return maxSize;
}
@VisibleForTesting
public void setMaxSize(final long maxSize) {
this.maxSize = maxSize;
}
public Duration getRefreshInterval() {
return refreshInterval;
}

View File

@ -29,6 +29,7 @@ public class S3ObjectMonitor implements Managed {
private final String s3Bucket;
private final String objectKey;
private final long maxObjectSize;
private final ScheduledExecutorService refreshExecutorService;
private final Duration refreshInterval;
@ -46,6 +47,7 @@ public class S3ObjectMonitor implements Managed {
final String s3Region,
final String s3Bucket,
final String objectKey,
final long maxObjectSize,
final ScheduledExecutorService refreshExecutorService,
final Duration refreshInterval,
final Consumer<S3Object> changeListener) {
@ -56,6 +58,7 @@ public class S3ObjectMonitor implements Managed {
.build(),
s3Bucket,
objectKey,
maxObjectSize,
refreshExecutorService,
refreshInterval,
changeListener);
@ -66,6 +69,7 @@ public class S3ObjectMonitor implements Managed {
final AmazonS3 s3Client,
final String s3Bucket,
final String objectKey,
final long maxObjectSize,
final ScheduledExecutorService refreshExecutorService,
final Duration refreshInterval,
final Consumer<S3Object> changeListener) {
@ -73,6 +77,7 @@ public class S3ObjectMonitor implements Managed {
this.s3Client = s3Client;
this.s3Bucket = s3Bucket;
this.objectKey = objectKey;
this.maxObjectSize = maxObjectSize;
this.refreshExecutorService = refreshExecutorService;
this.refreshInterval = refreshInterval;
@ -111,7 +116,17 @@ public class S3ObjectMonitor implements Managed {
final String refreshedETag = objectMetadata.getETag();
if (!StringUtils.equals(initialETag, refreshedETag) && lastETag.compareAndSet(initialETag, refreshedETag)) {
changeListener.accept(s3Client.getObject(s3Bucket, objectKey));
final S3Object s3Object = s3Client.getObject(s3Bucket, objectKey);
log.info("Object at s3://{}/{} has changed; new eTag is {} and object size is {} bytes",
s3Bucket, objectKey, s3Object.getObjectMetadata().getETag(), s3Object.getObjectMetadata().getContentLength());
if (s3Object.getObjectMetadata().getContentLength() <= maxObjectSize) {
changeListener.accept(s3Object);
} else {
log.warn("Object at s3://{}/{} has a size of {} bytes, which exceeds the maximum allowed size of {} bytes",
s3Bucket, objectKey, s3Object.getObjectMetadata().getContentLength(), maxObjectSize);
}
}
} catch (final Exception e) {
log.warn("Failed to refresh monitored object", e);

View File

@ -49,6 +49,7 @@ public class TorExitNodeManager implements Managed {
configuration.getS3Region(),
configuration.getS3Bucket(),
configuration.getObjectKey(),
configuration.getMaxSize(),
scheduledExecutorService,
configuration.getRefreshInterval(),
this::handleExitListChanged);

View File

@ -10,8 +10,9 @@ import java.util.UUID;
import java.util.concurrent.ScheduledExecutorService;
import java.util.function.Consumer;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@ -32,7 +33,9 @@ class S3ObjectMonitorTest {
final S3ObjectMonitor objectMonitor = new S3ObjectMonitor(
s3Client,
bucket,
objectKey, mock(ScheduledExecutorService.class),
objectKey,
16 * 1024 * 1024,
mock(ScheduledExecutorService.class),
Duration.ofMinutes(1),
listener);
@ -46,4 +49,37 @@ class S3ObjectMonitorTest {
verify(listener).accept(s3Object);
}
@Test
void refreshOversizedObject() {
final AmazonS3 s3Client = mock(AmazonS3.class);
final ObjectMetadata metadata = mock(ObjectMetadata.class);
final S3Object s3Object = mock(S3Object.class);
final String bucket = "s3bucket";
final String objectKey = "greatest-smooth-jazz-hits-of-all-time.zip";
final long maxObjectSize = 16 * 1024 * 1024;
//noinspection unchecked
final Consumer<S3Object> listener = mock(Consumer.class);
final S3ObjectMonitor objectMonitor = new S3ObjectMonitor(
s3Client,
bucket,
objectKey,
maxObjectSize,
mock(ScheduledExecutorService.class),
Duration.ofMinutes(1),
listener);
when(metadata.getETag()).thenReturn(UUID.randomUUID().toString());
when(metadata.getContentLength()).thenReturn(maxObjectSize + 1);
when(s3Object.getObjectMetadata()).thenReturn(metadata);
when(s3Client.getObjectMetadata(bucket, objectKey)).thenReturn(metadata);
when(s3Client.getObject(bucket, objectKey)).thenReturn(s3Object);
objectMonitor.refresh();
verify(listener, never()).accept(any());
}
}