From 1f8c0068242d85eef9528b09a4d47439debf135e Mon Sep 17 00:00:00 2001 From: Your Name Date: Wed, 6 Nov 2024 18:47:30 +0000 Subject: [PATCH 1/6] ZAP: disable verify, deprecated --- scanners/zap/zap_none.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scanners/zap/zap_none.py b/scanners/zap/zap_none.py index a5fca9e..e18c57d 100644 --- a/scanners/zap/zap_none.py +++ b/scanners/zap/zap_none.py @@ -106,7 +106,7 @@ def run(self): if not self.state == State.READY: raise RuntimeError("[ZAP SCANNER]: ERROR, not ready to run") - self._check_plugin_status() + # self._check_plugin_status() # temporary workaround: cleanup addon state # see https://github.com/zaproxy/zaproxy/issues/7590#issuecomment-1308909500 From fe1f2fb94723e6b5827333d32c86cdf0292a0cee Mon Sep 17 00:00:00 2001 From: Your Name Date: Wed, 6 Nov 2024 19:30:22 +0000 Subject: [PATCH 2/6] ZAP: disable auto update by default + typo fix --- README.md | 4 +- config/config-template-zap-long.yaml | 2 +- .../manifests/rapidast-vapi-configmap.yaml | 2 +- scanners/zap/zap.py | 2 +- scanners/zap/zap_none.py | 51 +------------------ 5 files changed, 6 insertions(+), 55 deletions(-) diff --git a/README.md b/README.md index eca6071..f212106 100644 --- a/README.md +++ b/README.md @@ -449,9 +449,9 @@ Example: `podman pod create --userns=keep-id:uid=1000,gid=1000 myApp_Pod` This is useful for debugging. Set `scanners.zap.miscOptions.enableUI: True` (default: False). Then, the ZAP desktop will run with GUI on your host and show the progress of scanning. -+ Disable add-on updates: ++ Enable add-on updates: -Set `scanners.zap.miscOptions.updateAddons: False` (default: True). Then, ZAP will update its addons first and run the scan. +Set `scanners.zap.miscOptions.updateAddons: True` (default: False). ZAP will first update its addons and then run the scan. + Install additional addons: diff --git a/config/config-template-zap-long.yaml b/config/config-template-zap-long.yaml index 79051f5..b477395 100644 --- a/config/config-template-zap-long.yaml +++ b/config/config-template-zap-long.yaml @@ -188,7 +188,7 @@ scanners: # EnableUI (default: false), requires a compatible runtime (e.g.: `type: none`) enableUI: False - # Defaults to True, set False to prevent auto update of ZAP plugins + # Defaults to False, set True to force auto update of ZAP plugins updateAddons: True # List (comma-separated string or list) of additional addons to install diff --git a/e2e-tests/manifests/rapidast-vapi-configmap.yaml b/e2e-tests/manifests/rapidast-vapi-configmap.yaml index d5e0b9a..be5fe8b 100644 --- a/e2e-tests/manifests/rapidast-vapi-configmap.yaml +++ b/e2e-tests/manifests/rapidast-vapi-configmap.yaml @@ -30,7 +30,7 @@ data: miscOptions: # enableUI (default: false), requires a compatible runtime (e.g.: flatpak or no containment) #enableUI: True - # Defaults to True, set False to prevent auto update of ZAP plugins + # Defaults to False, set True to force auto update of ZAP plugins updateAddons: False # additionalAddons: ascanrulesBeta # If set to True and authentication is oauth2_rtoken and api.apiUrl is set, download the API outside of ZAP diff --git a/scanners/zap/zap.py b/scanners/zap/zap.py index 2e93b6e..1f5e60a 100644 --- a/scanners/zap/zap.py +++ b/scanners/zap/zap.py @@ -151,7 +151,7 @@ def get_update_command(self): *self._get_standard_options(), "-cmd", ] - if self.my_conf("miscOptions.updateAddons", default=True): + if self.my_conf("miscOptions.updateAddons"): command.append("-addonupdate") addons = self.my_conf("miscOptions.additionalAddons", default=[]) diff --git a/scanners/zap/zap_none.py b/scanners/zap/zap_none.py index e18c57d..3e98581 100644 --- a/scanners/zap/zap_none.py +++ b/scanners/zap/zap_none.py @@ -106,8 +106,6 @@ def run(self): if not self.state == State.READY: raise RuntimeError("[ZAP SCANNER]: ERROR, not ready to run") - # self._check_plugin_status() - # temporary workaround: cleanup addon state # see https://github.com/zaproxy/zaproxy/issues/7590#issuecomment-1308909500 statefile = f"{self.host_home_dir}/add-ons-state.xml" @@ -260,7 +258,7 @@ def _handle_plugins(self): command = self.get_update_command() if not command: - logging.debug("Skpping addon handling: no install, no update") + logging.debug("Skipping addon handling: no install, no update") return # manually specify directory command.extend(["-dir", self.container_home_dir]) @@ -273,53 +271,6 @@ def _handle_plugins(self): f"ZAP did not handle the addon requirements correctly, and exited with code {result.returncode}" ) - def _check_plugin_status(self): - """MacOS workaround for "The mandatory add-on was not found" error - See https://github.com/zaproxy/zaproxy/issues/7703 - """ - logging.info("Zap: verifying the viability of ZAP") - - cmd = self.my_conf("container.parameters.executable") - if shutil.which(cmd) is None: - logging.error(f"{cmd} not found in PATH, is ZAP installed?") - sys.exit(1) - - command = [cmd] - command.extend(self._get_standard_options()) - command.extend(["-dir", self.container_home_dir]) - command.append("-cmd") - - logging.debug(f"ZAP create home command: {command}") - result = subprocess.run(command, check=False, capture_output=True) - if result.returncode == 0: - logging.debug("ZAP appears to be in a correct state") - elif result.stderr.find(bytes("The mandatory add-on was not found:", "ascii")) > 0: - logging.info("Missing mandatory plugins. Fixing") - url_root = "https://github.com/zaproxy/zap-extensions/releases/download" - anonymous_download( - url=f"{url_root}/callhome-v0.6.0/callhome-release-0.6.0.zap", - dest=f"{self.host_home_dir}/plugin/callhome-release-0.6.0.zap", - proxy=self.my_conf("proxy", default=None), - ) - anonymous_download( - url=f"{url_root}/network-v0.9.0/network-beta-0.9.0.zap", - dest=f"{self.host_home_dir}/plugin/network-beta-0.9.0.zap", - proxy=self.my_conf("proxy", default=None), - ) - logging.info("Workaround: installing all addons") - - command = [self.my_conf("container.parameters.executable")] - command.extend(self._get_standard_options()) - command.extend(["-dir", self.container_home_dir]) - command.append("-cmd") - command.append("-addoninstallall") - - logging.debug(f"ZAP: installing all addons: {command}") - result = subprocess.run(command, check=False) - - else: - logging.warning(f"ZAP appears to be in a incorrect state. Error: {result.stderr}") - def _create_home_if_needed(self): """Some tools (most notably: ZAP's Ajax Spider with Firefox) require a writable home directory. When RapiDAST is run in Openshift, the user's home is /, which is not writable. From 10ee2878dabb2309f87c43a001f73ee29e74a173 Mon Sep 17 00:00:00 2001 From: Your Name Date: Wed, 6 Nov 2024 21:13:23 +0000 Subject: [PATCH 3/6] ZAP: disable addon check during the scan --- scanners/zap/zap.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/scanners/zap/zap.py b/scanners/zap/zap.py index 1f5e60a..d1ab1a1 100644 --- a/scanners/zap/zap.py +++ b/scanners/zap/zap.py @@ -191,6 +191,9 @@ def _setup_zap_cli(self): """ self.zap_cli.extend(self._get_standard_options()) + # Addon update has already been done, if enabled. Prevent a new check for update + self.zap_cli.append("-silent") + # Create a session, to store them as evidence self.zap_cli.extend(["-newsession", f"{self.container_work_dir}/session_data/session"]) From 075a2b44ddd22f1196ab87de260960c857838c8d Mon Sep 17 00:00:00 2001 From: Your Name Date: Thu, 7 Nov 2024 08:52:40 +0000 Subject: [PATCH 4/6] ZAP: fix linting --- scanners/zap/zap_none.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/scanners/zap/zap_none.py b/scanners/zap/zap_none.py index 3e98581..2e6f7c5 100644 --- a/scanners/zap/zap_none.py +++ b/scanners/zap/zap_none.py @@ -2,15 +2,12 @@ import os import platform import pprint -import shutil import subprocess -import sys from shutil import disk_usage from .zap import MODULE_DIR from .zap import Zap from scanners import State -from scanners.downloaders import anonymous_download from scanners.path_translators import make_mapping_for_scanner CLASSNAME = "ZapNone" From 51e272b3ca9d559655a74172d7b43b3f202a190b Mon Sep 17 00:00:00 2001 From: Your Name Date: Tue, 12 Nov 2024 10:06:43 +0000 Subject: [PATCH 5/6] zap/podman: handle_plugins() no longer returns the entire command But instead returns either the command to handle plugins as a list. The caller then needs to, if need be, inject that in a `sh` wrapper --- scanners/zap/zap_podman.py | 40 +++++++++++++++++-------- tests/scanners/zap/test_setup_podman.py | 9 ++---- 2 files changed, 29 insertions(+), 20 deletions(-) diff --git a/scanners/zap/zap_podman.py b/scanners/zap/zap_podman.py index 131a97f..0c769af 100644 --- a/scanners/zap/zap_podman.py +++ b/scanners/zap/zap_podman.py @@ -103,7 +103,26 @@ def run(self): if not self.state == State.READY: raise RuntimeError("[ZAP SCANNER]: ERROR, not ready to run") - cli = self._handle_plugins() + # zap_podman's _handle_plugins simply returns the CLI to run as a list + # [empty if no update], to be assembled with the scan command in a + # single `sh` wrapper + plugins_cmd = self._handle_plugins() + if plugins_cmd: + # We need to merge the update and the scan in a single `sh` wrapped + # command, split by `;` + # 1) protect & turn the update command in a string + full_cmd_as_string = self._zap_cli_list_to_str_for_sh(plugins_cmd) + # 2) Add a separator + full_cmd_as_string += "; " + # 3) protect & turn the scan command in a string + full_cmd_as_string += self._zap_cli_list_to_str_for_sh(self.zap_cli) + + cli = ["sh", "-c", full_cmd_as_string] + + else: + # No update: we can run a single scan command + cli = self.zap_cli + cli = self.podman.get_complete_cli(cli) # DO STUFF @@ -199,19 +218,14 @@ def _handle_plugins(self): By running a separate instance of ZAP prior to the real scan. This is required because some addons require a restart of ZAP. - In "podman" mode, we have to run both the plugin command and - the scan command in the same run. So we inject that in shell. + In "podman" mode, we have to run both the plugin command and the scan + command in the same run, so the _handle_plugins function itself can't + run the update. + So we simply return the `get_update_command()` [command to execute to + make the update, or empty list], and the caller will figure how to make + use of that] """ - - shell = ["sh", "-c"] - update_cmd = self._zap_cli_list_to_str_for_sh(self.get_update_command()) - if update_cmd: - update_cmd += "; " - update_cmd += self._zap_cli_list_to_str_for_sh(self.zap_cli) - shell.append(update_cmd) - - logging.debug(f"Update command: {shell}") - return shell + return self.get_update_command() def _setup_podman_cli(self): """Prepare the podman command. diff --git a/tests/scanners/zap/test_setup_podman.py b/tests/scanners/zap/test_setup_podman.py index 550458a..f073363 100644 --- a/tests/scanners/zap/test_setup_podman.py +++ b/tests/scanners/zap/test_setup_podman.py @@ -185,10 +185,5 @@ def test_podman_handling_plugins(test_config): assert "pluginB" in test_zap.get_update_command() shell = test_zap._handle_plugins() - assert len(shell) == 3 - assert shell[0] == "sh" - assert shell[1] == "-c" - assert re.search( - "^zap.sh .* -cmd -addonupdate -addoninstall pluginA -addoninstall pluginB; .*", - shell[2], - ) + assert_shell = ["zap.sh", "-config", "network.connection.httpProxy.enabled=false", "-config", "network.localServers.mainProxy.port=47691", "-cmd", "-addonupdate", "-addoninstall", "pluginA", "-addoninstall", "pluginB"] + assert shell == assert_shell From 0fd7f6231cb29ae277d893d99eb7560da5e77a5f Mon Sep 17 00:00:00 2001 From: Your Name Date: Tue, 12 Nov 2024 16:39:40 +0000 Subject: [PATCH 6/6] fix black error --- tests/scanners/zap/test_setup_podman.py | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/tests/scanners/zap/test_setup_podman.py b/tests/scanners/zap/test_setup_podman.py index f073363..7aefc95 100644 --- a/tests/scanners/zap/test_setup_podman.py +++ b/tests/scanners/zap/test_setup_podman.py @@ -185,5 +185,17 @@ def test_podman_handling_plugins(test_config): assert "pluginB" in test_zap.get_update_command() shell = test_zap._handle_plugins() - assert_shell = ["zap.sh", "-config", "network.connection.httpProxy.enabled=false", "-config", "network.localServers.mainProxy.port=47691", "-cmd", "-addonupdate", "-addoninstall", "pluginA", "-addoninstall", "pluginB"] + assert_shell = [ + "zap.sh", + "-config", + "network.connection.httpProxy.enabled=false", + "-config", + "network.localServers.mainProxy.port=47691", + "-cmd", + "-addonupdate", + "-addoninstall", + "pluginA", + "-addoninstall", + "pluginB", + ] assert shell == assert_shell