diff --git a/hooks/persistence-defectdojo/hook/.sdkmanrc b/hooks/persistence-defectdojo/hook/.sdkmanrc new file mode 100644 index 0000000000..0262a26101 --- /dev/null +++ b/hooks/persistence-defectdojo/hook/.sdkmanrc @@ -0,0 +1,3 @@ +# Enable auto-env through the sdkman_auto_env config +# Add key=value pairs of SDKs to use below +java=11.0.11.hs-adpt diff --git a/hooks/persistence-defectdojo/hook/build.gradle b/hooks/persistence-defectdojo/hook/build.gradle index 4c55fa28df..84b1fe99df 100644 --- a/hooks/persistence-defectdojo/hook/build.gradle +++ b/hooks/persistence-defectdojo/hook/build.gradle @@ -22,7 +22,13 @@ repositories { dependencies { implementation 'io.kubernetes:client-java:12.0.0' - implementation 'io.securecodebox:defectdojo-client:0.0.16-SNAPSHOT' + implementation 'io.securecodebox:defectdojo-client:0.0.18-SNAPSHOT' + + implementation group: 'org.springframework', name: 'spring-web', version: '5.3.9' + implementation 'com.fasterxml.jackson.core:jackson-core:2.12.4' + implementation 'com.fasterxml.jackson.core:jackson-annotations:2.12.4' + implementation 'com.fasterxml.jackson.core:jackson-databind:2.12.4' + implementation 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.12.4' implementation group: 'org.slf4j', name: 'slf4j-api', version: '1.7.30' implementation 'org.slf4j:slf4j-log4j12:1.7.30' diff --git a/hooks/persistence-defectdojo/hook/src/main/java/io/securecodebox/persistence/mapping/DefectDojoFindingToSecureCodeBoxMapper.java b/hooks/persistence-defectdojo/hook/src/main/java/io/securecodebox/persistence/mapping/DefectDojoFindingToSecureCodeBoxMapper.java index 26665d11ac..1f227a4d16 100644 --- a/hooks/persistence-defectdojo/hook/src/main/java/io/securecodebox/persistence/mapping/DefectDojoFindingToSecureCodeBoxMapper.java +++ b/hooks/persistence-defectdojo/hook/src/main/java/io/securecodebox/persistence/mapping/DefectDojoFindingToSecureCodeBoxMapper.java @@ -10,6 +10,9 @@ import io.securecodebox.persistence.models.SecureCodeBoxFinding; import org.springframework.web.util.UriComponentsBuilder; +import java.time.Instant; +import java.time.OffsetDateTime; +import java.time.format.DateTimeFormatter; import java.util.HashMap; import java.util.UUID; @@ -30,6 +33,8 @@ public SecureCodeBoxFinding fromDefectDojoFinding(io.securecodebox.persistence.d finding.setName(defectDojoFinding.getTitle()); finding.setCategory("DefectDojo Imported Finding"); finding.setDescription(defectDojoFinding.getDescription()); + Instant createdAtInstant = defectDojoFinding.getCreatedAt().toInstant(OffsetDateTime.now().getOffset()); + finding.setParsedAt(DateTimeFormatter.ISO_INSTANT.format(createdAtInstant)); var attributes = new HashMap(); attributes.put("defectdojo.org/finding-id", defectDojoFinding.getId()); diff --git a/hooks/persistence-defectdojo/hook/src/main/java/io/securecodebox/persistence/mapping/SecureCodeBoxFindingsToDefectDojoMapper.java b/hooks/persistence-defectdojo/hook/src/main/java/io/securecodebox/persistence/mapping/SecureCodeBoxFindingsToDefectDojoMapper.java index a594655199..78c8856474 100644 --- a/hooks/persistence-defectdojo/hook/src/main/java/io/securecodebox/persistence/mapping/SecureCodeBoxFindingsToDefectDojoMapper.java +++ b/hooks/persistence-defectdojo/hook/src/main/java/io/securecodebox/persistence/mapping/SecureCodeBoxFindingsToDefectDojoMapper.java @@ -72,7 +72,7 @@ protected static DefectDojoImportFinding fromSecureCodeBoxFinding(SecureCodeBoxF description = description + "\n " + attributesJson; } result.setDescription(description); - setFindingTimestamp(secureCodeBoxFinding, result); + setFindingDate(secureCodeBoxFinding, result); setFindingLocation(secureCodeBoxFinding, result); return result; } @@ -88,11 +88,14 @@ private static void setFindingLocation(SecureCodeBoxFinding secureCodeBoxFinding } } - private static void setFindingTimestamp(SecureCodeBoxFinding secureCodeBoxFinding, DefectDojoImportFinding result) { - Instant instant; - if (secureCodeBoxFinding.getTimestamp() != null) { - instant = Instant.from(DateTimeFormatter.ISO_INSTANT.parse(secureCodeBoxFinding.getTimestamp())); - } else { + private static void setFindingDate(SecureCodeBoxFinding secureCodeBoxFinding, DefectDojoImportFinding result) { + Instant instant = null; + if (secureCodeBoxFinding.getIdentifiedAt() != null && !secureCodeBoxFinding.getIdentifiedAt().isEmpty()) { + instant = Instant.parse(secureCodeBoxFinding.getIdentifiedAt()); + } else if (secureCodeBoxFinding.getParsedAt() != null && !secureCodeBoxFinding.getParsedAt().isEmpty()){ + instant = Instant.parse(secureCodeBoxFinding.getParsedAt()); + } + else { instant = Instant.now(); } LocalDateTime localDateTime = LocalDateTime.ofInstant(instant, ZoneId.systemDefault()); diff --git a/hooks/persistence-defectdojo/hook/src/main/java/io/securecodebox/persistence/models/DefectDojoImportFinding.java b/hooks/persistence-defectdojo/hook/src/main/java/io/securecodebox/persistence/models/DefectDojoImportFinding.java index 073e826274..5fe9c21ee5 100644 --- a/hooks/persistence-defectdojo/hook/src/main/java/io/securecodebox/persistence/models/DefectDojoImportFinding.java +++ b/hooks/persistence-defectdojo/hook/src/main/java/io/securecodebox/persistence/models/DefectDojoImportFinding.java @@ -31,6 +31,9 @@ public class DefectDojoImportFinding { @JsonProperty Boolean active; + @JsonProperty + String created; + @JsonProperty() Boolean verified; diff --git a/hooks/persistence-defectdojo/hook/src/main/java/io/securecodebox/persistence/models/SecureCodeBoxFinding.java b/hooks/persistence-defectdojo/hook/src/main/java/io/securecodebox/persistence/models/SecureCodeBoxFinding.java index c7d16ffcaf..58175760f2 100644 --- a/hooks/persistence-defectdojo/hook/src/main/java/io/securecodebox/persistence/models/SecureCodeBoxFinding.java +++ b/hooks/persistence-defectdojo/hook/src/main/java/io/securecodebox/persistence/models/SecureCodeBoxFinding.java @@ -36,8 +36,10 @@ public class SecureCodeBoxFinding { String osiLayer; @JsonProperty Severities severity; - @JsonProperty - String timestamp; + @JsonProperty("parsed_at") + String parsedAt; + @JsonProperty("identified_at") + String identifiedAt; @JsonProperty Map attributes; diff --git a/hooks/persistence-defectdojo/hook/src/test/java/io/securecodebox/persistence/mapping/DefectDojoFindingToSecureCodeBoxMapperTest.java b/hooks/persistence-defectdojo/hook/src/test/java/io/securecodebox/persistence/mapping/DefectDojoFindingToSecureCodeBoxMapperTest.java index eb5884341c..9b5b936421 100644 --- a/hooks/persistence-defectdojo/hook/src/test/java/io/securecodebox/persistence/mapping/DefectDojoFindingToSecureCodeBoxMapperTest.java +++ b/hooks/persistence-defectdojo/hook/src/test/java/io/securecodebox/persistence/mapping/DefectDojoFindingToSecureCodeBoxMapperTest.java @@ -15,7 +15,9 @@ import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; +import java.time.Instant; import java.time.LocalDateTime; +import java.time.ZoneId; import java.util.List; import static org.junit.jupiter.api.Assertions.*; @@ -44,6 +46,7 @@ public void shouldMapBasicFindings(){ .description("Content Security Policy (CSP) is an added layer of security that helps to detect and mitigate certain types of attacks, including Cross Site Scripting (XSS) and data injection attacks. These attacks are used for everything from data theft to site defacement or distribution of malware. CSP provides a set of standard HTTP headers that allow website owners to declare approved sources of content that browsers should be allowed to load on that page — covered types are JavaScript, CSS, HTML frames, fonts, images and embeddable objects such as Java applets, ActiveX, audio and video files.\n\nReference: https://developer.mozilla.org/en-US/docs/Web/Security/CSP/Introducing_Content_Security_Policyhttps://cheatsheetseries.owasp.org/cheatsheets/Content_Security_Policy_Cheat_Sheet.htmlhttp://www.w3.org/TR/CSP/http://w3c.github.io/webappsec/specs/content-security-policy/csp-specification.dev.htmlhttp://www.html5rocks.com/en/tutorials/security/content-security-policy/http://caniuse.com/#feat=contentsecuritypolicyhttp://content-security-policy.com/\n\nURL: http://juice-shop.securecodebox-test.svc:3000/sitemap.xml\nMethod: GET\n\nURL: http://juice-shop.securecodebox-test.svc:3000/ftp/coupons_2013.md.bak\nMethod: GET\n\nURL: http://juice-shop.securecodebox-test.svc:3000/\nMethod: GET\n\nURL: http://juice-shop.securecodebox-test.svc:3000/ftp\nMethod: GET\n\nURL: http://juice-shop.securecodebox-test.svc:3000/ftp/encrypt.pyc\nMethod: GET\n\nURL: http://juice-shop.securecodebox-test.svc:3000/ftp/suspicious_errors.yml\nMethod: GET\n\nURL: http://juice-shop.securecodebox-test.svc:3000/ftp/\nMethod: GET\n\nURL: http://juice-shop.securecodebox-test.svc:3000/ftp/package.json.bak\nMethod: GET\n\nURL: http://juice-shop.securecodebox-test.svc:3000/ftp/eastere.gg\nMethod: GET\n\nURL: http://juice-shop.securecodebox-test.svc:3000\nMethod: GET\n\nURL: http://juice-shop.securecodebox-test.svc:3000/ftp/quarantine\nMethod: GET\n") .mitigation("Ensure that your web server, application server, load balancer, etc. is configured to set the Content-Security-Policy header, to achieve optimal browser support: \"Content-Security-Policy\" for Chrome 25+, Firefox 23+ and Safari 7+, \"X-Content-Security-Policy\" for Firefox 4.0+ and Internet Explorer 10+, and \"X-WebKit-CSP\" for Chrome 14+ and Safari 6+.") // Random ids... + .createdAt(LocalDateTime.ofInstant(Instant.parse("2020-04-15T20:08:18.000Z"), ZoneId.systemDefault())) .endpoints(List.of(1337L, 42L, 3L)) .foundBy(List.of(3L)) .test(42L) @@ -73,6 +76,11 @@ public void shouldMapBasicFindings(){ "http://juice-shop.securecodebox-test.svc:3000", actualFinding.getLocation() ); + + assertEquals( + "2020-04-15T20:08:18Z", + actualFinding.getParsedAt() + ); } @Test diff --git a/hooks/persistence-defectdojo/hook/src/test/java/io/securecodebox/persistence/mapping/SecureCodeBoxFindingsToDefectDojoMapperTest.java b/hooks/persistence-defectdojo/hook/src/test/java/io/securecodebox/persistence/mapping/SecureCodeBoxFindingsToDefectDojoMapperTest.java index d1defc3605..a89cd7521e 100644 --- a/hooks/persistence-defectdojo/hook/src/test/java/io/securecodebox/persistence/mapping/SecureCodeBoxFindingsToDefectDojoMapperTest.java +++ b/hooks/persistence-defectdojo/hook/src/test/java/io/securecodebox/persistence/mapping/SecureCodeBoxFindingsToDefectDojoMapperTest.java @@ -46,6 +46,7 @@ public void correctlyParsesFindings() throws IOException { var description = "Description"; var severity = "HIGH"; var id = "123"; + var parsedAt = "2020-04-15T12:27:28.153Z"; var location = "ldap://[2001:db8::7]/c=GB?objectClass?one"; var attributes = new HashMap(); var subAttribute = new HashMap<>(); @@ -55,14 +56,17 @@ public void correctlyParsesFindings() throws IOException { attributes.put("attribute_3", "3"); var scbFinding = SecureCodeBoxFinding.builder().name(name).description(description) .severity(SecureCodeBoxFinding.Severities.HIGH).id(id).location(location).attributes(attributes) - .build(); + .parsedAt(parsedAt).build(); var ddFinding = SecureCodeBoxFindingsToDefectDojoMapper.fromSecureCodeBoxFinding(scbFinding); + assertEquals(ddFinding.getTitle(), name); assertEquals(ddFinding.getSeverity(), severity); assertEquals(ddFinding.getUniqueIdFromTool(), id); assertEquals(ddFinding.getEndpoints().get(0), location); + assertEquals(ddFinding.getDate(), "2020-04-15"); assertTrue(ddFinding.getDescription().startsWith(description)); + //Description should consist of description and attributes as JSON String attributesJson = ddFinding.getDescription().substring(description.length() + 1); String expectedAttributeJson = "{\n" + @@ -73,8 +77,8 @@ public void correctlyParsesFindings() throws IOException { " \"attribute_3\" : \"3\"\n" + "}"; ObjectMapper mapper = new ObjectMapper(); - var expectedJson = mapper.readTree(attributesJson); var actualJson = mapper.readTree(expectedAttributeJson); + assertNotNull(actualJson); assertEquals(mapper.readTree(attributesJson), mapper.readTree(expectedAttributeJson)); } diff --git a/hooks/persistence-defectdojo/hook/src/test/resources/kubehunter-scb-findings.json b/hooks/persistence-defectdojo/hook/src/test/resources/kubehunter-scb-findings.json index 257e766052..db834ebac8 100644 --- a/hooks/persistence-defectdojo/hook/src/test/resources/kubehunter-scb-findings.json +++ b/hooks/persistence-defectdojo/hook/src/test/resources/kubehunter-scb-findings.json @@ -2,7 +2,7 @@ { "name": "Read access to pod's service account token", "description": " Accessing the pod service account token gives an attacker the option to use the server API ", - "timestamp": "2020-04-15T20:08:18.000Z", + "parsed_at": "2020-04-15T20:08:18.000Z", "location": "tcp://10.1.0.1", "severity": "LOW", "category": "Access Risk", @@ -19,7 +19,7 @@ { "name": "CAP_NET_RAW Enabled", "description": "CAP_NET_RAW is enabled by default for pods.\n If an attacker manages to compromise a pod,\n they could potentially take advantage of this capability to perform network\n attacks on other pods running on the same node", - "timestamp": "2020-04-16T20:08:18Z", + "parsed_at": "2020-04-16T20:08:18Z", "location": "tcp://10.1.0.1", "severity": "LOW", "category": "Access Risk", @@ -33,7 +33,7 @@ { "name": "Access to pod's secrets", "description": " Accessing the pod's secrets within a compromised pod might disclose valuable data to a potential attacker", - "timestamp": "2020-04-17T20:08:18Z", + "parsed_at": "2020-04-17T20:08:18Z", "location": "tcp://10.1.0.1", "severity": "LOW", "category": "Access Risk",