|
8 | 8 | import com.fasterxml.jackson.annotation.JsonProperty; |
9 | 9 | import io.securecodebox.persistence.defectdojo.ScanType; |
10 | 10 | import io.securecodebox.persistence.defectdojo.config.DefectDojoConfig; |
11 | | -import io.securecodebox.persistence.defectdojo.exceptions.DefectDojoPersistenceException; |
12 | 11 | import io.securecodebox.persistence.defectdojo.models.ScanFile; |
13 | 12 | import lombok.Data; |
14 | 13 | import lombok.NonNull; |
15 | | -import org.apache.http.HttpHost; |
16 | | -import org.apache.http.auth.AuthScope; |
17 | | -import org.apache.http.auth.UsernamePasswordCredentials; |
18 | | -import org.apache.http.impl.client.BasicCredentialsProvider; |
19 | | -import org.apache.http.impl.client.HttpClientBuilder; |
20 | | -import org.apache.http.impl.client.ProxyAuthenticationStrategy; |
21 | | -import org.springframework.core.io.ByteArrayResource; |
22 | | -import org.springframework.http.HttpEntity; |
23 | | -import org.springframework.http.HttpHeaders; |
24 | | -import org.springframework.http.HttpMethod; |
25 | | -import org.springframework.http.MediaType; |
26 | | -import org.springframework.http.client.HttpComponentsClientHttpRequestFactory; |
27 | | -import org.springframework.http.converter.FormHttpMessageConverter; |
28 | | -import org.springframework.http.converter.ResourceHttpMessageConverter; |
29 | | -import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; |
30 | | -import org.springframework.util.LinkedMultiValueMap; |
31 | 14 | import org.springframework.util.MultiValueMap; |
32 | | -import org.springframework.web.client.HttpClientErrorException; |
33 | | -import org.springframework.web.client.RestTemplate; |
34 | | - |
35 | | -import java.nio.charset.StandardCharsets; |
36 | | -import java.util.List; |
37 | | - |
38 | | -final class ImportScanService { |
39 | | - |
40 | | - private final String defectDojoUrl; |
41 | | - private final String defectDojoApiKey; |
42 | | - |
43 | | - /** |
44 | | - * Dedicated constructor. |
45 | | - * |
46 | | - * @param config not {@code null} |
47 | | - */ |
48 | | - public ImportScanService(final @NonNull DefectDojoConfig config) { |
49 | | - super(); |
50 | | - this.defectDojoUrl = config.getUrl(); |
51 | | - this.defectDojoApiKey = config.getApiKey(); |
52 | | - } |
53 | 15 |
|
| 16 | +/** |
| 17 | + * Service to re/import findings into DefectDojo |
| 18 | + */ |
| 19 | +public interface ImportScanService { |
54 | 20 | /** |
55 | | - * The DefectDojo Authentication Header |
| 21 | + * Factory method to create new instance of service default implementation |
56 | 22 | * |
| 23 | + * @param config must not be {@code null} |
57 | 24 | * @return never {@code null} |
58 | 25 | */ |
59 | | - HttpHeaders createDefectDojoAuthorizationHeaders() { |
60 | | - final var authorizationHeader = new HttpHeaders(); |
61 | | - authorizationHeader.set(HttpHeaders.AUTHORIZATION, String.format("Token %s", defectDojoApiKey)); |
62 | | - return authorizationHeader; |
63 | | - } |
64 | | - |
65 | | - protected RestTemplate createRestTemplate() { |
66 | | - if (System.getProperty("http.proxyUser") != null && System.getProperty("http.proxyPassword") != null) { |
67 | | - // Configuring Proxy Authentication explicitly as it isn't done by default for spring rest templates :( |
68 | | - final var credentials = new BasicCredentialsProvider(); |
69 | | - credentials.setCredentials( |
70 | | - new AuthScope(System.getProperty("http.proxyHost"), Integer.parseInt(System.getProperty("http.proxyPort"))), |
71 | | - new UsernamePasswordCredentials(System.getProperty("http.proxyUser"), System.getProperty("http.proxyPassword")) |
72 | | - ); |
73 | | - |
74 | | - final var clientBuilder = HttpClientBuilder.create(); |
75 | | - |
76 | | - clientBuilder.useSystemProperties(); |
77 | | - clientBuilder.setProxy(new HttpHost(System.getProperty("http.proxyHost"), Integer.parseInt(System.getProperty("http.proxyPort")))); |
78 | | - clientBuilder.setDefaultCredentialsProvider(credentials); |
79 | | - clientBuilder.setProxyAuthenticationStrategy(new ProxyAuthenticationStrategy()); |
80 | | - |
81 | | - final var factory = new HttpComponentsClientHttpRequestFactory(); |
82 | | - factory.setHttpClient(clientBuilder.build()); |
83 | | - return new RestTemplate(factory); |
84 | | - } else { |
85 | | - return new RestTemplate(); |
86 | | - } |
87 | | - } |
88 | | - |
89 | | - /* |
90 | | - * Before version 1.5.4. testName (in DefectDojo _test_type_) must be defectDojoScanName, afterward, you can have something else. |
91 | | - */ |
92 | | - protected ImportScanResponse createFindings(ScanFile scanFile, String endpoint, long lead, String currentDate, ScanType scanType, long testType, MultiValueMap<String, String> options) { |
93 | | - var restTemplate = this.createRestTemplate(); |
94 | | - HttpHeaders headers = createDefectDojoAuthorizationHeaders(); |
95 | | - headers.setContentType(MediaType.MULTIPART_FORM_DATA); |
96 | | - restTemplate.setMessageConverters(List.of( |
97 | | - new FormHttpMessageConverter(), |
98 | | - new ResourceHttpMessageConverter(), |
99 | | - new MappingJackson2HttpMessageConverter()) |
100 | | - ); |
101 | | - |
102 | | - final var body = new LinkedMultiValueMap<String, Object>(); |
103 | | - |
104 | | - body.add("lead", Long.toString(lead)); |
105 | | - body.add("scan_date", currentDate); |
106 | | - body.add("scan_type", scanType.getTestType()); |
107 | | - body.add("close_old_findings", "true"); |
108 | | - body.add("skip_duplicates", "false"); |
109 | | - body.add("test_type", String.valueOf(testType)); |
110 | | - |
111 | | - for (final var optionName : options.keySet()) { |
112 | | - body.remove(optionName); |
113 | | - } |
114 | | - |
115 | | - // FIXME: Workaround due to type incompatibility of MultiValueMap<String, String> and MultiValueMap<String, Object>. |
116 | | - for (final var option : options.entrySet()) { |
117 | | - body.add(option.getKey(), option.getValue()); |
118 | | - } |
119 | | - |
120 | | - try { |
121 | | - ByteArrayResource contentsAsResource = new ByteArrayResource(scanFile.getContent().getBytes(StandardCharsets.UTF_8)) { |
122 | | - @Override |
123 | | - public String getFilename() { |
124 | | - return scanFile.getName(); |
125 | | - } |
126 | | - }; |
127 | | - |
128 | | - // FIXME Why do we add the whole byte array resiurce here as object? Is not simply the file name sufficient here? Then we could use <String, String> |
129 | | - body.add("file", contentsAsResource); |
130 | | - |
131 | | - // FIXME: We do not define the the type T of the body here! |
132 | | - final var payload = new HttpEntity<>(body, headers); |
133 | | - |
134 | | - return restTemplate.exchange(defectDojoUrl + "/api/v2/" + endpoint + "/", HttpMethod.POST, payload, ImportScanResponse.class).getBody(); |
135 | | - } catch (HttpClientErrorException e) { |
136 | | - throw new DefectDojoPersistenceException("Failed to attach findings to engagement."); |
137 | | - } |
| 26 | + default ImportScanService createDefault(@NonNull DefectDojoConfig config) { |
| 27 | + return new DefaultImportScanService(config); |
138 | 28 | } |
139 | 29 |
|
140 | | - public ImportScanResponse importScan(ScanFile scanFile, long engagementId, long lead, String currentDate, ScanType scanType, long testType) { |
141 | | - final var options = new LinkedMultiValueMap<String, String>(); |
142 | | - options.add("engagement", Long.toString(engagementId)); // FIXME Seems to be duplicated bc it is done again in the overloaded method. |
| 30 | + ImportScanResponse importScan(ScanFile scanFile, long engagementId, long lead, String currentDate, ScanType scanType, long testType); |
143 | 31 |
|
144 | | - return this.importScan(scanFile, engagementId, lead, currentDate, scanType, testType, options); |
145 | | - } |
146 | | - |
147 | | - public ImportScanResponse importScan(ScanFile scanFile, long engagementId, long lead, String currentDate, ScanType scanType, long testType, MultiValueMap<String, String> options) { |
148 | | - options.add("engagement", Long.toString(engagementId)); |
149 | | - |
150 | | - return this.createFindings(scanFile, "import-scan", lead, currentDate, scanType, testType, options); |
151 | | - } |
| 32 | + ImportScanResponse importScan(ScanFile scanFile, long engagementId, long lead, String currentDate, ScanType scanType, long testType, MultiValueMap<String, String> options); |
152 | 33 |
|
153 | | - public ImportScanResponse reimportScan(ScanFile scanFile, long testId, long lead, String currentDate, ScanType scanType, long testType) { |
154 | | - final var options = new LinkedMultiValueMap<String, String>(); |
155 | | - options.add("test", Long.toString(testId)); // FIXME Seems to be duplicated bc it is done again in the overloaded method. |
| 34 | + ImportScanResponse reimportScan(ScanFile scanFile, long testId, long lead, String currentDate, ScanType scanType, long testType); |
156 | 35 |
|
157 | | - return this.reimportScan(scanFile, testId, lead, currentDate, scanType, testType, options); |
158 | | - } |
159 | | - |
160 | | - public ImportScanResponse reimportScan(ScanFile scanFile, long testId, long lead, String currentDate, ScanType scanType, long testType, MultiValueMap<String, String> options) { |
161 | | - options.add("test", Long.toString(testId)); |
162 | | - |
163 | | - return this.createFindings(scanFile, "reimport-scan", lead, currentDate, scanType, testType, options); |
164 | | - } |
| 36 | + ImportScanResponse reimportScan(ScanFile scanFile, long testId, long lead, String currentDate, ScanType scanType, long testType, MultiValueMap<String, String> options); |
165 | 37 |
|
166 | 38 | @Data |
167 | | - public static class ImportScanResponse { |
| 39 | + class ImportScanResponse { |
168 | 40 | @JsonProperty |
169 | 41 | protected Boolean verified; |
170 | 42 |
|
|
0 commit comments