diff --git a/sechub-commons-model/src/main/java/com/mercedesbenz/sechub/commons/model/SecHubWebScanApiConfiguration.java b/sechub-commons-model/src/main/java/com/mercedesbenz/sechub/commons/model/SecHubWebScanApiConfiguration.java index cc84270d1a..c4edf1cdc7 100644 --- a/sechub-commons-model/src/main/java/com/mercedesbenz/sechub/commons/model/SecHubWebScanApiConfiguration.java +++ b/sechub-commons-model/src/main/java/com/mercedesbenz/sechub/commons/model/SecHubWebScanApiConfiguration.java @@ -1,6 +1,7 @@ // SPDX-License-Identifier: MIT package com.mercedesbenz.sechub.commons.model; +import java.net.URL; import java.util.LinkedHashSet; import java.util.Set; @@ -10,8 +11,12 @@ public class SecHubWebScanApiConfiguration implements SecHubDataConfigurationUsageByName { public static final String PROPERTY_TYPE = "type"; + public static final String PROPERTY_API_DEFINITION_URL = "apiDefinitionUrl"; private SecHubWebScanApiType type; + private Set namesOfUsedDataConfigurationObjects = new LinkedHashSet<>(); + + private URL apiDefinitionUrl; public SecHubWebScanApiType getType() { return type; @@ -21,10 +26,17 @@ public void setType(SecHubWebScanApiType type) { this.type = type; } - private Set namesOfUsedDataConfigurationObjects = new LinkedHashSet<>(); - @Override public Set getNamesOfUsedDataConfigurationObjects() { return namesOfUsedDataConfigurationObjects; } + + public URL getApiDefinitionUrl() { + return apiDefinitionUrl; + } + + public void setApiDefinitionUrl(URL apiDefinitionUrl) { + this.apiDefinitionUrl = apiDefinitionUrl; + } + } diff --git a/sechub-commons-model/src/test/java/com/mercedesbenz/sechub/commons/model/SecHubWebScanApiConfigurationTest.java b/sechub-commons-model/src/test/java/com/mercedesbenz/sechub/commons/model/SecHubWebScanApiConfigurationTest.java index 12aefffc5f..f3a291d10c 100644 --- a/sechub-commons-model/src/test/java/com/mercedesbenz/sechub/commons/model/SecHubWebScanApiConfigurationTest.java +++ b/sechub-commons-model/src/test/java/com/mercedesbenz/sechub/commons/model/SecHubWebScanApiConfigurationTest.java @@ -1,8 +1,12 @@ // SPDX-License-Identifier: MIT package com.mercedesbenz.sechub.commons.model; -import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import java.net.MalformedURLException; +import java.net.URL; import java.util.Set; import org.junit.jupiter.api.Test; @@ -36,6 +40,7 @@ void json_attribute_use_is_handled_correctly_by_from_json() { */ @Test void json_attribute_use_is_handled_correctly_by_to_json() { + /* prepare */ SecHubWebScanApiConfiguration config = new SecHubWebScanApiConfiguration(); config.getNamesOfUsedDataConfigurationObjects().add("ref1"); config.getNamesOfUsedDataConfigurationObjects().add("ref2"); @@ -48,4 +53,40 @@ void json_attribute_use_is_handled_correctly_by_to_json() { assertEquals(expected, json); } -} + @Test + void api_definition_url_is_handled_correctly() throws MalformedURLException { + /* prepare */ + SecHubWebScanApiConfiguration expectedConfig = new SecHubWebScanApiConfiguration(); + URL apiDefinitionUrl = new URL("https://example.com/api/v1/swagger/"); + expectedConfig.setApiDefinitionUrl(apiDefinitionUrl); + + /* execute */ + String json = JSONConverter.get().toJSON(expectedConfig); + SecHubWebScanApiConfiguration loadedConfig = JSONConverter.get().fromJSON(SecHubWebScanApiConfiguration.class, json); + + /* test */ + String expected = "{\"apiDefinitionUrl\":\"https://example.com/api/v1/swagger/\",\"use\":[]}"; + assertEquals(expected, json); + assertEquals(expectedConfig.getApiDefinitionUrl(), loadedConfig.getApiDefinitionUrl()); + } + + @Test + void full_openapi_definition_is_handled_correctly() throws MalformedURLException { + /* prepare */ + SecHubWebScanApiConfiguration expectedConfig = new SecHubWebScanApiConfiguration(); + URL apiDefinitionUrl = new URL("https://example.com/api/v1/swagger/"); + expectedConfig.setApiDefinitionUrl(apiDefinitionUrl); + expectedConfig.setType(SecHubWebScanApiType.OPEN_API); + expectedConfig.getNamesOfUsedDataConfigurationObjects().add("open-api-file-reference"); + + /* execute */ + String json = JSONConverter.get().toJSON(expectedConfig); + SecHubWebScanApiConfiguration loadedConfig = JSONConverter.get().fromJSON(SecHubWebScanApiConfiguration.class, json); + + /* test */ + String expected = "{\"type\":\"OPEN_API\",\"apiDefinitionUrl\":\"https://example.com/api/v1/swagger/\",\"use\":[\"open-api-file-reference\"]}"; + assertEquals(expected, json); + assertEquals(expectedConfig.getApiDefinitionUrl(), loadedConfig.getApiDefinitionUrl()); + } + +} \ No newline at end of file diff --git a/sechub-doc/src/docs/asciidoc/documents/shared/configuration/sechub_config.adoc b/sechub-doc/src/docs/asciidoc/documents/shared/configuration/sechub_config.adoc index 625d31f08d..dea3a7d03c 100644 --- a/sechub-doc/src/docs/asciidoc/documents/shared/configuration/sechub_config.adoc +++ b/sechub-doc/src/docs/asciidoc/documents/shared/configuration/sechub_config.adoc @@ -306,6 +306,8 @@ include::sechub_config_example8_web_scan_openapi_with_data_reference.json[] <2> web scan uses "openApi" as API type <3> web scan uses the referenced <> configuration "open-api-file-reference" to obtain the open api configuration file +<4> you can also use `apiDefinitionUrl` to specify an URL to read the API definition from. +Currently you can combine importing openApi definitions from files and URLs, but most of the time it does not make sense to combine this two options. [[sechub-config-openAPI-and-client-certificate]] ====== Example combination of openAPI definition and client certificate authentication diff --git a/sechub-doc/src/docs/asciidoc/documents/shared/configuration/sechub_config_example8_web_scan_openapi_with_data_reference.json b/sechub-doc/src/docs/asciidoc/documents/shared/configuration/sechub_config_example8_web_scan_openapi_with_data_reference.json index 1b3696e832..cf4fbfbd23 100644 --- a/sechub-doc/src/docs/asciidoc/documents/shared/configuration/sechub_config_example8_web_scan_openapi_with_data_reference.json +++ b/sechub-doc/src/docs/asciidoc/documents/shared/configuration/sechub_config_example8_web_scan_openapi_with_data_reference.json @@ -12,7 +12,8 @@ "url" : "https://productfailure.demo.example.org", "api" : { "type" : "openApi", //<2> - "use" : [ "open-api-file-reference" ] //<3> + "use" : [ "open-api-file-reference" ], //<3> + "apiDefinitionUrl" : "https://productfailure.demo.example.org/api/v1/swagger/?format=openapi" //<4> } } } \ No newline at end of file diff --git a/sechub-doc/src/test/java/com/mercedesbenz/sechub/restdoc/SchedulerRestControllerRestDocTest.java b/sechub-doc/src/test/java/com/mercedesbenz/sechub/restdoc/SchedulerRestControllerRestDocTest.java index 81eef3fb62..ca7b11dec9 100644 --- a/sechub-doc/src/test/java/com/mercedesbenz/sechub/restdoc/SchedulerRestControllerRestDocTest.java +++ b/sechub-doc/src/test/java/com/mercedesbenz/sechub/restdoc/SchedulerRestControllerRestDocTest.java @@ -17,6 +17,7 @@ import java.io.InputStream; import java.lang.annotation.Annotation; +import java.net.URL; import java.time.LocalDateTime; import java.util.ArrayList; import java.util.Arrays; @@ -569,6 +570,8 @@ public void restDoc_userCreatesNewJob_webscan_with_api_definition() throws Excep SecHubWebScanApiConfiguration apiConfig = new SecHubWebScanApiConfiguration(); apiConfig.setType(SecHubWebScanApiType.OPEN_API); apiConfig.getNamesOfUsedDataConfigurationObjects().add("openApi-file-reference"); + URL apiDefinitionUrl = new URL("https://www.example.org/api/v1/swagger/"); + apiConfig.setApiDefinitionUrl(apiDefinitionUrl); when(mockedScheduleCreateJobService.createJob(any(), any(SecHubConfiguration.class))).thenReturn(mockResult); @@ -579,7 +582,7 @@ public void restDoc_userCreatesNewJob_webscan_with_api_definition() throws Excep content(configureSecHub(). api("1.0"). webConfig(). - addURI("https://localhost/mywebapp/login"). + addURI("https://www.example.org/"). addApiConfig(apiConfig). build(). toJSON()) @@ -605,7 +608,8 @@ public void restDoc_userCreatesNewJob_webscan_with_api_definition() throws Excep fieldWithPath(PROPERTY_WEB_SCAN).description("Webscan configuration block").optional(), fieldWithPath(PROPERTY_WEB_SCAN+"."+SecHubWebScanConfiguration.PROPERTY_URL).description("Webscan URI to scan for").optional(), fieldWithPath(PROPERTY_WEB_SCAN+"."+SecHubWebScanConfiguration.PROPERTY_API+"."+SecHubWebScanApiConfiguration.PROPERTY_TYPE).description("Type of the API definition files that will be provided").optional(), - fieldWithPath(PROPERTY_WEB_SCAN+"."+SecHubWebScanConfiguration.PROPERTY_API+"."+SecHubDataConfigurationUsageByName.PROPERTY_USE).description("Reference to the data section containing the API definition files. Always use 'sources' with 'files' instead 'folders'.").optional() + fieldWithPath(PROPERTY_WEB_SCAN+"."+SecHubWebScanConfiguration.PROPERTY_API+"."+SecHubDataConfigurationUsageByName.PROPERTY_USE).description("Reference to the data section containing the API definition files. Always use 'sources' with 'files' instead 'folders'.").optional(), + fieldWithPath(PROPERTY_WEB_SCAN+"."+SecHubWebScanConfiguration.PROPERTY_API+"."+SecHubWebScanApiConfiguration.PROPERTY_API_DEFINITION_URL).description("Specifies an URL to read the API definition from.").optional() ), responseFields( fieldWithPath(SchedulerResult.PROPERTY_JOBID).description("A unique job id") diff --git a/sechub-wrapper-owasp-zap/src/main/java/com/mercedesbenz/sechub/zapwrapper/internal/scan/ClientApiFacade.java b/sechub-wrapper-owasp-zap/src/main/java/com/mercedesbenz/sechub/zapwrapper/internal/scan/ClientApiFacade.java index d3d23e0134..ee506736a0 100644 --- a/sechub-wrapper-owasp-zap/src/main/java/com/mercedesbenz/sechub/zapwrapper/internal/scan/ClientApiFacade.java +++ b/sechub-wrapper-owasp-zap/src/main/java/com/mercedesbenz/sechub/zapwrapper/internal/scan/ClientApiFacade.java @@ -1,6 +1,7 @@ // SPDX-License-Identifier: MIT package com.mercedesbenz.sechub.zapwrapper.internal.scan; +import java.net.URL; import java.util.ArrayList; import java.util.List; @@ -25,9 +26,11 @@ public ClientApiFacade(ClientApi clientApi) { /** * Create new context inside the ZAP with the given name. * - * @param contextName - * @return contextId returned by ZAP - * @throws ClientApiException + * @param contextName context name that will be created inside the current ZAP + * session + * @return api response of ZAP contextId returned by ZAP + * @throws ClientApiException when anything goes wrong communicating with ZAP + * when anything goes wrong communicating with ZAP */ public String createNewContext(String contextName) throws ClientApiException { ApiResponseElement createContextResponse = ((ApiResponseElement) clientApi.context.newContext(contextName)); @@ -38,21 +41,23 @@ public String createNewContext(String contextName) throws ClientApiException { * Create a new session inside the ZAP. Overwriting files if the parameter is * set. * - * @param contextName - * @param overwrite - * @return - * @throws ClientApiException + * @param contextName default context name inside the new ZAP session + * @param overwrite force the overwrite of the current session + * @return api response of ZAP api response of ZAP + * + * @throws ClientApiException when anything goes wrong communicating with ZAP */ public ApiResponse createNewSession(String contextName, String overwrite) throws ClientApiException { return clientApi.core.newSession(contextName, overwrite); } /** - * Set maximum alerts for rule. + * Set maximum alerts a rule can raise. * - * @param maximum - * @return - * @throws ClientApiException + * @param maximum specifies the maximum number of alerts each rule can raise. + * Setting "0" means unlimited amount of alerts for each rule. + * @return api response of ZAP + * @throws ClientApiException when anything goes wrong communicating with ZAP */ public ApiResponse configureMaximumAlertsForEachRule(String maximum) throws ClientApiException { return clientApi.core.setOptionMaximumAlertInstances(maximum); @@ -61,8 +66,8 @@ public ApiResponse configureMaximumAlertsForEachRule(String maximum) throws Clie /** * Enables all passive rules. * - * @return - * @throws ClientApiException + * @return api response of ZAP + * @throws ClientApiException when anything goes wrong communicating with ZAP */ public ApiResponse enableAllPassiveScannerRules() throws ClientApiException { return clientApi.pscan.enableAllScanners(); @@ -71,9 +76,10 @@ public ApiResponse enableAllPassiveScannerRules() throws ClientApiException { /** * Enable all active rules for the given policy. * - * @param policy - * @return - * @throws ClientApiException + * @param policy specifies the policy that will be configured. Configuring + * null configures the default policy. + * @return api response of ZAP + * @throws ClientApiException when anything goes wrong communicating with ZAP */ public ApiResponse enableAllActiveScannerRulesForPolicy(String policy) throws ClientApiException { return clientApi.ascan.enableAllScanners(null); @@ -82,19 +88,20 @@ public ApiResponse enableAllActiveScannerRulesForPolicy(String policy) throws Cl /** * Set the Browser used by the AjaxSpider. * - * @param browserId - * @return - * @throws ClientApiException + * @param browserId Id of the browser that shall be used by ZAP + * @return api response of ZAP + * @throws ClientApiException when anything goes wrong communicating with ZAP */ public ApiResponse configureAjaxSpiderBrowserId(String browserId) throws ClientApiException { return clientApi.ajaxSpider.setOptionBrowserId(browserId); } /** + * Disable passive rule by given ruleId. * - * @param ruleId - * @return - * @throws ClientApiException + * @param ruleId id of the rule that will be disabled + * @return api response of ZAP + * @throws ClientApiException when anything goes wrong communicating with ZAP */ public ApiResponse disablePassiveScannerRule(String ruleId) throws ClientApiException { return clientApi.pscan.disableScanners(ruleId); @@ -103,10 +110,11 @@ public ApiResponse disablePassiveScannerRule(String ruleId) throws ClientApiExce /** * Disable the given rule by ID inside the given policy. * - * @param ruleId - * @param policy - * @return - * @throws ClientApiException + * @param ruleId id of the rule that will be disabled + * @param policy specifies the policy that will be configured. Configuring + * null configures the default policy. + * @return api response of ZAP + * @throws ClientApiException when anything goes wrong communicating with ZAP */ public ApiResponse disableActiveScannerRuleForPolicy(String ruleId, String policy) throws ClientApiException { return clientApi.ascan.disableScanners(ruleId, null); @@ -115,35 +123,37 @@ public ApiResponse disableActiveScannerRuleForPolicy(String ruleId, String polic /** * Set HTTP proxy with the given parameters. * - * @param host - * @param port - * @param realm - * @param username - * @param password - * @return - * @throws ClientApiException + * @param host hostname of the proxy + * @param port port of the proxy + * @param realm realm of the proxy + * @param username username to access the proxy + * @param password password to access the proxy + * @return api response of ZAP + * @throws ClientApiException when anything goes wrong communicating with ZAP */ public ApiResponse configureHttpProxy(String host, String port, String realm, String username, String password) throws ClientApiException { return clientApi.network.setHttpProxy(host, port, realm, username, password); } /** - * Set usage of a HTTP proxy. + * Enable or disable HTTP proxy. * - * @param enabled - * @return - * @throws ClientApiException + * @param enabled if "true" proxy will be used by ZAP, if "false" proxy will not + * be used by ZAP. + * @return api response of ZAP + * @throws ClientApiException when anything goes wrong communicating with ZAP */ public ApiResponse setHttpProxyEnabled(String enabled) throws ClientApiException { return clientApi.network.setHttpProxyEnabled(enabled); } /** - * Set usage of HTTP proxy authentication. + * Enable or disable HTTP proxy authentication. * - * @param enabled - * @return - * @throws ClientApiException + * @param enabled if "true" proxy will be used by ZAP, if "false" proxy will not + * be used by ZAP. + * @return api response of ZAP + * @throws ClientApiException when anything goes wrong communicating with ZAP */ public ApiResponse setHttpProxyAuthEnabled(String enabled) throws ClientApiException { return clientApi.network.setHttpProxyAuthEnabled(enabled); @@ -153,16 +163,21 @@ public ApiResponse setHttpProxyAuthEnabled(String enabled) throws ClientApiExcep * Add replacer rule. If a entry already exists from the last scan it is * replaced. * - * @param description - * @param enabled - * @param matchtype - * @param matchregex - * @param matchstring - * @param replacement - * @param initiators - * @param url - * @return - * @throws ClientApiException + * @param description Id of the created or overwritten replacer rule + * @param enabled "true"/"false" to enable/disable the replacer rule + * @param matchtype is one of [REQ_HEADER, REQ_HEADER_STR, REQ_BODY_STR, + * RESP_HEADER, RESP_HEADER_STR, RESP_BODY_STR] + * @param matchregex "true" if the matchString shall be treated as regex. When + * "false" simple string comparison is used. + * @param matchstring matchString is the string that will be matched against + * @param replacement replacement is the replacement string + * @param initiators initiators may be blank (for all initiators) or a comma + * separated list of integers as defined in HttpSender + * @param url pattern this replacer rule shall be used for. If + * null it is applied for any URL. + * @return api response of ZAP + * @throws ClientApiException when anything goes wrong communicating with ZAP */ public ApiResponse addReplacerRule(String description, String enabled, String matchtype, String matchregex, String matchstring, String replacement, String initiators, String url) throws ClientApiException { @@ -180,10 +195,11 @@ public ApiResponse addReplacerRule(String description, String enabled, String ma /** * Include URL pattern to the given context. * - * @param contextName - * @param urlPattern - * @return - * @throws ClientApiException + * @param contextName name of the context the given URL pattern shall be added + * to + * @param urlPattern regex URL pattern + * @return api response of ZAP + * @throws ClientApiException when anything goes wrong communicating with ZAP */ public ApiResponse addIncludeUrlPatternToContext(String contextName, String urlPattern) throws ClientApiException { return clientApi.context.includeInContext(contextName, urlPattern); @@ -192,10 +208,11 @@ public ApiResponse addIncludeUrlPatternToContext(String contextName, String urlP /** * Exclude URL pattern from the given context. * - * @param contextName - * @param urlPattern - * @return - * @throws ClientApiException + * @param contextName name of the context the given URL pattern shall be added + * to + * @param urlPattern regex URL pattern + * @return api response of ZAP + * @throws ClientApiException when anything goes wrong communicating with ZAP */ public ApiResponse addExcludeUrlPatternToContext(String contextName, String urlPattern) throws ClientApiException { return clientApi.context.excludeFromContext(contextName, urlPattern); @@ -205,9 +222,11 @@ public ApiResponse addExcludeUrlPatternToContext(String contextName, String urlP * Access an URL through the ZAP. Successfully accessing the site will add it to * the site tree. * - * @param url - * @param followRedirects - * @return ApiResponse of ZAP or null when URL was not accessible. + * @param url must be a valid URL the ZAP can access. + * @param followRedirects "true"/"false" depending if you want the ZAP to follow + * redirects or not. + * @return api response of ZAP ApiResponse of ZAP or null when URL + * was not accessible. */ public ApiResponse accessUrlViaZap(String url, String followRedirects) { ApiResponse response = null; @@ -227,21 +246,38 @@ public ApiResponse accessUrlViaZap(String url, String followRedirects) { * @param openApiFile * @param url * @param contextId - * @return - * @throws ClientApiException + * @return api response of ZAP + * @throws ClientApiException when anything goes wrong communicating with ZAP */ public ApiResponse importOpenApiFile(String openApiFile, String url, String contextId) throws ClientApiException { return clientApi.openapi.importFile(openApiFile, url, contextId); } + /** + * Import the given openApi in the context from the given apiDefinitionUrl. + * While importing from the URL the ZAP tries to access all API endpoints via + * the given targetUrl and adds them to the sites tree if they could be + * accessed. + * + * @param apiDefinitionUrl URL with the openApi/swagger definition + * @param targetUrl targetUrl of the application, generally the base URL + * @param contextId Id of the context to which the API definitions shall + * be added + * @return api response of ZAP + * @throws ClientApiException when anything goes wrong communicating with ZAP + */ + public ApiResponse importOpenApiDefintionFromUrl(URL apiDefinitionUrl, String targetUrl, String contextId) throws ClientApiException { + return clientApi.openapi.importUrl(apiDefinitionUrl.toString(), targetUrl, contextId); + } + /** * Import the given PKCS12 client certificate using the optional client * certificates password if necessary. * - * @param filepath - * @param password - * @return - * @throws ClientApiException + * @param filepath path to the PKCS12 certificate + * @param password password of the certificate file + * @return api response of ZAP + * @throws ClientApiException when anything goes wrong communicating with ZAP */ public ApiResponse importPkcs12ClientCertificate(String filepath, String password) throws ClientApiException { // add the client certificate to the list ZAP keeps inside the network add-on @@ -250,6 +286,12 @@ public ApiResponse importPkcs12ClientCertificate(String filepath, String passwor return clientApi.network.addPkcs12ClientCertificate(filepath, password, "0"); } + /** + * Enable client certificate. + * + * @return api response of ZAP + * @throws ClientApiException when anything goes wrong communicating with ZAP + */ public ApiResponse enableClientCertificate() throws ClientApiException { return clientApi.network.setUseClientCertificate("true"); } @@ -268,8 +310,8 @@ public ApiResponse disableClientCertificate() throws ClientApiException { * the user and write a report which is empty or contains at least the passively * detected results. * - * @return - * @throws ClientApiException + * @return true if at least one URL was detected by ZAP, false otherwise + * @throws ClientApiException when anything goes wrong communicating with ZAP */ public boolean atLeastOneURLDetected() throws ClientApiException { ApiResponseList sitesList = (ApiResponseList) clientApi.core.sites(); @@ -278,35 +320,16 @@ public boolean atLeastOneURLDetected() throws ClientApiException { /** * Removes a replacer rule by the given description. (Description is the ID for - * the replacer rule) + * the replacer rule inside ZAP) * - * @param description - * @return - * @throws ClientApiException + * @param description Id of the replacer rule that shall be removed + * @return api response of ZAP + * @throws ClientApiException when anything goes wrong communicating with ZAP */ public ApiResponse removeReplacerRule(String description) throws ClientApiException { return clientApi.replacer.removeRule(description); } - /** - * Generate a report for the given parameters. - * - * @param title - * @param template - * @param theme - * @param description - * @param contexts - * @param sites - * @param sections - * @param includedconfidences - * @param includedrisks - * @param reportfilename - * @param reportfilenamepattern - * @param reportdir - * @param display - * @return - * @throws ClientApiException - */ public ApiResponse generateReport(String title, String template, String theme, String description, String contexts, String sites, String sections, String includedconfidences, String includedrisks, String reportfilename, String reportfilenamepattern, String reportdir, String display) throws ClientApiException { @@ -319,7 +342,7 @@ public ApiResponse generateReport(String title, String template, String theme, S * * @return The status as string after the ajax spider scan is started it is * either "running" or "stopped". - * @throws ClientApiException + * @throws ClientApiException when anything goes wrong communicating with ZAP */ public String getAjaxSpiderStatus() throws ClientApiException { return ((ApiResponseElement) clientApi.ajaxSpider.status()).getValue(); @@ -328,8 +351,8 @@ public String getAjaxSpiderStatus() throws ClientApiException { /** * Stop the ajax spider. * - * @return - * @throws ClientApiException + * @return api response of ZAP + * @throws ClientApiException when anything goes wrong communicating with ZAP */ public ApiResponse stopAjaxSpider() throws ClientApiException { return clientApi.ajaxSpider.stop(); @@ -338,9 +361,9 @@ public ApiResponse stopAjaxSpider() throws ClientApiException { /** * Stop the spider for the given scan ID. * - * @param scanId - * @return - * @throws ClientApiException + * @param scanId spider Id + * @return api response of ZAP + * @throws ClientApiException when anything goes wrong communicating with ZAP */ public ApiResponse stopSpiderScan(String scanId) throws ClientApiException { return clientApi.spider.stop(scanId); @@ -349,8 +372,8 @@ public ApiResponse stopSpiderScan(String scanId) throws ClientApiException { /** * Get a list of all URLs detected by the spider scan. * - * @return - * @throws ClientApiException + * @return api response of ZAP + * @throws ClientApiException when anything goes wrong communicating with ZAP */ public List getAllSpiderUrls() throws ClientApiException { List results = ((ApiResponseList) clientApi.spider.allUrls()).getItems(); @@ -364,10 +387,10 @@ public List getAllSpiderUrls() throws ClientApiException { /** * Get the status of the spider scan with a specific scan ID. * - * @param scanId - * @return The status as a number between 0 and 100. (percentage of scan - * completion) - * @throws ClientApiException + * @param scanId spider Id + * @return api response of ZAP The status as a number between 0 and 100. + * (percentage of scan completion) + * @throws ClientApiException when anything goes wrong communicating with ZAP */ public int getSpiderStatusForScan(String scanId) throws ClientApiException { ApiResponseElement status = (ApiResponseElement) clientApi.spider.status(scanId); @@ -377,9 +400,9 @@ public int getSpiderStatusForScan(String scanId) throws ClientApiException { /** * Get the number of records left to scan for the passive scan. * - * @param scanId - * @return - * @throws ClientApiException + * @param scanId passive scan Id + * @return api response of ZAP + * @throws ClientApiException when anything goes wrong communicating with ZAP */ public int getNumberOfPassiveScannerRecordsToScan() throws ClientApiException { ApiResponseElement recordsToScan = (ApiResponseElement) clientApi.pscan.recordsToScan(); @@ -389,9 +412,9 @@ public int getNumberOfPassiveScannerRecordsToScan() throws ClientApiException { /** * Stop the active scanner for the given scan ID. * - * @param scanId - * @return - * @throws ClientApiException + * @param scanId active scan Id + * @return api response of ZAP + * @throws ClientApiException when anything goes wrong communicating with ZAP */ public ApiResponse stopActiveScan(String scanId) throws ClientApiException { return clientApi.ascan.stop(scanId); @@ -400,10 +423,10 @@ public ApiResponse stopActiveScan(String scanId) throws ClientApiException { /** * Get the status of the active scan with a specific scan ID. * - * @param scanId - * @return The status as a number between 0 and 100. (percentage of scan - * completion) - * @throws ClientApiException + * @param scanId active scan Id + * @return api response of ZAP The status as a number between 0 and 100. + * (percentage of scan completion) + * @throws ClientApiException when anything goes wrong communicating with ZAP */ public int getActiveScannerStatusForScan(String scanId) throws ClientApiException { ApiResponseElement status = (ApiResponseElement) clientApi.ascan.status(scanId); @@ -413,13 +436,14 @@ public int getActiveScannerStatusForScan(String scanId) throws ClientApiExceptio /** * Start the spider with the given parameters. * - * @param targetUrlAsString - * @param maxChildren - * @param recurse - * @param contextName - * @param subTreeOnly - * @return the ID of the started spider scan - * @throws ClientApiException + * @param targetUrlAsString URL to scan + * @param maxChildren limit the number of children scanned + * @param recurse "true"/"false" to prevent the spider from seeding + * recursively + * @param contextName the context that shall be used for this scan + * @param subTreeOnly restrict the spider under a site's subtree + * @return api response of ZAP the ID of the started spider scan + * @throws ClientApiException when anything goes wrong communicating with ZAP */ public String startSpiderScan(String targetUrlAsString, String maxChildren, String recurse, String contextName, String subTreeOnly) throws ClientApiException { @@ -430,12 +454,13 @@ public String startSpiderScan(String targetUrlAsString, String maxChildren, Stri /** * Start the ajax spider with the given parameters. * - * @param targetUrlAsString - * @param inScope - * @param contextName - * @param subTreeOnly - * @return the response of the ZAP API call - * @throws ClientApiException + * @param targetUrlAsString URL to scan + * @param inScope "true"/"false" either you want to scan only in scope + * or beyond + * @param contextName the context that shall be used for this scan + * @param subTreeOnly restrict the ajax spider under a site's subtree + * @return api response of ZAP the response of the ZAP API call + * @throws ClientApiException when anything goes wrong communicating with ZAP */ public ApiResponse startAjaxSpiderScan(String targetUrlAsString, String inScope, String contextName, String subTreeOnly) throws ClientApiException { return clientApi.ajaxSpider.scan(targetUrlAsString, inScope, contextName, subTreeOnly); @@ -444,14 +469,17 @@ public ApiResponse startAjaxSpiderScan(String targetUrlAsString, String inScope, /** * Start the active scanner with the given parameters. * - * @param targetUrlAsString - * @param recurse - * @param inScopeOnly - * @param scanPolicyName - * @param method - * @param postData - * @return the ID of the started active scan - * @throws ClientApiException + * @param targetUrlAsString URL to scan + * @param recurse "true"/"false" to prevent the active scan from + * seeding recursively + * @param inScopeOnly "true"/"false" either you want to scan only in scope + * or beyond + * @param scanPolicyName active scan rule policy to use for the scan + * null means default + * @param method method to use + * @param postData explicit post data + * @return api response of ZAP the ID of the started active scan + * @throws ClientApiException when anything goes wrong communicating with ZAP */ public String startActiveScan(String targetUrlAsString, String recurse, String inScopeOnly, String scanPolicyName, String method, String postData) throws ClientApiException { @@ -462,14 +490,15 @@ public String startActiveScan(String targetUrlAsString, String recurse, String i /** * Start the spider with the given parameters as the given user. * - * @param contextId - * @param userId - * @param url - * @param maxchildren - * @param recurse - * @param subtreeonly - * @return the ID of the started spider scan - * @throws ClientApiException + * @param contextId Id of the context to use + * @param userId Id of the user to use + * @param url target URL to scan + * @param maxchildren limit the number of children scanned + * @param recurse "true"/"false" to prevent the spider from seeding + * recursively + * @param subtreeonly restrict the spider under a site's subtree + * @return api response of ZAP the ID of the started spider scan + * @throws ClientApiException when anything goes wrong communicating with ZAP */ public String startSpiderScanAsUser(String contextId, String userId, String url, String maxchildren, String recurse, String subtreeonly) throws ClientApiException { @@ -480,12 +509,12 @@ public String startSpiderScanAsUser(String contextId, String userId, String url, /** * Start the ajax spider with the given parameters as the given user. * - * @param contextname - * @param username - * @param url - * @param subtreeonly - * @return the response of the ZAP API call - * @throws ClientApiException + * @param contextname the context that shall be used for this scan + * @param username name of the user that shall be used for the scan + * @param url target URL to scan + * @param subtreeonly restrict the ajax spider under a site's subtree + * @return api response of ZAP the response of the ZAP API call + * @throws ClientApiException when anything goes wrong communicating with ZAP */ public ApiResponse startAjaxSpiderScanAsUser(String contextname, String username, String url, String subtreeonly) throws ClientApiException { return clientApi.ajaxSpider.scanAsUser(contextname, username, url, subtreeonly); @@ -494,15 +523,17 @@ public ApiResponse startAjaxSpiderScanAsUser(String contextname, String username /** * Start the active scanner with the given parameters as the given user. * - * @param url - * @param contextId - * @param userId - * @param recurse - * @param scanpolicyname - * @param method - * @param postdata - * @return the ID of the started active scan - * @throws ClientApiException + * @param url target URL to scan + * @param contextId Id of the context to use + * @param userId Id of the user to use + * @param recurse "true"/"false" to prevent the active scan from seeding + * recursively + * @param scanpolicyname active scan rule policy to use for the scan + * null means default + * @param method method to use + * @param postData explicit post data + * @return api response of ZAP the ID of the started active scan + * @throws ClientApiException when anything goes wrong communicating with ZAP */ public String startActiveScanAsUser(String url, String contextId, String userId, String recurse, String scanpolicyname, String method, String postdata) throws ClientApiException { @@ -513,11 +544,12 @@ public String startActiveScanAsUser(String url, String contextId, String userId, /** * Configure the given authentication method for the given context. * - * @param contextId - * @param authMethodName - * @param authMethodConfigParams - * @return - * @throws ClientApiException + * @param contextId Id of the context to use + * @param authMethodName Id of the authentication method + * @param authMethodConfigParams required parameters for the authentication + * method + * @return api response of ZAP + * @throws ClientApiException when anything goes wrong communicating with ZAP */ public ApiResponse configureAuthenticationMethod(String contextId, String authMethodName, String authMethodConfigParams) throws ClientApiException { return clientApi.authentication.setAuthenticationMethod(contextId, authMethodName, authMethodConfigParams); @@ -526,11 +558,12 @@ public ApiResponse configureAuthenticationMethod(String contextId, String authMe /** * Set session management method for the given context. * - * @param contextId - * @param methodName - * @param methodconfigparams - * @return - * @throws ClientApiException + * @param contextId Id of the context to use + * @param methodName Id of the session management method + * @param methodconfigparams required parameters of the session management + * method + * @return api response of ZAP + * @throws ClientApiException when anything goes wrong communicating with ZAP */ public ApiResponse setSessionManagementMethod(String contextId, String methodName, String methodconfigparams) throws ClientApiException { return clientApi.sessionManagement.setSessionManagementMethod(contextId, methodName, methodconfigparams); @@ -539,10 +572,10 @@ public ApiResponse setSessionManagementMethod(String contextId, String methodNam /** * Create a new user inside the given context. * - * @param contextId - * @param username - * @return - * @throws ClientApiException + * @param contextId Id of the context to use + * @param username Name of the user that shall be created + * @return api response of ZAP + * @throws ClientApiException when anything goes wrong communicating with ZAP */ public String createNewUser(String contextId, String username) throws ClientApiException { ApiResponseElement creatUserResponse = ((ApiResponseElement) clientApi.users.newUser(contextId, username)); @@ -552,11 +585,13 @@ public String createNewUser(String contextId, String username) throws ClientApiE /** * Set authentication credentials for the given user inside the given context. * - * @param contextId - * @param userId - * @param authCredentialsConfigParams - * @return - * @throws ClientApiException + * @param contextId Id of the context to use + * @param userId Id of the user that shall be configured + * for the scan. + * @param authCredentialsConfigParams credential configuration for the given + * context + * @return api response of ZAP + * @throws ClientApiException when anything goes wrong communicating with ZAP */ public ApiResponse configureAuthenticationCredentials(String contextId, String userId, String authCredentialsConfigParams) throws ClientApiException { return clientApi.users.setAuthenticationCredentials(contextId, userId, authCredentialsConfigParams); @@ -565,11 +600,11 @@ public ApiResponse configureAuthenticationCredentials(String contextId, String u /** * Sets whether or not the user, should be enabled inside the given context. * - * @param contextId - * @param userId - * @param enabled - * @return - * @throws ClientApiException + * @param contextId Id of the context to use + * @param userId Id of the user that shall enabled/disabled. + * @param enabled "true"/"false" to enable/disable the user + * @return api response of ZAP + * @throws ClientApiException when anything goes wrong communicating with ZAP */ public ApiResponse setUserEnabled(String contextId, String userId, String enabled) throws ClientApiException { return clientApi.users.setUserEnabled(contextId, userId, enabled); @@ -578,10 +613,10 @@ public ApiResponse setUserEnabled(String contextId, String userId, String enable /** * Set the user that will be used in forced user mode for the given context. * - * @param contextId - * @param userId - * @return - * @throws ClientApiException + * @param contextId Id of the context to use + * @param userId Id of the user to use + * @return api response of ZAP + * @throws ClientApiException when anything goes wrong communicating with ZAP */ public ApiResponse setForcedUser(String contextId, String userId) throws ClientApiException { return clientApi.forcedUser.setForcedUser(contextId, userId); @@ -590,9 +625,9 @@ public ApiResponse setForcedUser(String contextId, String userId) throws ClientA /** * Set if the forced user mode should be enabled or not. * - * @param enabled - * @return - * @throws ClientApiException + * @param enabled "true"/"false" to enable/disable the enforced user mode + * @return api response of ZAP + * @throws ClientApiException when anything goes wrong communicating with ZAP */ public ApiResponse setForcedUserModeEnabled(boolean enabled) throws ClientApiException { return clientApi.forcedUser.setForcedUserModeEnabled(enabled); diff --git a/sechub-wrapper-owasp-zap/src/main/java/com/mercedesbenz/sechub/zapwrapper/scan/ZapScanner.java b/sechub-wrapper-owasp-zap/src/main/java/com/mercedesbenz/sechub/zapwrapper/scan/ZapScanner.java index 5af77cf8e4..14149627de 100644 --- a/sechub-wrapper-owasp-zap/src/main/java/com/mercedesbenz/sechub/zapwrapper/scan/ZapScanner.java +++ b/sechub-wrapper-owasp-zap/src/main/java/com/mercedesbenz/sechub/zapwrapper/scan/ZapScanner.java @@ -4,6 +4,7 @@ import java.io.File; import java.io.IOException; import java.io.UnsupportedEncodingException; +import java.net.URL; import java.net.URLEncoder; import java.nio.charset.StandardCharsets; import java.nio.file.Files; @@ -257,18 +258,21 @@ void addIncludedAndExcludedUrlsToContext() throws ClientApiException { } void loadApiDefinitions(String zapContextId) throws ClientApiException { - if (scanContext.getApiDefinitionFiles().isEmpty()) { - LOG.info("For scan {}: No file with API definition found!", scanContext.getContextName()); - return; - } Optional apiConfig = scanContext.getSecHubWebScanConfiguration().getApi(); if (!apiConfig.isPresent()) { LOG.info("For scan {}: No API definition was found!", scanContext.getContextName()); return; } - switch (apiConfig.get().getType()) { + SecHubWebScanApiConfiguration secHubWebScanApiConfiguration = apiConfig.get(); + + switch (secHubWebScanApiConfiguration.getType()) { case OPEN_API: + URL apiDefinitionUrl = secHubWebScanApiConfiguration.getApiDefinitionUrl(); + if (apiDefinitionUrl != null) { + LOG.info("For scan {}: Loading openAPI definition from : {}", scanContext.getContextName(), apiDefinitionUrl.toString()); + clientApiFacade.importOpenApiDefintionFromUrl(apiDefinitionUrl, scanContext.getTargetUrlAsString(), zapContextId); + } for (File apiFile : scanContext.getApiDefinitionFiles()) { LOG.info("For scan {}: Loading openAPI file: {}", scanContext.getContextName(), apiFile.toString()); clientApiFacade.importOpenApiFile(apiFile.toString(), scanContext.getTargetUrlAsString(), zapContextId); diff --git a/sechub-wrapper-owasp-zap/src/test/java/com/mercedesbenz/sechub/zapwrapper/scan/ZapScannerTest.java b/sechub-wrapper-owasp-zap/src/test/java/com/mercedesbenz/sechub/zapwrapper/scan/ZapScannerTest.java index f2b498a213..39957ac8e5 100644 --- a/sechub-wrapper-owasp-zap/src/test/java/com/mercedesbenz/sechub/zapwrapper/scan/ZapScannerTest.java +++ b/sechub-wrapper-owasp-zap/src/test/java/com/mercedesbenz/sechub/zapwrapper/scan/ZapScannerTest.java @@ -20,7 +20,6 @@ import java.net.URL; import java.nio.file.Paths; import java.util.ArrayList; -import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; @@ -359,16 +358,18 @@ void set_includes_and_excludes_api_facade_is_called_once_for_each_include_and_on void import_openapi_file_but_api_file_is_null_api_facade_is_never_called() throws ClientApiException { /* prepare */ String contextId = "context-id"; - when(scanContext.getApiDefinitionFiles()).thenReturn(Collections.emptyList()); ApiResponse response = mock(ApiResponse.class); + when(scanContext.getSecHubWebScanConfiguration()).thenReturn(new SecHubWebScanConfiguration()); when(clientApiFacade.importOpenApiFile(any(), any(), any())).thenReturn(response); + when(clientApiFacade.importOpenApiDefintionFromUrl(any(), any(), any())).thenReturn(response); /* execute */ scannerToTest.loadApiDefinitions(contextId); /* test */ verify(clientApiFacade, never()).importOpenApiFile(any(), any(), any()); + verify(clientApiFacade, never()).importOpenApiDefintionFromUrl(any(), any(), any()); } @ParameterizedTest @@ -380,7 +381,7 @@ void import_openapi_file_api_facade_is_called_once(String sechubConfigFile) thro SecHubWebScanConfiguration sechubWebScanConfig = SecHubScanConfiguration.createFromJSON(json).getWebScan().get(); List apiFiles = new ArrayList<>(); - apiFiles.add(new File("examplefile.json")); + apiFiles.add(new File("openapi3.json")); when(scanContext.getApiDefinitionFiles()).thenReturn(apiFiles); when(scanContext.getSecHubWebScanConfiguration()).thenReturn(sechubWebScanConfig); @@ -395,6 +396,52 @@ void import_openapi_file_api_facade_is_called_once(String sechubConfigFile) thro verify(clientApiFacade, times(1)).importOpenApiFile(any(), any(), any()); } + @ParameterizedTest + @ValueSource(strings = { "src/test/resources/sechub-config-examples/no-auth-with-openapi-from-url.json" }) + void import_openapi_defintion_from_url_api_facade_is_called_once(String sechubConfigFile) throws ClientApiException { + /* prepare */ + String contextId = "context-id"; + String json = TestFileReader.loadTextFile(sechubConfigFile); + SecHubWebScanConfiguration sechubWebScanConfig = SecHubScanConfiguration.createFromJSON(json).getWebScan().get(); + when(scanContext.getSecHubWebScanConfiguration()).thenReturn(sechubWebScanConfig); + + ApiResponse response = mock(ApiResponse.class); + when(clientApiFacade.importOpenApiFile(any(), any(), any())).thenReturn(response); + when(clientApiFacade.importOpenApiDefintionFromUrl(any(), any(), any())).thenReturn(response); + + /* execute */ + scannerToTest.loadApiDefinitions(contextId); + + /* test */ + verify(clientApiFacade, never()).importOpenApiFile(any(), any(), any()); + verify(clientApiFacade, times(1)).importOpenApiDefintionFromUrl(any(), any(), any()); + } + + @ParameterizedTest + @ValueSource(strings = { "src/test/resources/sechub-config-examples/no-auth-with-openapi-from-file-and-url.json" }) + void import_openapi_from_file_and_from_url_api_facade_is_called_once(String sechubConfigFile) throws ClientApiException { + /* prepare */ + String contextId = "context-id"; + String json = TestFileReader.loadTextFile(sechubConfigFile); + SecHubWebScanConfiguration sechubWebScanConfig = SecHubScanConfiguration.createFromJSON(json).getWebScan().get(); + + List apiFiles = new ArrayList<>(); + apiFiles.add(new File("openapi3.json")); + + when(scanContext.getApiDefinitionFiles()).thenReturn(apiFiles); + when(scanContext.getSecHubWebScanConfiguration()).thenReturn(sechubWebScanConfig); + + ApiResponse response = mock(ApiResponse.class); + when(clientApiFacade.importOpenApiFile(any(), any(), any())).thenReturn(response); + + /* execute */ + scannerToTest.loadApiDefinitions(contextId); + + /* test */ + verify(clientApiFacade, times(1)).importOpenApiFile(any(), any(), any()); + verify(clientApiFacade, times(1)).importOpenApiDefintionFromUrl(any(), any(), any()); + } + @Test void import_client_certificate_file_but_client_certificate_file_is_null_api_facade_is_never_called() throws ClientApiException { /* prepare */ diff --git a/sechub-wrapper-owasp-zap/src/test/resources/sechub-config-examples/no-auth-with-openapi-from-file-and-url.json b/sechub-wrapper-owasp-zap/src/test/resources/sechub-config-examples/no-auth-with-openapi-from-file-and-url.json new file mode 100644 index 0000000000..dd3d2a2b4c --- /dev/null +++ b/sechub-wrapper-owasp-zap/src/test/resources/sechub-config-examples/no-auth-with-openapi-from-file-and-url.json @@ -0,0 +1,19 @@ +{ + "apiVersion" : "1.0", + "data" : { + "sources" : [ { + "name" : "open-api-file-reference", + "fileSystem" : { + "files" : [ "openapi3.json" ] + } + } ] + }, + "webScan" : { + "url" : "https://localhost:8443", + "api" : { + "type" : "openApi", + "apiDefinitionUrl" : "https://example.com/api/v1/swagger/?format=openapi", + "use" : [ "open-api-file-reference" ] + } + } +} \ No newline at end of file diff --git a/sechub-wrapper-owasp-zap/src/test/resources/sechub-config-examples/no-auth-with-openapi-from-url.json b/sechub-wrapper-owasp-zap/src/test/resources/sechub-config-examples/no-auth-with-openapi-from-url.json new file mode 100644 index 0000000000..a53bf2dd7e --- /dev/null +++ b/sechub-wrapper-owasp-zap/src/test/resources/sechub-config-examples/no-auth-with-openapi-from-url.json @@ -0,0 +1,10 @@ +{ + "apiVersion" : "1.0", + "webScan" : { + "url" : "https://localhost:8443", + "api" : { + "type" : "openApi", + "apiDefinitionUrl" : "https://example.com/api/v1/swagger/?format=openapi" + } + } +} \ No newline at end of file