diff --git a/package-lock.json b/package-lock.json index bb9e8c9..0f19305 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "soos-dast", - "version": "2.0.22", + "version": "2.0.23", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "soos-dast", - "version": "2.0.22", + "version": "2.0.23", "license": "MIT", "dependencies": { "@soos-io/api-client": "0.2.35", diff --git a/package.json b/package.json index f4b20d5..1ef95ae 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "soos-dast", - "version": "2.0.22", + "version": "2.0.23", "description": "SOOS DAST - The affordable no limit web vulnerability scanner", "main": "index.js", "scripts": { diff --git a/src/index.ts b/src/index.ts index 2e95c39..d464a17 100644 --- a/src/index.ts +++ b/src/index.ts @@ -19,7 +19,7 @@ import { IntegrationType, } from "@soos-io/api-client"; import { version } from "../package.json"; -import { ZAPCommandGenerator } from "./utilities"; +import { ZAPCommandGenerator, ZAPReportTransformer } from "./utilities"; import AnalysisService from "@soos-io/api-client/dist/services/AnalysisService"; import AnalysisArgumentParser, { IBaseScanArguments, @@ -324,23 +324,15 @@ class SOOSDASTAnalysis { const runSuccess = fs.existsSync(SOOS_DAST_CONSTANTS.Files.ReportScanResultFile); soosLogger.info(`Scan finished with success: ${runSuccess}`); - const discoveredUrls = - fs.existsSync(SOOS_DAST_CONSTANTS.Files.SpideredUrlsFile) && - fs.statSync(SOOS_DAST_CONSTANTS.Files.SpideredUrlsFile).isFile() - ? fs - .readFileSync(SOOS_DAST_CONSTANTS.Files.SpideredUrlsFile, "utf-8") - .split("\n") - .filter((url) => url.trim() !== "") - : []; - const data = JSON.parse( - fs.readFileSync(SOOS_DAST_CONSTANTS.Files.ReportScanResultFile, "utf-8"), - ); - data["discoveredUrls"] = discoveredUrls; - fs.writeFileSync( - SOOS_DAST_CONSTANTS.Files.ReportScanResultFile, - JSON.stringify(data, null, 4), + fs.readFileSync( + SOOS_DAST_CONSTANTS.Files.ReportScanResultFile, + SOOS_CONSTANTS.FileUploads.Encoding, + ), ); + + ZAPReportTransformer.transformReport(data); + const formData = new FormData(); formData.append("resultVersion", data["@version"]); diff --git a/src/utilities/ZAPReportTransformer.ts b/src/utilities/ZAPReportTransformer.ts new file mode 100644 index 0000000..13b2955 --- /dev/null +++ b/src/utilities/ZAPReportTransformer.ts @@ -0,0 +1,47 @@ +import * as fs from "fs"; +import { SOOS_DAST_CONSTANTS } from "../constants"; + +export class ZAPReportTransformer { + // TODO - PA-12868 Rework this approach + public static transformReport(reportData: any): void { + this.addDiscoveredUrls(reportData); + this.obfuscateFields(reportData); + this.saveReportContent(reportData); + } + + public static addDiscoveredUrls(reportData: any): void { + const discoveredUrls = + fs.existsSync(SOOS_DAST_CONSTANTS.Files.SpideredUrlsFile) && + fs.statSync(SOOS_DAST_CONSTANTS.Files.SpideredUrlsFile).isFile() + ? fs + .readFileSync(SOOS_DAST_CONSTANTS.Files.SpideredUrlsFile, "utf-8") + .split("\n") + .filter((url) => url.trim() !== "") + : []; + + reportData["discoveredUrls"] = discoveredUrls; + } + + public static obfuscateFields(reportData: any): void { + for (let key in reportData) { + if (typeof reportData[key] === "object" && reportData[key] !== null) { + this.obfuscateFields(reportData[key]); + } else { + if (key === "request-header") { + reportData[key] = this.obfuscateBearerToken(reportData[key]); + } + } + } + } + + private static obfuscateBearerToken(field: string): string { + return field.replace(/(Authorization:\s*)[^\r\n]+/, "$1****"); + } + + private static saveReportContent = (reportData: any) => { + fs.writeFileSync( + SOOS_DAST_CONSTANTS.Files.ReportScanResultFile, + JSON.stringify(reportData, null, 4), + ); + }; +} diff --git a/src/utilities/index.ts b/src/utilities/index.ts index f7d8e8c..8980ece 100644 --- a/src/utilities/index.ts +++ b/src/utilities/index.ts @@ -1 +1,2 @@ export * from "./ZAPCommandGenerator"; +export * from "./ZAPReportTransformer"; diff --git a/src/zap_hooks/helpers/auth_context.py b/src/zap_hooks/helpers/auth.py similarity index 92% rename from src/zap_hooks/helpers/auth_context.py rename to src/zap_hooks/helpers/auth.py index e7c1d81..e00d6df 100644 --- a/src/zap_hooks/helpers/auth_context.py +++ b/src/zap_hooks/helpers/auth.py @@ -20,36 +20,12 @@ from src.zap_hooks.helpers.utilities import array_to_dict, log from src.zap_hooks.model.log_level import LogLevel from src.zap_hooks.helpers.logging import LoggingFilter -import src.zap_hooks.helpers.globals as globals -def setup_context(zap, target, config): +def setup_replacer(zap, target, config): # Set an X-Scanner header so requests can be identified in logs zap.replacer.add_rule(description='Scanner', enabled=True, matchtype='REQ_HEADER', matchregex=False, matchstring='X-Scanner', replacement="ZAP") - globals.context_id = zap.context.new_context(globals.context_name) - - zap_common.context_name = globals.context_name - zap_common.context_id = globals.context_id - - # include everything below the target - config.auth_include_urls.append(target + '.*') - - # include additional url's - for include in config.auth_include_urls: - zap.context.include_in_context(globals.context_name, include) - log(f"Included {include}") - - # exclude all urls that end the authenticated session - if len(config.auth_exclude_urls) == 0: - config.auth_exclude_urls.append('.*logout.*') - config.auth_exclude_urls.append('.*uitloggen.*') - config.auth_exclude_urls.append('.*afmelden.*') - config.auth_exclude_urls.append('.*signout.*') - - for exclude in config.auth_exclude_urls: - zap.context.exclude_from_context(globals.context_name, exclude) - log(f"Excluded {exclude}") def setup_webdriver() -> webdriver.Chrome: @@ -82,7 +58,7 @@ def authenticate(zap, target, config): clear_driver = False try: if zap is not None: - setup_context(zap, target, config) + setup_replacer(zap, target, config) if config.auth_login_url: driver_instance = setup_webdriver() @@ -205,7 +181,7 @@ def add_authorization_header(zap, auth_token): if zap is not None: zap.replacer.add_rule(description='AuthHeader', enabled=True, matchtype='REQ_HEADER', matchregex=False, matchstring='Authorization', replacement=auth_token) - log(f"Authorization header added: {auth_token}") + log("Authorization header added") def login(driver, config): """Main function to perform logging using selenium webdriver""" diff --git a/src/zap_hooks/helpers/configuration.py b/src/zap_hooks/helpers/configuration.py index 97a243f..57d6896 100644 --- a/src/zap_hooks/helpers/configuration.py +++ b/src/zap_hooks/helpers/configuration.py @@ -65,8 +65,6 @@ def load_config(self, extra_zap_params): self.auth_check_delay = float(os.environ.get('AUTH_CHECK_DELAY') or 5) self.auth_check_element = os.environ.get('AUTH_CHECK_ELEMENT') or EMPTY_STRING self.auth_verification_url = os.environ.get('AUTH_VERIFICATION_URL') or EMPTY_STRING - self.auth_exclude_urls = self._get_hook_param_list(os.environ.get('AUTH_EXCLUDE_URLS')) or list() - self.auth_include_urls = self._get_hook_param_list(os.environ.get('AUTH_INCLUDE_URLS')) or list() self.xss_collector = os.environ.get('XSS_COLLECTOR') or EMPTY_STRING self.cookies = os.environ.get('CUSTOM_COOKIES') or EMPTY_STRING self.header = os.environ.get('CUSTOM_HEADER') or EMPTY_STRING diff --git a/src/zap_hooks/helpers/constants.py b/src/zap_hooks/helpers/constants.py index 02f1e1c..29cd30b 100644 --- a/src/zap_hooks/helpers/constants.py +++ b/src/zap_hooks/helpers/constants.py @@ -3,7 +3,6 @@ UTF_8_ENCODING = "utf-8" LOG_FORMAT = "%(asctime)s %(message)s" ZAP_ACTIVE_SCAN_POLICY_NAME = "Default Policy" -ZAP_AUTH_CONTEXT = "ctx-zap-docker" ZAP_HTTP_SENDER_SCRIPTS_FOLDER_PATH = "/home/zap/.ZAP/scripts/scripts/httpsender" FILTER_LOGS = [ diff --git a/src/zap_hooks/helpers/globals.py b/src/zap_hooks/helpers/globals.py deleted file mode 100644 index 30e2dfa..0000000 --- a/src/zap_hooks/helpers/globals.py +++ /dev/null @@ -1,6 +0,0 @@ -from src.zap_hooks.helpers.constants import ZAP_AUTH_CONTEXT -def initialize(): - global context_name - global context_id - context_name = ZAP_AUTH_CONTEXT - context_id = None \ No newline at end of file diff --git a/src/zap_hooks/soos_zap_hook.py b/src/zap_hooks/soos_zap_hook.py index bf95b56..0f5f943 100644 --- a/src/zap_hooks/soos_zap_hook.py +++ b/src/zap_hooks/soos_zap_hook.py @@ -2,13 +2,12 @@ from typing import List import os -from src.zap_hooks.helpers.auth_context import authenticate +from src.zap_hooks.helpers.auth import authenticate from src.zap_hooks.helpers.configuration import DASTConfig from src.zap_hooks.helpers.utilities import log, exit_app, LogLevel, serialize_and_save from src.zap_hooks.helpers import custom_cookies as cookies from src.zap_hooks.helpers import custom_headers as headers from src.zap_hooks.helpers import constants as Constants -import src.zap_hooks.helpers.globals as globals config = DASTConfig() @@ -25,7 +24,6 @@ def start_zap(port, extra_zap_params): def zap_started(zap, target): log("zap_started_hook is running") os.system("cp -R /zap/reports/traditional-json/report.json /root/.ZAP/reports/traditional-json/report.json") - globals.initialize() try: # ZAP Docker scripts reset the target to the root URL if target.count('/') > 2: @@ -71,7 +69,6 @@ def zap_started(zap, target): def zap_import_context(zap, context_file): log("zap_import_context_hook is running") log(f"importing context from file: {context_file}") - zap.context.remove_context(globals.context_name) def zap_pre_shutdown(zap): if config.debug_mode: