diff --git a/owasp/suppressions.xml b/owasp/suppressions.xml
index 02f3e651..431c3754 100644
--- a/owasp/suppressions.xml
+++ b/owasp/suppressions.xml
@@ -26,4 +26,8 @@
Still WIP
CVE-2022-41862
+
+ False positive
+ CVE-2018-14335
+
diff --git a/src/main/java/eu/europa/ec/dgc/gateway/restapi/controller/CertificateRevocationListController.java b/src/main/java/eu/europa/ec/dgc/gateway/restapi/controller/CertificateRevocationListController.java
index b9fe0874..6de321d6 100644
--- a/src/main/java/eu/europa/ec/dgc/gateway/restapi/controller/CertificateRevocationListController.java
+++ b/src/main/java/eu/europa/ec/dgc/gateway/restapi/controller/CertificateRevocationListController.java
@@ -264,6 +264,9 @@ public ResponseEntity uploadBatch(
case INVALID_COUNTRY:
throw new DgcgResponseException(HttpStatus.FORBIDDEN, "0x000", "Invalid Country sent", "",
e.getMessage());
+ case INVALID_DATE:
+ throw new DgcgResponseException(HttpStatus.BAD_REQUEST, "0x000", "Invalid Dates", "",
+ e.getMessage());
case UPLOADER_CERT_CHECK_FAILED:
throw new DgcgResponseException(HttpStatus.FORBIDDEN, "0x000", "Invalid Upload Certificate",
batch.getSignerCertificate().getSubject().toString(), "Certificate used to sign the batch "
diff --git a/src/main/java/eu/europa/ec/dgc/gateway/service/RevocationListService.java b/src/main/java/eu/europa/ec/dgc/gateway/service/RevocationListService.java
index 56f53840..5341b005 100644
--- a/src/main/java/eu/europa/ec/dgc/gateway/service/RevocationListService.java
+++ b/src/main/java/eu/europa/ec/dgc/gateway/service/RevocationListService.java
@@ -104,6 +104,7 @@ public RevocationBatchEntity addRevocationBatch(
contentCheckUploaderCertificate(signerCertificate, authenticatedCountryCode);
RevocationBatchDto parsedBatch = contentCheckValidJson(uploadedRevocationBatch, RevocationBatchDto.class);
+ contentCheckValidDate(parsedBatch);
contentCheckValidValues(parsedBatch);
contentCheckUploaderCountry(parsedBatch, authenticatedCountryCode);
@@ -331,6 +332,13 @@ private T contentCheckValidJson(String json, Class clazz) throws Revocati
}
}
+ private void contentCheckValidDate(RevocationBatchDto parsedBatch) throws RevocationBatchServiceException {
+ if (!parsedBatch.getExpires().isAfter(ZonedDateTime.now())) {
+ throw new RevocationBatchServiceException(
+ RevocationBatchServiceException.Reason.INVALID_DATE,
+ "Expiration date must be in future.");
+ }
+ }
private void contentCheckValidValues(RevocationBatchDto parsedBatch) throws RevocationBatchServiceException {
@@ -443,6 +451,7 @@ public enum Reason {
INVALID_JSON,
INVALID_JSON_VALUES,
INVALID_COUNTRY,
+ INVALID_DATE,
UPLOADER_CERT_CHECK_FAILED,
NOT_FOUND,
GONE
diff --git a/src/test/java/eu/europa/ec/dgc/gateway/restapi/controller/CertificateRevocationListIntegrationTest.java b/src/test/java/eu/europa/ec/dgc/gateway/restapi/controller/CertificateRevocationListIntegrationTest.java
index 72f66477..84dc2fd0 100644
--- a/src/test/java/eu/europa/ec/dgc/gateway/restapi/controller/CertificateRevocationListIntegrationTest.java
+++ b/src/test/java/eu/europa/ec/dgc/gateway/restapi/controller/CertificateRevocationListIntegrationTest.java
@@ -379,6 +379,56 @@ void testUploadFailedInvalidCountry() throws Exception {
Assertions.assertEquals(auditEventEntitiesInDb, auditEventRepository.count());
}
+ @Test
+ void testUploadFailedInvalidExpirationDate() throws Exception {
+ long revocationBatchesInDb = revocationBatchRepository.count();
+ long auditEventEntitiesInDb = auditEventRepository.count();
+
+ X509Certificate signerCertificate =
+ trustedPartyTestHelper.getCert(TrustedPartyEntity.CertificateType.UPLOAD, countryCode);
+ PrivateKey signerPrivateKey =
+ trustedPartyTestHelper.getPrivateKey(TrustedPartyEntity.CertificateType.UPLOAD, countryCode);
+
+ RevocationBatchDto revocationBatchDto = new RevocationBatchDto();
+ revocationBatchDto.setCountry(countryCode);
+ revocationBatchDto.setExpires(ZonedDateTime.now().minusSeconds(10));
+ revocationBatchDto.setHashType(RevocationHashTypeDto.SIGNATURE);
+ revocationBatchDto.setKid("UNKNOWN_KID");
+ revocationBatchDto.setEntries(List.of(
+ new RevocationBatchDto.BatchEntryDto(Base64.getEncoder().encodeToString(
+ new byte[] {0xa, 0xa, 0xa, 0xa, 0xa, 0xa, 0xa, 0xa, 0xa, 0xa, 0xa, 0xa, 0xa, 0xa, 0xa, 0xa})),
+ new RevocationBatchDto.BatchEntryDto(Base64.getEncoder().encodeToString(
+ new byte[] {0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb})),
+ new RevocationBatchDto.BatchEntryDto(Base64.getEncoder().encodeToString(
+ new byte[] {0xc, 0xc, 0xc, 0xc, 0xc, 0xc, 0xc, 0xc, 0xc, 0xc, 0xc, 0xc, 0xc, 0xc, 0xc, 0xc})),
+ new RevocationBatchDto.BatchEntryDto(Base64.getEncoder().encodeToString(
+ new byte[] {0xd, 0xd, 0xd, 0xd, 0xd, 0xd, 0xd, 0xd, 0xd, 0xd, 0xd, 0xd, 0xd, 0xd, 0xd, 0xd})),
+ new RevocationBatchDto.BatchEntryDto(Base64.getEncoder().encodeToString(
+ new byte[] {0xe, 0xe, 0xe, 0xe, 0xe, 0xe, 0xe, 0xe, 0xe, 0xe, 0xe, 0xe, 0xe, 0xe, 0xe, 0xe}))
+ ));
+
+ String payload = new SignedStringMessageBuilder()
+ .withSigningCertificate(certificateUtils.convertCertificate(signerCertificate), signerPrivateKey)
+ .withPayload(objectMapper.writeValueAsString(revocationBatchDto))
+ .buildAsString();
+
+ String authCertHash =
+ trustedPartyTestHelper.getHash(TrustedPartyEntity.CertificateType.AUTHENTICATION, countryCode);
+ trustedPartyTestHelper.setRoles(countryCode, TrustedPartyEntity.CertificateRoles.REVOCATION_UPLOADER);
+
+ mockMvc.perform(post("/revocation-list")
+ .content(payload)
+ .contentType("application/cms")
+ .header(dgcConfigProperties.getCertAuth().getHeaderFields().getThumbprint(), authCertHash)
+ .header(dgcConfigProperties.getCertAuth().getHeaderFields().getDistinguishedName(), authCertSubject)
+ )
+ .andExpect(status().isBadRequest())
+ .andExpect(header().doesNotExist(HttpHeaders.ETAG));
+
+ Assertions.assertEquals(revocationBatchesInDb, revocationBatchRepository.count());
+ Assertions.assertEquals(auditEventEntitiesInDb, auditEventRepository.count());
+ }
+
@Test
void testDeleteRevocationBatch() throws Exception {
RevocationBatchEntity entity = new RevocationBatchEntity();