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

View File

@ -29,6 +29,7 @@ public class S3ObjectMonitor implements Managed {
private final String s3Bucket; private final String s3Bucket;
private final String objectKey; private final String objectKey;
private final long maxObjectSize;
private final ScheduledExecutorService refreshExecutorService; private final ScheduledExecutorService refreshExecutorService;
private final Duration refreshInterval; private final Duration refreshInterval;
@ -46,6 +47,7 @@ public class S3ObjectMonitor implements Managed {
final String s3Region, final String s3Region,
final String s3Bucket, final String s3Bucket,
final String objectKey, final String objectKey,
final long maxObjectSize,
final ScheduledExecutorService refreshExecutorService, final ScheduledExecutorService refreshExecutorService,
final Duration refreshInterval, final Duration refreshInterval,
final Consumer<S3Object> changeListener) { final Consumer<S3Object> changeListener) {
@ -56,6 +58,7 @@ public class S3ObjectMonitor implements Managed {
.build(), .build(),
s3Bucket, s3Bucket,
objectKey, objectKey,
maxObjectSize,
refreshExecutorService, refreshExecutorService,
refreshInterval, refreshInterval,
changeListener); changeListener);
@ -66,6 +69,7 @@ public class S3ObjectMonitor implements Managed {
final AmazonS3 s3Client, final AmazonS3 s3Client,
final String s3Bucket, final String s3Bucket,
final String objectKey, final String objectKey,
final long maxObjectSize,
final ScheduledExecutorService refreshExecutorService, final ScheduledExecutorService refreshExecutorService,
final Duration refreshInterval, final Duration refreshInterval,
final Consumer<S3Object> changeListener) { final Consumer<S3Object> changeListener) {
@ -73,6 +77,7 @@ public class S3ObjectMonitor implements Managed {
this.s3Client = s3Client; this.s3Client = s3Client;
this.s3Bucket = s3Bucket; this.s3Bucket = s3Bucket;
this.objectKey = objectKey; this.objectKey = objectKey;
this.maxObjectSize = maxObjectSize;
this.refreshExecutorService = refreshExecutorService; this.refreshExecutorService = refreshExecutorService;
this.refreshInterval = refreshInterval; this.refreshInterval = refreshInterval;
@ -111,7 +116,17 @@ public class S3ObjectMonitor implements Managed {
final String refreshedETag = objectMetadata.getETag(); final String refreshedETag = objectMetadata.getETag();
if (!StringUtils.equals(initialETag, refreshedETag) && lastETag.compareAndSet(initialETag, refreshedETag)) { 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) { } catch (final Exception e) {
log.warn("Failed to refresh monitored object", e); log.warn("Failed to refresh monitored object", e);

View File

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

View File

@ -10,8 +10,9 @@ import java.util.UUID;
import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledExecutorService;
import java.util.function.Consumer; 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.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
@ -32,7 +33,9 @@ class S3ObjectMonitorTest {
final S3ObjectMonitor objectMonitor = new S3ObjectMonitor( final S3ObjectMonitor objectMonitor = new S3ObjectMonitor(
s3Client, s3Client,
bucket, bucket,
objectKey, mock(ScheduledExecutorService.class), objectKey,
16 * 1024 * 1024,
mock(ScheduledExecutorService.class),
Duration.ofMinutes(1), Duration.ofMinutes(1),
listener); listener);
@ -46,4 +49,37 @@ class S3ObjectMonitorTest {
verify(listener).accept(s3Object); 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());
}
} }