From 9b34ad4b09fd7c9d37de5ac2d00feec0c826bd67 Mon Sep 17 00:00:00 2001 From: Jan Winz Date: Tue, 26 Nov 2024 08:26:35 +0100 Subject: [PATCH] Refactoring to be able to close the browser earlier #3622 - add a ScriptLoginResult which contains all the session data - update the current script login to use the ScriptLoginResult - update and extend testcases --- .../scan/login/ScriptLoginResult.java | 60 ++++++++++++++ .../zapwrapper/scan/login/ZapScriptLogin.java | 33 +++----- ...=> ZapScriptLoginSessionConfigurator.java} | 49 ++++------- .../login/ZapScriptLoginWebDriverFactory.java | 2 + .../login/ZapWrapperGroovyScriptExecutor.java | 71 +++++++++++++--- .../sechub/zapwrapper/util/TOTPGenerator.java | 4 +- ...apScriptLoginSessionConfiguratorTest.java} | 82 ++++++++++++------- .../scan/login/ZapScriptLoginTest.java | 75 +++++++---------- .../ZapWrapperGroovyScriptExecutorTest.java | 55 ++++++++++--- 9 files changed, 279 insertions(+), 152 deletions(-) create mode 100644 sechub-wrapper-owasp-zap/src/main/java/com/mercedesbenz/sechub/zapwrapper/scan/login/ScriptLoginResult.java rename sechub-wrapper-owasp-zap/src/main/java/com/mercedesbenz/sechub/zapwrapper/scan/login/{ZapScriptLoginSessionGrabber.java => ZapScriptLoginSessionConfigurator.java} (66%) rename sechub-wrapper-owasp-zap/src/test/java/com/mercedesbenz/sechub/zapwrapper/scan/login/{ZapScriptLoginSessionGrabberTest.java => ZapScriptLoginSessionConfiguratorTest.java} (58%) diff --git a/sechub-wrapper-owasp-zap/src/main/java/com/mercedesbenz/sechub/zapwrapper/scan/login/ScriptLoginResult.java b/sechub-wrapper-owasp-zap/src/main/java/com/mercedesbenz/sechub/zapwrapper/scan/login/ScriptLoginResult.java new file mode 100644 index 000000000..f436fc070 --- /dev/null +++ b/sechub-wrapper-owasp-zap/src/main/java/com/mercedesbenz/sechub/zapwrapper/scan/login/ScriptLoginResult.java @@ -0,0 +1,60 @@ +// SPDX-License-Identifier: MIT +package com.mercedesbenz.sechub.zapwrapper.scan.login; + +import java.util.*; + +import org.openqa.selenium.Cookie; + +public class ScriptLoginResult { + + private Set sessionCookies; + private Map localStorage; + private Map sessionStorage; + + private boolean loginFailed; + + public ScriptLoginResult() { + this.sessionCookies = new HashSet<>(); + this.localStorage = new HashMap<>(); + this.sessionStorage = new HashMap<>(); + } + + public Set getSessionCookies() { + return Collections.unmodifiableSet(sessionCookies); + } + + public void setSessionCookies(Set sessionCookies) { + if (sessionCookies != null) { + this.sessionCookies = sessionCookies; + } + } + + public Map getLocalStorage() { + return Collections.unmodifiableMap(localStorage); + } + + public void setLocalStorage(Map localStorage) { + if (localStorage != null) { + this.localStorage = localStorage; + } + } + + public Map getSessionStorage() { + return Collections.unmodifiableMap(sessionStorage); + } + + public void setSessionStorage(Map sessionStorage) { + if (sessionStorage != null) { + this.sessionStorage = sessionStorage; + } + } + + public boolean isLoginFailed() { + return loginFailed; + } + + public void setLoginFailed(boolean loginFailed) { + this.loginFailed = loginFailed; + } + +} diff --git a/sechub-wrapper-owasp-zap/src/main/java/com/mercedesbenz/sechub/zapwrapper/scan/login/ZapScriptLogin.java b/sechub-wrapper-owasp-zap/src/main/java/com/mercedesbenz/sechub/zapwrapper/scan/login/ZapScriptLogin.java index 092b1960f..827ef9879 100644 --- a/sechub-wrapper-owasp-zap/src/main/java/com/mercedesbenz/sechub/zapwrapper/scan/login/ZapScriptLogin.java +++ b/sechub-wrapper-owasp-zap/src/main/java/com/mercedesbenz/sechub/zapwrapper/scan/login/ZapScriptLogin.java @@ -2,11 +2,7 @@ package com.mercedesbenz.sechub.zapwrapper.scan.login; import java.io.File; -import java.io.IOException; -import javax.script.ScriptException; - -import org.openqa.selenium.firefox.FirefoxDriver; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.zaproxy.clientapi.core.ClientApiException; @@ -19,19 +15,16 @@ public class ZapScriptLogin { private static final Logger LOG = LoggerFactory.getLogger(ZapScriptLogin.class); - private ZapScriptLoginWebDriverFactory webDriverFactory; private ZapWrapperGroovyScriptExecutor groovyScriptExecutor; - private ZapScriptLoginSessionGrabber sessionGrabber; + private ZapScriptLoginSessionConfigurator sessionConfigurator; public ZapScriptLogin() { - this(new ZapScriptLoginWebDriverFactory(), new ZapWrapperGroovyScriptExecutor(), new ZapScriptLoginSessionGrabber()); + this(new ZapWrapperGroovyScriptExecutor(), new ZapScriptLoginSessionConfigurator()); } - ZapScriptLogin(ZapScriptLoginWebDriverFactory webDriverFactory, ZapWrapperGroovyScriptExecutor groovyScriptExecutor, - ZapScriptLoginSessionGrabber sessionGrabber) { - this.webDriverFactory = webDriverFactory; + ZapScriptLogin(ZapWrapperGroovyScriptExecutor groovyScriptExecutor, ZapScriptLoginSessionConfigurator sessionConfigurator) { this.groovyScriptExecutor = groovyScriptExecutor; - this.sessionGrabber = sessionGrabber; + this.sessionConfigurator = sessionConfigurator; } /** @@ -49,26 +42,22 @@ public String login(ZapScanContext scanContext, ClientApiWrapper clientApiWrappe "Expected a groovy script file to perform login, but no script was found. Cannot perform script login without the script file.", ZapWrapperExitCode.PDS_CONFIGURATION_ERROR); } - LOG.info("Creating selenium web driver."); - FirefoxDriver firefox = webDriverFactory.createFirefoxWebdriver(scanContext.getProxyInformation(), true); + LOG.info("Calling groovy script executor to execute login script."); + ScriptLoginResult loginResult = groovyScriptExecutor.executeScript(groovyScriptLoginFile, scanContext); + if (loginResult.isLoginFailed()) { + throw new ZapWrapperRuntimeException("An error happened during script login.", ZapWrapperExitCode.PRODUCT_EXECUTION_ERROR); + } try { - LOG.info("Calling groovy script executor to execute login script."); - groovyScriptExecutor.executeScript(groovyScriptLoginFile, firefox, scanContext); - LOG.info("Calling session grabber to read the HTTP session data and pass them to ZAP."); - return sessionGrabber.extractSessionAndPassToZAP(firefox, scanContext.getTargetUrlAsString(), clientApiWrapper); - } catch (IOException | ScriptException e) { - throw new ZapWrapperRuntimeException("An error happened while executing the script file.", e, ZapWrapperExitCode.IO_ERROR); + return sessionConfigurator.passSessionDataToZAP(loginResult, scanContext.getTargetUrlAsString(), clientApiWrapper); } catch (ClientApiException e) { throw new ZapWrapperRuntimeException("An error happened while grabbing the session data.", e, ZapWrapperExitCode.PRODUCT_EXECUTION_ERROR); - } finally { - firefox.quit(); } } public void cleanUpScriptLoginData(String targetUrl, ClientApiWrapper clientApiWrapper) throws ClientApiException { - sessionGrabber.cleanUpOldSessionDataIfNecessary(targetUrl, clientApiWrapper); + sessionConfigurator.cleanUpOldSessionDataIfNecessary(targetUrl, clientApiWrapper); } } diff --git a/sechub-wrapper-owasp-zap/src/main/java/com/mercedesbenz/sechub/zapwrapper/scan/login/ZapScriptLoginSessionGrabber.java b/sechub-wrapper-owasp-zap/src/main/java/com/mercedesbenz/sechub/zapwrapper/scan/login/ZapScriptLoginSessionConfigurator.java similarity index 66% rename from sechub-wrapper-owasp-zap/src/main/java/com/mercedesbenz/sechub/zapwrapper/scan/login/ZapScriptLoginSessionGrabber.java rename to sechub-wrapper-owasp-zap/src/main/java/com/mercedesbenz/sechub/zapwrapper/scan/login/ZapScriptLoginSessionConfigurator.java index 45af4fe7a..f05f25e9b 100644 --- a/sechub-wrapper-owasp-zap/src/main/java/com/mercedesbenz/sechub/zapwrapper/scan/login/ZapScriptLoginSessionGrabber.java +++ b/sechub-wrapper-owasp-zap/src/main/java/com/mercedesbenz/sechub/zapwrapper/scan/login/ZapScriptLoginSessionConfigurator.java @@ -4,56 +4,52 @@ import java.util.Map; import org.openqa.selenium.Cookie; -import org.openqa.selenium.JavascriptExecutor; -import org.openqa.selenium.firefox.FirefoxDriver; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.zaproxy.clientapi.core.ClientApiException; import com.mercedesbenz.sechub.zapwrapper.internal.scan.ClientApiWrapper; -public class ZapScriptLoginSessionGrabber { - private static final Logger LOG = LoggerFactory.getLogger(ZapScriptLoginSessionGrabber.class); +public class ZapScriptLoginSessionConfigurator { + private static final Logger LOG = LoggerFactory.getLogger(ZapScriptLoginSessionConfigurator.class); private static final String JWT_REPLACER_DESCRIPTION = "JWT"; private static final String SESSION_TOKEN_IDENTIFIER = "session-token"; private static final String SESSION_IDENTIFIER = "authenticated-session"; - private static final String LOCAL_STORAGE = "localStorage"; - private static final String SESSION_STORAGE = "sessionStorage"; - private JWTSupport jwtSupport; - public ZapScriptLoginSessionGrabber() { + public ZapScriptLoginSessionConfigurator() { this(new JWTSupport()); } - ZapScriptLoginSessionGrabber(JWTSupport jwtSupport) { + ZapScriptLoginSessionConfigurator(JWTSupport jwtSupport) { this.jwtSupport = jwtSupport; } /** - * The sessionGrabber will add all necessary session data to ZAP. + * This method will add all necessary session data to ZAP. ZAP can use this + * authenticated session for scanning. * - * @param firefox + * @param loginResult * @param targetUrl * @param clientApiWrapper * @return the name/identifier of the authenticated session inside ZAP * @throws ClientApiException */ - public String extractSessionAndPassToZAP(FirefoxDriver firefox, String targetUrl, ClientApiWrapper clientApiWrapper) throws ClientApiException { + public String passSessionDataToZAP(ScriptLoginResult loginResult, String targetUrl, ClientApiWrapper clientApiWrapper) throws ClientApiException { cleanUpOldSessionDataIfNecessary(targetUrl, clientApiWrapper); clientApiWrapper.addHTTPSessionToken(targetUrl, SESSION_TOKEN_IDENTIFIER); clientApiWrapper.createEmptyHTTPSession(targetUrl, SESSION_IDENTIFIER); - for (Cookie cookie : firefox.manage().getCookies()) { + for (Cookie cookie : loginResult.getSessionCookies()) { clientApiWrapper.setHTTPSessionTokenValue(targetUrl, SESSION_IDENTIFIER, cookie.getName(), cookie.getValue()); } clientApiWrapper.setActiveHTTPSession(targetUrl, SESSION_IDENTIFIER); - if (!addJwtAsReplacerRuleToZap(firefox, clientApiWrapper, LOCAL_STORAGE)) { - addJwtAsReplacerRuleToZap(firefox, clientApiWrapper, SESSION_STORAGE); + if (!addJwtAsReplacerRuleToZap(loginResult.getSessionStorage(), clientApiWrapper)) { + addJwtAsReplacerRuleToZap(loginResult.getLocalStorage(), clientApiWrapper); } boolean followRedirects = true; @@ -93,7 +89,7 @@ public void cleanUpOldSessionDataIfNecessary(String targetUrl, ClientApiWrapper } } - private boolean addJwtAsReplacerRuleToZap(FirefoxDriver firefox, ClientApiWrapper clientApiWrapper, String storageType) throws ClientApiException { + private boolean addJwtAsReplacerRuleToZap(Map storage, ClientApiWrapper clientApiWrapper) throws ClientApiException { boolean enabled = true; // "REQ_HEADER" means the header entry will be added to the requests if not // existing or replaced if already existing @@ -111,11 +107,10 @@ private boolean addJwtAsReplacerRuleToZap(FirefoxDriver firefox, ClientApiWrappe // any URL String url = null; - LOG.info("Searching: {} for JWT and add JWT as replacer rule.", storageType); - Map localStorage = retrieveStorage(firefox, storageType); + LOG.info("Searching browser storage for JWT and add JWT as replacer rule."); - for (String key : localStorage.keySet()) { - String value = localStorage.get(key); + for (String key : storage.keySet()) { + String value = storage.get(key); if (jwtSupport.isJWT(value)) { replacement = "Bearer %s".formatted(value); clientApiWrapper.addReplacerRule(JWT_REPLACER_DESCRIPTION, enabled, matchtype, matchregex, matchstring, replacement, initiators, url); @@ -125,18 +120,4 @@ private boolean addJwtAsReplacerRuleToZap(FirefoxDriver firefox, ClientApiWrappe return false; } - private Map retrieveStorage(JavascriptExecutor jsExecutor, String storageType) { - String script = """ - let items = {}; - for (let i = 0; i < %s.length; i++) { - let key = %s.key(i); - items[key] = %s.getItem(key); - } - return items; - """.formatted(storageType, storageType, storageType); - - @SuppressWarnings("unchecked") - Map storage = (Map) jsExecutor.executeScript(script); - return storage; - } } diff --git a/sechub-wrapper-owasp-zap/src/main/java/com/mercedesbenz/sechub/zapwrapper/scan/login/ZapScriptLoginWebDriverFactory.java b/sechub-wrapper-owasp-zap/src/main/java/com/mercedesbenz/sechub/zapwrapper/scan/login/ZapScriptLoginWebDriverFactory.java index 2c7c17ba1..f243def4d 100644 --- a/sechub-wrapper-owasp-zap/src/main/java/com/mercedesbenz/sechub/zapwrapper/scan/login/ZapScriptLoginWebDriverFactory.java +++ b/sechub-wrapper-owasp-zap/src/main/java/com/mercedesbenz/sechub/zapwrapper/scan/login/ZapScriptLoginWebDriverFactory.java @@ -16,6 +16,7 @@ public class ZapScriptLoginWebDriverFactory { private static final Dimension DEFAULT_WEBDRIVER_RESOLUTION = new Dimension(1920, 1080); public FirefoxDriver createFirefoxWebdriver(ProxyInformation proxyInformation, boolean headless) { + FirefoxOptions options = new FirefoxOptions(); if (headless) { LOG.info("Using firefox in headless mode."); @@ -32,6 +33,7 @@ public FirefoxDriver createFirefoxWebdriver(ProxyInformation proxyInformation, b proxy.setSslProxy(proxyString); options.setProxy(proxy); } + LOG.info("Creating selenium firefox driver."); FirefoxDriver firefox = new FirefoxDriver(options); // Set the window size, some application need a windows size to render correctly // even in headless mode diff --git a/sechub-wrapper-owasp-zap/src/main/java/com/mercedesbenz/sechub/zapwrapper/scan/login/ZapWrapperGroovyScriptExecutor.java b/sechub-wrapper-owasp-zap/src/main/java/com/mercedesbenz/sechub/zapwrapper/scan/login/ZapWrapperGroovyScriptExecutor.java index 3db347146..909cc6ca1 100644 --- a/sechub-wrapper-owasp-zap/src/main/java/com/mercedesbenz/sechub/zapwrapper/scan/login/ZapWrapperGroovyScriptExecutor.java +++ b/sechub-wrapper-owasp-zap/src/main/java/com/mercedesbenz/sechub/zapwrapper/scan/login/ZapWrapperGroovyScriptExecutor.java @@ -8,12 +8,14 @@ import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.time.Duration; +import java.util.Map; import javax.script.Bindings; import javax.script.ScriptEngine; import javax.script.ScriptException; import org.codehaus.groovy.jsr223.GroovyScriptEngineFactory; +import org.openqa.selenium.JavascriptExecutor; import org.openqa.selenium.firefox.FirefoxDriver; import org.openqa.selenium.support.ui.WebDriverWait; import org.slf4j.Logger; @@ -29,21 +31,57 @@ public class ZapWrapperGroovyScriptExecutor { private static final Logger LOG = LoggerFactory.getLogger(ZapWrapperGroovyScriptExecutor.class); - private static final long WEBDRIVER_TIMEOUT_PER_STEP_IN_SECONDS = 30; + private static final int WEBDRIVER_TIMEOUT_PER_STEP_IN_SECONDS = 30; - public void executeScript(File scriptFile, FirefoxDriver firefox, ZapScanContext scanContext) throws IOException, ScriptException { + private static final String LOCAL_STORAGE = "localStorage"; + private static final String SESSION_STORAGE = "sessionStorage"; + + private ZapScriptLoginWebDriverFactory webDriverFactory; + + private int webdriverTimeoutInSeconds; + + public ZapWrapperGroovyScriptExecutor() { + this(new ZapScriptLoginWebDriverFactory(), WEBDRIVER_TIMEOUT_PER_STEP_IN_SECONDS); + } + + ZapWrapperGroovyScriptExecutor(ZapScriptLoginWebDriverFactory webDriverFactory, int webdriverTimeoutInSeconds) { + this.webDriverFactory = webDriverFactory; + this.webdriverTimeoutInSeconds = webdriverTimeoutInSeconds; + } + + public ScriptLoginResult executeScript(File scriptFile, ZapScanContext scanContext) { + + FirefoxDriver firefox = webDriverFactory.createFirefoxWebdriver(scanContext.getProxyInformation(), true); + WebDriverWait wait = new WebDriverWait(firefox, Duration.ofSeconds(webdriverTimeoutInSeconds)); - String script = Files.readString(scriptFile.toPath()); ScriptEngine scriptEngine = new GroovyScriptEngineFactory().getScriptEngine(); LOG.info("Create bindings for groovy script."); - Bindings bindings = createBindings(scanContext, scriptEngine, firefox); - - LOG.info("Execute groovy login script."); - scriptEngine.eval(script, bindings); + Bindings bindings = createBindings(scanContext, scriptEngine, firefox, wait); + + ScriptLoginResult loginResult = new ScriptLoginResult(); + try { + String script = Files.readString(scriptFile.toPath()); + LOG.info("Execute groovy login script."); + scriptEngine.eval(script, bindings); + + // load target URL to ensure the correct page is loaded in the browser + firefox.get(scanContext.getTargetUrlAsString()); + + LOG.info("Execution successful, perparing login result with session data."); + loginResult.setSessionCookies(firefox.manage().getCookies()); + loginResult.setSessionStorage(retrieveStorage(firefox, SESSION_STORAGE)); + loginResult.setLocalStorage(retrieveStorage(firefox, LOCAL_STORAGE)); + } catch (IOException | ScriptException e) { + LOG.error("An error happened while executing the script file.", e); + loginResult.setLoginFailed(true); + } finally { + firefox.quit(); + } + return loginResult; } - private Bindings createBindings(ZapScanContext scanContext, ScriptEngine scriptEngine, FirefoxDriver firefox) { + private Bindings createBindings(ZapScanContext scanContext, ScriptEngine scriptEngine, FirefoxDriver firefox, WebDriverWait wait) { // TODO 2024-11-21 jan: use templates structure from sechub webscan config SecHubWebScanConfiguration secHubWebScanConfiguration = scanContext.getSecHubWebScanConfiguration(); WebLoginConfiguration webLoginConfiguration = secHubWebScanConfiguration.getLogin().get(); @@ -66,8 +104,6 @@ private Bindings createBindings(ZapScanContext scanContext, ScriptEngine scriptE String user = "DUMMY"; String password = "DUMMY"; - WebDriverWait wait = new WebDriverWait(firefox, Duration.ofSeconds(WEBDRIVER_TIMEOUT_PER_STEP_IN_SECONDS)); - Bindings bindings = scriptEngine.createBindings(); bindings.put(FIREFOX_WEBDRIVER_KEY, firefox); bindings.put(FIREFOX_WEBDRIVER_WAIT_KEY, wait); @@ -82,4 +118,19 @@ private Bindings createBindings(ZapScanContext scanContext, ScriptEngine scriptE return bindings; } + + private Map retrieveStorage(JavascriptExecutor jsExecutor, String storageType) { + String script = """ + let items = {}; + for (let i = 0; i < %s.length; i++) { + let key = %s.key(i); + items[key] = %s.getItem(key); + } + return items; + """.formatted(storageType, storageType, storageType); + + @SuppressWarnings("unchecked") + Map storage = (Map) jsExecutor.executeScript(script); + return storage; + } } diff --git a/sechub-wrapper-owasp-zap/src/main/java/com/mercedesbenz/sechub/zapwrapper/util/TOTPGenerator.java b/sechub-wrapper-owasp-zap/src/main/java/com/mercedesbenz/sechub/zapwrapper/util/TOTPGenerator.java index d5b6a8991..be427514a 100644 --- a/sechub-wrapper-owasp-zap/src/main/java/com/mercedesbenz/sechub/zapwrapper/util/TOTPGenerator.java +++ b/sechub-wrapper-owasp-zap/src/main/java/com/mercedesbenz/sechub/zapwrapper/util/TOTPGenerator.java @@ -41,7 +41,7 @@ public TOTPGenerator(String seed, int totpLength, TOTPHashAlgorithm hashAlgorith } /** - * This method generates a TOTP for the current times stamp in milliseconds. + * This method generates a TOTP for the current timestamp in milliseconds. * * @return totp currently valid */ @@ -50,7 +50,7 @@ public String now() { } /** - * This method generates a TOTP for a time stamp in milliseconds. + * This method generates a TOTP for a timestamp in milliseconds. * * @param seed * @param currentTimeMillis diff --git a/sechub-wrapper-owasp-zap/src/test/java/com/mercedesbenz/sechub/zapwrapper/scan/login/ZapScriptLoginSessionGrabberTest.java b/sechub-wrapper-owasp-zap/src/test/java/com/mercedesbenz/sechub/zapwrapper/scan/login/ZapScriptLoginSessionConfiguratorTest.java similarity index 58% rename from sechub-wrapper-owasp-zap/src/test/java/com/mercedesbenz/sechub/zapwrapper/scan/login/ZapScriptLoginSessionGrabberTest.java rename to sechub-wrapper-owasp-zap/src/test/java/com/mercedesbenz/sechub/zapwrapper/scan/login/ZapScriptLoginSessionConfiguratorTest.java index 772a8961e..39796f414 100644 --- a/sechub-wrapper-owasp-zap/src/test/java/com/mercedesbenz/sechub/zapwrapper/scan/login/ZapScriptLoginSessionGrabberTest.java +++ b/sechub-wrapper-owasp-zap/src/test/java/com/mercedesbenz/sechub/zapwrapper/scan/login/ZapScriptLoginSessionConfiguratorTest.java @@ -1,10 +1,10 @@ // SPDX-License-Identifier: MIT package com.mercedesbenz.sechub.zapwrapper.scan.login; +import static org.junit.jupiter.api.Assertions.*; import static org.mockito.ArgumentMatchers.*; import static org.mockito.Mockito.*; -import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.Set; @@ -14,20 +14,16 @@ import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; import org.openqa.selenium.Cookie; -import org.openqa.selenium.WebDriver.Options; -import org.openqa.selenium.firefox.FirefoxDriver; import org.zaproxy.clientapi.core.ApiResponse; import org.zaproxy.clientapi.core.ClientApiException; import com.mercedesbenz.sechub.zapwrapper.internal.scan.ClientApiWrapper; -class ZapScriptLoginSessionGrabberTest { +class ZapScriptLoginSessionConfiguratorTest { - private ZapScriptLoginSessionGrabber sessionGrabberToTest; + private ZapScriptLoginSessionConfigurator sessionConfiguratorToTest; private ClientApiWrapper clientApiWrapper; - private FirefoxDriver firefox; - private Options webDriverOptions; private JWTSupport jwtSupport; private ApiResponse zapApiResponse; @@ -38,8 +34,6 @@ class ZapScriptLoginSessionGrabberTest { @BeforeEach void beforeEach() { zapApiResponse = mock(); - firefox = mock(); - webDriverOptions = mock(); jwtSupport = mock(); clientApiWrapper = mock(ClientApiWrapper.class, new Answer() { @@ -50,7 +44,7 @@ public Object answer(InvocationOnMock invocation) throws Throwable { } }); - sessionGrabberToTest = new ZapScriptLoginSessionGrabber(jwtSupport); + sessionConfiguratorToTest = new ZapScriptLoginSessionConfigurator(jwtSupport); } @Test @@ -62,20 +56,16 @@ void one_cookie_and_one_jwt_results_in_each_mock_called_once() throws ClientApiE Map storage = new HashMap<>(); storage.put("jwt", "1234"); - when(firefox.manage()).thenReturn(webDriverOptions); + ScriptLoginResult loginResult = new ScriptLoginResult(); + loginResult.setSessionCookies(cookies); + loginResult.setSessionStorage(storage); - when(webDriverOptions.getCookies()).thenReturn(cookies); - when(firefox.executeScript(anyString())).thenReturn(storage); when(jwtSupport.isJWT(storage.get("jwt"))).thenReturn(true); /* execute */ - sessionGrabberToTest.extractSessionAndPassToZAP(firefox, TARGET_URL, clientApiWrapper); + sessionConfiguratorToTest.passSessionDataToZAP(loginResult, TARGET_URL, clientApiWrapper); /* test */ - verify(firefox, times(1)).manage(); - verify(webDriverOptions, times(1)).getCookies(); - verify(firefox, times(1)).executeScript(anyString()); - verify(clientApiWrapper, times(1)).removeHTTPSession(eq(TARGET_URL), any()); verify(clientApiWrapper, times(1)).removeHTTPSessionToken(eq(TARGET_URL), any()); verify(clientApiWrapper, times(1)).removeReplacerRule(any()); @@ -91,27 +81,22 @@ void one_cookie_and_one_jwt_results_in_each_mock_called_once() throws ClientApiE @Test void no_cookie_and_no_jwt_results_clienapiwrapper_not_adding_replacer_rule() throws ClientApiException { /* prepare */ - when(firefox.manage()).thenReturn(webDriverOptions); + Map storage = new HashMap<>(); + storage.put("jwt", "1234"); + ScriptLoginResult loginResult = new ScriptLoginResult(); + loginResult.setSessionStorage(storage); - when(webDriverOptions.getCookies()).thenReturn(Collections.emptySet()); - when(firefox.executeScript(anyString())).thenReturn(Collections.emptyMap()); when(jwtSupport.isJWT(anyString())).thenReturn(false); /* execute */ - sessionGrabberToTest.extractSessionAndPassToZAP(firefox, TARGET_URL, clientApiWrapper); + sessionConfiguratorToTest.passSessionDataToZAP(loginResult, TARGET_URL, clientApiWrapper); /* test */ - // both browser storages are checked now without JWT - verify(firefox, times(2)).executeScript(anyString()); // no JWT can be added verify(clientApiWrapper, never()).addReplacerRule(any(), anyBoolean(), any(), anyBoolean(), any(), any(), any(), any()); // no cookie can be added verify(clientApiWrapper, never()).setHTTPSessionTokenValue(eq(TARGET_URL), any(), any(), any()); - // the other calls must be the same as on every execution without error - verify(firefox, times(1)).manage(); - verify(webDriverOptions, times(1)).getCookies(); - verify(clientApiWrapper, times(1)).removeHTTPSession(eq(TARGET_URL), any()); verify(clientApiWrapper, times(1)).removeHTTPSessionToken(eq(TARGET_URL), any()); verify(clientApiWrapper, times(1)).removeReplacerRule(any()); @@ -122,4 +107,45 @@ void no_cookie_and_no_jwt_results_clienapiwrapper_not_adding_replacer_rule() thr verify(clientApiWrapper, times(1)).accessUrlViaZap(TARGET_URL, FOLLOW_REDIRECTS); } + @Test + void zap_available_clean_up_old_session_data_does_not_throw_an_exception() throws ClientApiException { + /* execute + test */ + assertDoesNotThrow(() -> sessionConfiguratorToTest.cleanUpOldSessionDataIfNecessary(TARGET_URL, clientApiWrapper)); + } + + @Test + void zap_not_available_removing_old_session_does_throw_an_exception() throws ClientApiException { + String errorMessage = "Connection refused"; + + ClientApiException apiException = new ClientApiException(errorMessage); + + when(clientApiWrapper.removeHTTPSession(any(), any())).thenThrow(apiException); + + /* execute + test */ + assertThrows(ClientApiException.class, () -> sessionConfiguratorToTest.cleanUpOldSessionDataIfNecessary(TARGET_URL, clientApiWrapper)); + } + + @Test + void zap_not_available_removing_old_session_token_does_throw_an_exception() throws ClientApiException { + String errorMessage = "Connection refused"; + + ClientApiException apiException = new ClientApiException(errorMessage); + + when(clientApiWrapper.removeHTTPSessionToken(any(), any())).thenThrow(apiException); + + /* execute + test */ + assertThrows(ClientApiException.class, () -> sessionConfiguratorToTest.cleanUpOldSessionDataIfNecessary(TARGET_URL, clientApiWrapper)); + } + + @Test + void zap_not_available_removing_old_jwt_replacer_rule_does_throw_an_exception() throws ClientApiException { + String errorMessage = "Connection refused"; + + ClientApiException apiException = new ClientApiException(errorMessage); + + when(clientApiWrapper.removeReplacerRule(any())).thenThrow(apiException); + + /* execute + test */ + assertThrows(ClientApiException.class, () -> sessionConfiguratorToTest.cleanUpOldSessionDataIfNecessary(TARGET_URL, clientApiWrapper)); + } } diff --git a/sechub-wrapper-owasp-zap/src/test/java/com/mercedesbenz/sechub/zapwrapper/scan/login/ZapScriptLoginTest.java b/sechub-wrapper-owasp-zap/src/test/java/com/mercedesbenz/sechub/zapwrapper/scan/login/ZapScriptLoginTest.java index bac27a65e..84934feab 100644 --- a/sechub-wrapper-owasp-zap/src/test/java/com/mercedesbenz/sechub/zapwrapper/scan/login/ZapScriptLoginTest.java +++ b/sechub-wrapper-owasp-zap/src/test/java/com/mercedesbenz/sechub/zapwrapper/scan/login/ZapScriptLoginTest.java @@ -5,17 +5,13 @@ import static org.mockito.Mockito.*; import java.io.File; -import java.io.IOException; import java.net.MalformedURLException; import java.net.URISyntaxException; import java.net.URL; import java.util.Optional; -import javax.script.ScriptException; - import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.openqa.selenium.firefox.FirefoxDriver; import org.zaproxy.clientapi.core.ClientApiException; import com.mercedesbenz.sechub.commons.model.SecHubWebScanConfiguration; @@ -30,99 +26,88 @@ class ZapScriptLoginTest { private ZapScriptLogin scriptLoginToTest; - private ZapScriptLoginWebDriverFactory webDriverFactory = mock(); - private ZapWrapperGroovyScriptExecutor groovyScriptExecutor = mock(); - private ZapScriptLoginSessionGrabber sessionGrabber = mock(); + private ZapWrapperGroovyScriptExecutor groovyScriptExecutor; + private ZapScriptLoginSessionConfigurator sessionConfigurator; - private ClientApiWrapper clientApiWrapper = mock(); - private FirefoxDriver firefox = mock(); + private ClientApiWrapper clientApiWrapper; @BeforeEach void beforeEach() { - webDriverFactory = mock(); groovyScriptExecutor = mock(); - sessionGrabber = mock(); + sessionConfigurator = mock(); clientApiWrapper = mock(); - firefox = mock(); - scriptLoginToTest = new ZapScriptLogin(webDriverFactory, groovyScriptExecutor, sessionGrabber); + scriptLoginToTest = new ZapScriptLogin(groovyScriptExecutor, sessionConfigurator); } @Test void script_login_execution_is_perfomed_as_expected() throws Exception { /* prepare */ - + ScriptLoginResult loginResult = new ScriptLoginResult(); ZapScanContext scanContext = createValidZapScanContext(); - when(webDriverFactory.createFirefoxWebdriver(scanContext.getProxyInformation(), true)).thenReturn(firefox); - doNothing().when(groovyScriptExecutor).executeScript(scanContext.getGroovyScriptLoginFile(), firefox, scanContext); - when(sessionGrabber.extractSessionAndPassToZAP(firefox, scanContext.getTargetUrlAsString(), clientApiWrapper)).thenReturn(AUTH_SESSION); + when(groovyScriptExecutor.executeScript(scanContext.getGroovyScriptLoginFile(), scanContext)).thenReturn(loginResult); + when(sessionConfigurator.passSessionDataToZAP(loginResult, scanContext.getTargetUrlAsString(), clientApiWrapper)).thenReturn(AUTH_SESSION); /* execute */ scriptLoginToTest.login(scanContext, clientApiWrapper); /* test */ - verify(webDriverFactory, times(1)).createFirefoxWebdriver(scanContext.getProxyInformation(), true); - verify(groovyScriptExecutor, times(1)).executeScript(scanContext.getGroovyScriptLoginFile(), firefox, scanContext); - verify(sessionGrabber, times(1)).extractSessionAndPassToZAP(firefox, scanContext.getTargetUrlAsString(), clientApiWrapper); - verify(firefox, times(1)).quit(); + verify(groovyScriptExecutor, times(1)).executeScript(scanContext.getGroovyScriptLoginFile(), scanContext); + verify(sessionConfigurator, times(1)).passSessionDataToZAP(loginResult, scanContext.getTargetUrlAsString(), clientApiWrapper); } @Test - void script_can_not_be_read_results_in_firefox_closed_and_session_grabber_never_called() throws Exception { + void script_can_not_be_read_results_in_firefox_closed_and_session_configurator_never_called() throws Exception { /* prepare */ + ScriptLoginResult loginResult = new ScriptLoginResult(); + loginResult.setLoginFailed(true); ZapScanContext scanContext = createValidZapScanContext(); - when(webDriverFactory.createFirefoxWebdriver(scanContext.getProxyInformation(), true)).thenReturn(firefox); - doThrow(IOException.class).when(groovyScriptExecutor).executeScript(scanContext.getGroovyScriptLoginFile(), firefox, scanContext); - when(sessionGrabber.extractSessionAndPassToZAP(firefox, scanContext.getTargetUrlAsString(), clientApiWrapper)).thenReturn(AUTH_SESSION); + when(groovyScriptExecutor.executeScript(scanContext.getGroovyScriptLoginFile(), scanContext)).thenReturn(loginResult); + when(sessionConfigurator.passSessionDataToZAP(loginResult, scanContext.getTargetUrlAsString(), clientApiWrapper)).thenReturn(AUTH_SESSION); /* execute */ assertThrows(ZapWrapperRuntimeException.class, () -> scriptLoginToTest.login(scanContext, clientApiWrapper)); /* test */ - verify(webDriverFactory, times(1)).createFirefoxWebdriver(scanContext.getProxyInformation(), true); - verify(groovyScriptExecutor, times(1)).executeScript(scanContext.getGroovyScriptLoginFile(), firefox, scanContext); - verify(sessionGrabber, never()).extractSessionAndPassToZAP(firefox, scanContext.getTargetUrlAsString(), clientApiWrapper); - verify(firefox, times(1)).quit(); + verify(groovyScriptExecutor, times(1)).executeScript(scanContext.getGroovyScriptLoginFile(), scanContext); + verify(sessionConfigurator, never()).passSessionDataToZAP(loginResult, scanContext.getTargetUrlAsString(), clientApiWrapper); } @Test - void script_login_execution_fails_results_in_firefox_closed_and_session_grabber_never_called() throws Exception { + void script_login_execution_fails_results_in_firefox_closed_and_session_configurator_never_called() throws Exception { /* prepare */ + ScriptLoginResult loginResult = new ScriptLoginResult(); + loginResult.setLoginFailed(true); ZapScanContext scanContext = createValidZapScanContext(); - when(webDriverFactory.createFirefoxWebdriver(scanContext.getProxyInformation(), true)).thenReturn(firefox); - doThrow(ScriptException.class).when(groovyScriptExecutor).executeScript(scanContext.getGroovyScriptLoginFile(), firefox, scanContext); - when(sessionGrabber.extractSessionAndPassToZAP(firefox, scanContext.getTargetUrlAsString(), clientApiWrapper)).thenReturn(AUTH_SESSION); + when(groovyScriptExecutor.executeScript(scanContext.getGroovyScriptLoginFile(), scanContext)).thenReturn(loginResult); + when(sessionConfigurator.passSessionDataToZAP(loginResult, scanContext.getTargetUrlAsString(), clientApiWrapper)).thenReturn(AUTH_SESSION); /* execute */ assertThrows(ZapWrapperRuntimeException.class, () -> scriptLoginToTest.login(scanContext, clientApiWrapper)); /* test */ - verify(webDriverFactory, times(1)).createFirefoxWebdriver(scanContext.getProxyInformation(), true); - verify(groovyScriptExecutor, times(1)).executeScript(scanContext.getGroovyScriptLoginFile(), firefox, scanContext); - verify(sessionGrabber, never()).extractSessionAndPassToZAP(firefox, scanContext.getTargetUrlAsString(), clientApiWrapper); - verify(firefox, times(1)).quit(); + verify(groovyScriptExecutor, times(1)).executeScript(scanContext.getGroovyScriptLoginFile(), scanContext); + verify(sessionConfigurator, never()).passSessionDataToZAP(loginResult, scanContext.getTargetUrlAsString(), clientApiWrapper); } @Test - void session_grabbing_fails_results_in_firefox_closed() throws Exception { + void session_configurator_fails_results_in_excpetion_thrown() throws Exception { /* prepare */ + ScriptLoginResult loginResult = new ScriptLoginResult(); ZapScanContext scanContext = createValidZapScanContext(); - when(webDriverFactory.createFirefoxWebdriver(scanContext.getProxyInformation(), true)).thenReturn(firefox); - doNothing().when(groovyScriptExecutor).executeScript(scanContext.getGroovyScriptLoginFile(), firefox, scanContext); - doThrow(ClientApiException.class).when(sessionGrabber).extractSessionAndPassToZAP(firefox, scanContext.getTargetUrlAsString(), clientApiWrapper); + when(groovyScriptExecutor.executeScript(scanContext.getGroovyScriptLoginFile(), scanContext)).thenReturn(loginResult); + doThrow(ClientApiException.class).when(sessionConfigurator).passSessionDataToZAP(loginResult, scanContext.getTargetUrlAsString(), clientApiWrapper); /* execute */ assertThrows(ZapWrapperRuntimeException.class, () -> scriptLoginToTest.login(scanContext, clientApiWrapper)); /* test */ - verify(webDriverFactory, times(1)).createFirefoxWebdriver(scanContext.getProxyInformation(), true); - verify(groovyScriptExecutor, times(1)).executeScript(scanContext.getGroovyScriptLoginFile(), firefox, scanContext); - verify(sessionGrabber, times(1)).extractSessionAndPassToZAP(firefox, scanContext.getTargetUrlAsString(), clientApiWrapper); - verify(firefox, times(1)).quit(); + verify(groovyScriptExecutor, times(1)).executeScript(scanContext.getGroovyScriptLoginFile(), scanContext); + verify(sessionConfigurator, times(1)).passSessionDataToZAP(loginResult, scanContext.getTargetUrlAsString(), clientApiWrapper); } private ZapScanContext createValidZapScanContext() throws MalformedURLException, URISyntaxException { diff --git a/sechub-wrapper-owasp-zap/src/test/java/com/mercedesbenz/sechub/zapwrapper/scan/login/ZapWrapperGroovyScriptExecutorTest.java b/sechub-wrapper-owasp-zap/src/test/java/com/mercedesbenz/sechub/zapwrapper/scan/login/ZapWrapperGroovyScriptExecutorTest.java index 07f36e36a..74a119be5 100644 --- a/sechub-wrapper-owasp-zap/src/test/java/com/mercedesbenz/sechub/zapwrapper/scan/login/ZapWrapperGroovyScriptExecutorTest.java +++ b/sechub-wrapper-owasp-zap/src/test/java/com/mercedesbenz/sechub/zapwrapper/scan/login/ZapWrapperGroovyScriptExecutorTest.java @@ -2,17 +2,19 @@ package com.mercedesbenz.sechub.zapwrapper.scan.login; import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.*; import java.io.File; -import java.io.IOException; import java.net.URI; import java.net.URL; +import java.util.Collections; import java.util.Optional; -import javax.script.ScriptException; - import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.openqa.selenium.WebDriver.Options; +import org.openqa.selenium.firefox.FirefoxDriver; import com.mercedesbenz.sechub.commons.model.SecHubWebScanConfiguration; import com.mercedesbenz.sechub.commons.model.login.WebLoginConfiguration; @@ -22,18 +24,43 @@ class ZapWrapperGroovyScriptExecutorTest { private ZapWrapperGroovyScriptExecutor scriptExecutorToTest; + private ZapScriptLoginWebDriverFactory webDriverFactory; + private FirefoxDriver firefox; + private Options options; + @BeforeEach void beforeEach() { - scriptExecutorToTest = new ZapWrapperGroovyScriptExecutor(); + webDriverFactory = mock(); + firefox = mock(); + options = mock(); + + scriptExecutorToTest = new ZapWrapperGroovyScriptExecutor(webDriverFactory, 0); + + when(webDriverFactory.createFirefoxWebdriver(any(), anyBoolean())).thenReturn(firefox); + when(firefox.manage()).thenReturn(options); + when(options.getCookies()).thenReturn(Collections.emptySet()); } @Test - void throws_io_exception_when_script_cannot_be_read() { + void throws_io_exception_when_script_cannot_be_read() throws Exception { /* prepare */ File scriptFile = new File("not-existing.groovy"); - /* execute + test */ - assertThrows(IOException.class, () -> scriptExecutorToTest.executeScript(scriptFile, null, null)); + SecHubWebScanConfiguration webScanConfig = new SecHubWebScanConfiguration(); + webScanConfig.setUrl(URI.create("http://example.com")); + + WebLoginConfiguration login = new WebLoginConfiguration(); + login.setUrl(new URL("http://example.com/login")); + webScanConfig.setLogin(Optional.of(login)); + + ZapScanContext zapScanContext = ZapScanContext.builder().setSecHubWebScanConfiguration(webScanConfig).setTargetUrl(webScanConfig.getUrl().toURL()) + .build(); + + /* execute */ + ScriptLoginResult loginResult = scriptExecutorToTest.executeScript(scriptFile, zapScanContext); + + /* test */ + assertTrue(loginResult.isLoginFailed()); } @Test @@ -51,8 +78,11 @@ void throws_script_exception_when_script_contains_errors() throws Exception { ZapScanContext zapScanContext = ZapScanContext.builder().setSecHubWebScanConfiguration(webScanConfig).setTargetUrl(webScanConfig.getUrl().toURL()) .build(); - /* execute + test */ - assertThrows(ScriptException.class, () -> scriptExecutorToTest.executeScript(scriptFile, null, zapScanContext)); + /* execute */ + ScriptLoginResult loginResult = scriptExecutorToTest.executeScript(scriptFile, zapScanContext); + + /* test */ + assertTrue(loginResult.isLoginFailed()); } @Test @@ -70,8 +100,11 @@ void valid_script_is_executed_as_expected() throws Exception { ZapScanContext zapScanContext = ZapScanContext.builder().setSecHubWebScanConfiguration(webScanConfig).setTargetUrl(webScanConfig.getUrl().toURL()) .build(); - /* execute + test */ - assertDoesNotThrow(() -> scriptExecutorToTest.executeScript(scriptFile, null, zapScanContext)); + /* execute */ + ScriptLoginResult loginResult = scriptExecutorToTest.executeScript(scriptFile, zapScanContext); + + /* test */ + assertFalse(loginResult.isLoginFailed()); } }