From 7d20f9ceef2744b0ef269b4abf01c1c1b764db60 Mon Sep 17 00:00:00 2001 From: Cedric Buissart Date: Wed, 20 Sep 2023 01:53:44 +0200 Subject: [PATCH] [containerize] Create optional default config (#128) * [containerize] Create optional default config If there is a file called 'rapidast-defaults.yaml' located in the same directory as `rapidast.py`, then this file is loaded as a YAML file. It content will be merged to the current rapidast config BUT without overwriting any existing entries. In practice, this allows anyone to create default values. Containerfile uses that to set `general.container.type` to 'none' This way, if a config file does not set a container type for a given scanner, `none` will be chosen. * [templates] container type set to default Commenting out the container.type, so that templates rely on default. Except in `generic` scanner, where `podman` is the only supported type, so it makes sense to enforce it at the moment. Relying on default will allow RapiDAST to use podman on a host, and none in the RapiDAST image. Additional changes: I tried to normalize documentation within the YAML templates. e.g.: ``` ``` Is that a entry called `Optional` commented out or is it documentation? Now: - if there is no space between the `#` and the first letter, it's an entry commented out - if there is a space between `#` and the first letter, and that letter is upper case, it's probably documentation * fix: MacOS template selects type none --- config/config-template-generic-scan.yaml | 11 ++++++----- config/config-template-multi-scan.yaml | 8 ++++---- config/config-template-zap-long.yaml | 13 +++++++------ config/config-template-zap-mac.yaml | 3 ++- config/config-template-zap-simple.yaml | 7 ++++--- configmodel/__init__.py | 2 ++ containerize/Containerfile | 3 +++ containerize/container_default_config.yaml | 3 +++ rapidast.py | 14 +++++++++++++- 9 files changed, 44 insertions(+), 20 deletions(-) create mode 100644 containerize/container_default_config.yaml diff --git a/config/config-template-generic-scan.yaml b/config/config-template-generic-scan.yaml index 8bdb4513..c2ba201f 100644 --- a/config/config-template-generic-scan.yaml +++ b/config/config-template-generic-scan.yaml @@ -23,7 +23,8 @@ general: # podman: RapiDAST runs each scanner using podman # flatpak: RapiDAST runs each scanner using flatpak # none: RapiDAST runs each scanner in the same host or container (where RapiDAST itself is running in a container) - type: "podman" + # When undefined, relies on rapidast-defaults.yaml, or `podman` if nothing is set + #type: "podman" # `scanners' is a section that configures scanning options scanners: @@ -42,15 +43,15 @@ scanners: image: "" # Optional: command to run. By default, the image's ENTRYPOINT will be run - # command: "" + #command: "" # Optional: inject into an existing Pod - # podName: "mypod" + #podName: "mypod" # Optional: list of expected return codes, anything else will be considered as an error. by default: [0] - # validReturns: [ 0, 1 ] + #validReturns: [ 0, 1 ] # Optional: list of volume to mount, e.g.: to retrieve results on the host - # volumes: + #volumes: # - "::Z" # for Linux # - ":" # for Mac diff --git a/config/config-template-multi-scan.yaml b/config/config-template-multi-scan.yaml index 04addc8f..4a27c9df 100644 --- a/config/config-template-multi-scan.yaml +++ b/config/config-template-multi-scan.yaml @@ -33,7 +33,8 @@ general: # podman: RapiDAST runs each scanner using podman # flatpak: RapiDAST runs each scanner using flatpak # none: RapiDAST runs each scanner in the same host or container (where RapiDAST itself is running in a container) - type: "podman" + # When undefined, relies on rapidast-defaults.yaml, or `podman` if nothing is set + #type: "podman" # `scanners' is a section that configures scanning options # See the config-template-long.yaml for more options @@ -49,11 +50,10 @@ scanners: disabledRules: "2,10015,10027,10096,10024" # Enable activeScan by uncommenting, once scans with the passiveScan only has run successfully - # # If no policy is chosen, a default ("API-scan-minimal") will be selected - # # The list of policies can be found in scanners/zap/policies/ + # If no policy is chosen, a default ("API-scan-minimal") will be selected + # The list of policies can be found in scanners/zap/policies/ #activeScan: # policy: "API-scan-minimal" - # generic_1: # This is a generic scanner configuration, defined by the user diff --git a/config/config-template-zap-long.yaml b/config/config-template-zap-long.yaml index 427d0d0c..e03cb384 100644 --- a/config/config-template-zap-long.yaml +++ b/config/config-template-zap-long.yaml @@ -72,7 +72,8 @@ general: # flatpak: RapiDAST runs each scanner using flatpak # none: RapiDAST runs each scanner in the same host or container (where RapiDAST itself is running in a container) # container. - type: "podman" + # When undefined, relies on rapidast-defaults.yaml, or `podman` if nothing is set + #type: "podman" # `scanners' is a section that configures scanning options @@ -111,7 +112,7 @@ scanners: browserId: firefox-headless passiveScan: - # optional comma-separated list of passive rules to disable + # Optional comma-separated list of passive rules to disable # Use https://www.zaproxy.org/docs/alerts/ to match rule with its ID disabledRules: "2,10015,10027,10096,10024" @@ -126,12 +127,12 @@ scanners: #podName: "mypod" # optional: inject ZAP in an existing Pod executable: "zap.sh" # for Linux - # executable: "/Applications/OWASP ZAP.app/Contents/Java/zap.sh" # for MacOS, when general.container.type is 'none' only + #executable: "/Applications/OWASP ZAP.app/Contents/Java/zap.sh" # for MacOS, when general.container.type is 'none' only report: format: ["json"] - # format: ["json","html","sarif","xml"] # default: "json" only + #format: ["json","html","sarif","xml"] # default: "json" only urls: # Optional, `includes` and `excludes` take a list of regexps. @@ -146,14 +147,14 @@ scanners: # - "^https?://example.com:3000/do-not-descend-here/.*$" miscOptions: - # enableUI (default: false), requires a compatible runtime (e.g.: flatpak or no containment) + # EnableUI (default: false), requires a compatible runtime (e.g.: flatpak or no containment) enableUI: False # Defaults to True, set False to prevent auto update of ZAP plugins updateAddons: True # If set to True and authentication is oauth2_rtoken: manually download schemas (e.g.: openAPI, GraphQL) oauth2ManualDownload: False - # overwrite the default port in case it is required. The default port was selected to avoid any collision with other services + # Overwrite the default port in case it is required. The default port was selected to avoid any collision with other services zapPort: 8080 # Maximum heap size of the JVM. Default: ΒΌ of the RAM. acceptable values: [0-9]+[kKmMgG]? diff --git a/config/config-template-zap-mac.yaml b/config/config-template-zap-mac.yaml index 2ddee681..b4c35861 100644 --- a/config/config-template-zap-mac.yaml +++ b/config/config-template-zap-mac.yaml @@ -32,11 +32,12 @@ general: # podman: RapiDAST runs each scanner using podman # flatpak: RapiDAST runs each scanner using flatpak # none: RapiDAST runs each scanner in the same host or container (where RapiDAST itself is running in a container) + # When undefined, relies on rapidast-defaults.yaml, or `podman` if nothing is set type: "none" scanners: zap: - # define a scan through the ZAP scanner + # Define a scan through the ZAP scanner apiScan: apis: apiFile: "path/to/local/openapi-schema" diff --git a/config/config-template-zap-simple.yaml b/config/config-template-zap-simple.yaml index 76433416..a475741b 100644 --- a/config/config-template-zap-simple.yaml +++ b/config/config-template-zap-simple.yaml @@ -33,7 +33,8 @@ general: # podman: RapiDAST runs each scanner using podman # flatpak: RapiDAST runs each scanner using flatpak # none: RapiDAST runs each scanner in the same host or container (where RapiDAST itself is running in a container) - type: "podman" + # When undefined, relies on rapidast-defaults.yaml, or `podman` if nothing is set + #type: "podman" # `scanners' is a section that configures scanning options # See the config-template-long.yaml for more options @@ -49,7 +50,7 @@ scanners: disabledRules: "2,10015,10027,10096,10024" # Enable activeScan by uncommenting, once scans with the passiveScan only has run successfully - # # If no policy is chosen, a default ("API-scan-minimal") will be selected - # # The list of policies can be found in scanners/zap/policies/ + # If no policy is chosen, a default ("API-scan-minimal") will be selected + # The list of policies can be found in scanners/zap/policies/ #activeScan: # policy: "API-scan-minimal" diff --git a/configmodel/__init__.py b/configmodel/__init__.py index c15ff8fe..96355c28 100644 --- a/configmodel/__init__.py +++ b/configmodel/__init__.py @@ -191,6 +191,8 @@ def path_to_list(path): - path_to_list(('a','b','c')) => ['a','b','c'] """ + if not path: + return [] if isinstance(path, str): path = path.split(".") return list(path) diff --git a/containerize/Containerfile b/containerize/Containerfile index 26997950..d8abc934 100644 --- a/containerize/Containerfile +++ b/containerize/Containerfile @@ -39,6 +39,9 @@ COPY ./utils/ /opt/rapidast/utils/ COPY ./config/ /opt/rapidast/config/ COPY ./requirements.txt /opt/rapidast/ +### Overload default config (set 'none' as default container type) +COPY ./containerize/container_default_config.yaml /opt/rapidast/rapidast-defaults.yaml + ### Add /opt/{zap,rapidast}/ to the PATH (for any user and future user) COPY ./containerize/path_rapidast.sh /etc/profile.d/rapidast.sh diff --git a/containerize/container_default_config.yaml b/containerize/container_default_config.yaml new file mode 100644 index 00000000..ea31cc17 --- /dev/null +++ b/containerize/container_default_config.yaml @@ -0,0 +1,3 @@ +general: + container: + type: "none" diff --git a/rapidast.py b/rapidast.py index 18db11e7..0c4726ce 100755 --- a/rapidast.py +++ b/rapidast.py @@ -158,15 +158,27 @@ def run(): f"log level set to debug. Config file: '{parser.parse_args().config_file}'" ) + # Load config file try: config = configmodel.RapidastConfigModel( yaml.safe_load(load_config_file(parser.parse_args().config_file)) ) except yaml.YAMLError as exc: raise RuntimeError( - f"Something went wrong while parsing one of the config '{parser.parse_args().config_file}':\n {str(exc)}" + f"YAML error in config {parser.parse_args().config_file}':\n {str(exc)}" ) from exc + # Optionally adds default if file exists (will not overwrite existing entries) + default_conf = os.path.join(os.path.dirname(__file__), "rapidast-defaults.yaml") + if os.path.exists(default_conf): + logging.info(f"Loading defaults from: {default_conf}") + try: + config.merge(yaml.safe_load(load_config_file(default_conf)), preserve=True) + except yaml.YAMLError as exc: + raise RuntimeError( + f"YAML error in config {default_conf}':\n {str(exc)}" + ) from exc + # Update to latest config schema if need be config = configmodel.converter.update_to_latest_config(config)