-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathVirusScanValidationTest.java
More file actions
213 lines (187 loc) · 7.2 KB
/
VirusScanValidationTest.java
File metadata and controls
213 lines (187 loc) · 7.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
package cloud.cleo.clamav.test;
import cloud.cleo.clamav.ScanStatus;
import static cloud.cleo.clamav.ScanStatus.ONLY_TAG_INFECTED;
import static cloud.cleo.clamav.ScanStatus.SCAN_TAG_NAME;
import java.io.PrintWriter;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.BeforeAll;
import software.amazon.awssdk.services.s3.S3Client;
import software.amazon.awssdk.services.s3.model.*;
import java.time.Duration;
import java.util.List;
import static org.assertj.core.api.Assertions.assertThat;
import org.junit.jupiter.api.AfterAll;
import static org.junit.jupiter.api.Assumptions.assumeTrue;
import org.junit.jupiter.api.MethodOrderer;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.TestMethodOrder;
import org.junit.platform.engine.discovery.DiscoverySelectors;
import org.junit.platform.launcher.Launcher;
import org.junit.platform.launcher.LauncherDiscoveryRequest;
import org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder;
import org.junit.platform.launcher.core.LauncherFactory;
import org.junit.platform.launcher.listeners.SummaryGeneratingListener;
/**
* Test to validate the container Lambda can properly set tags and identify a known infected file.
*
* In order for tests to run, you need to set VALIDATION_BUCKET in workflow and of course populate that bucket with test
* files.
*
* @author sjensen
*/
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class VirusScanValidationTest {
private static final String BUCKET_NAME = System.getenv("VALIDATION_BUCKET");
private static final String INFECTED_KEY = "eicar.txt";
private static final String OVERSIZED_KEY = "large-test-file.zip";
private static final S3Client s3 = S3Client.create();
@BeforeAll
static void checkSetup() {
if (BUCKET_NAME == null || BUCKET_NAME.isEmpty()) {
throw new IllegalStateException("VALIDATION_BUCKET environment variable must be set.");
}
// Ensure all tags are cleared before testing starts
clearTags(INFECTED_KEY);
clearTags(OVERSIZED_KEY);
//throw new AssertionError("Testing rollback");
}
/**
* When all tagging is enabled, right before scanning takes place, it should be in SCANNING state, so this test is
* forced at Order(1) to ensure it tests this first.
*
* @throws InterruptedException
*/
@Test
@Order(1)
public void validateScanningTagSetImmediatelyIfNotOnlyInfected() throws InterruptedException {
// This will show as aborted in test results (won't error out, but in reality its a skip)
assumeTrue(!ONLY_TAG_INFECTED, "Skipping because ONLY_TAG_INFECTED is true");
retriggerScan(INFECTED_KEY);
// Tag should be applied pretty fast as soon as Lambda kicks off
waitForTagValue(INFECTED_KEY, ScanStatus.SCANNING, Duration.ofSeconds(30));
}
/**
* Validate a known virus file (EICAR Signature) tests to be INFECTED.
*
* @throws InterruptedException
*/
@Test
@Order(2)
public void validateScanOfKnownInfectedFile() throws InterruptedException {
if (ONLY_TAG_INFECTED) {
// If SCANNING test was skipped, re-trigger scan here
retriggerScan(INFECTED_KEY);
}
// Need to wait for scan to complete, which sometimes can take over a minute
waitForTagValue(INFECTED_KEY, ScanStatus.INFECTED, Duration.ofMinutes(2));
}
/**
* When file to scan is over MAX_BYTES, then ensure it is not scanned and immediately tagged FILE_SIZE_EXCEEDED.
*
* @throws InterruptedException
*/
@Test
@Order(3)
public void validateScanOfOversizedFile() throws InterruptedException {
retriggerScan(OVERSIZED_KEY);
// Should be detected before scanning on the S3 Head operation
waitForTagValue(OVERSIZED_KEY, ScanStatus.FILE_SIZE_EXCEEED, Duration.ofSeconds(30));
}
/**
* Close S3 client at end to shut down threads.
*/
@AfterAll
static void cleanup() {
if (s3 != null) {
s3.close();
}
}
/**
* Wait up for scan tag to have an expected value.
*
* @param key
* @param expectedValue
* @param timeout
* @throws InterruptedException
*/
private static void waitForTagValue(String key, ScanStatus expectedValue, Duration timeout) throws InterruptedException {
long timeoutMillis = timeout.toMillis();
long sleepMillis = 5000;
long start = System.currentTimeMillis();
while (System.currentTimeMillis() - start <= timeoutMillis) {
List<Tag> tags = getTags(key);
String actual = tags.stream()
.filter(tag -> SCAN_TAG_NAME.equals(tag.key()))
.map(Tag::value)
.findFirst()
.orElse(null);
if (expectedValue.name().equals(actual)) {
assertThat(actual).isEqualTo(expectedValue.name());
return;
}
Thread.sleep(sleepMillis);
}
throw new AssertionError("Timed out waiting for scan-status tag: " + expectedValue + " on key: " + key);
}
/**
* Get all tags on S3 Object.
*
* @param key
* @return
*/
private static List<Tag> getTags(String key) {
GetObjectTaggingResponse response = s3.getObjectTagging(GetObjectTaggingRequest.builder()
.bucket(BUCKET_NAME)
.key(key)
.build());
return response.tagSet();
}
/**
* Remove any tags on the object.
*
* @param key
*/
private static void clearTags(String key) {
s3.deleteObjectTagging(DeleteObjectTaggingRequest.builder()
.bucket(BUCKET_NAME)
.key(key)
.build());
}
/**
* Copy file onto itself to trigger a ObjectCreate event to start scanning.
*
* @param key
*/
private static void retriggerScan(String key) {
CopyObjectRequest copyRequest = CopyObjectRequest.builder()
.sourceBucket(BUCKET_NAME)
.sourceKey(key)
.destinationBucket(BUCKET_NAME)
.destinationKey(key)
.metadataDirective(MetadataDirective.COPY)
.build();
s3.copyObject(copyRequest);
}
/**
* Called via mvn exec:java
*
* @param args
*/
public static void main(String[] args) {
LauncherDiscoveryRequest request = LauncherDiscoveryRequestBuilder.request()
.selectors(DiscoverySelectors.selectClass(VirusScanValidationTest.class))
.build();
SummaryGeneratingListener listener = new SummaryGeneratingListener();
Launcher launcher = LauncherFactory.create();
launcher.execute(request, listener);
var summary = listener.getSummary();
summary.printTo(new PrintWriter(System.out, true));
summary.getFailures().forEach(failure -> {
System.err.println("Failure: " + failure.getTestIdentifier().getDisplayName());
failure.getException().printStackTrace();
});
if (summary.getTotalFailureCount() > 0 || summary.getContainersFailedCount() > 0) {
System.exit(1);
}
}
}