diff --git a/docker/CHANGELOG.md b/docker/CHANGELOG.md index 0b3e990686c..e28d9369770 100644 --- a/docker/CHANGELOG.md +++ b/docker/CHANGELOG.md @@ -1,6 +1,9 @@ # Changelog All notable changes to the docker containers will be documented in this file. +### 2026-06-19 +- Fetch packaged scan HTTP Sender scripts from [community-scripts](https://github.com/zaproxy/community-scripts/tree/main/httpsender) instead of keeping local copies. NOTE: The names of the script files have changed. + ### 2026-05-08 - Fixed bug in baseline scan which could result in a python error when running the packaged scan without a mapped drive. diff --git a/docker/Dockerfile-live b/docker/Dockerfile-live index 24e44777900..f74daa358aa 100644 --- a/docker/Dockerfile-live +++ b/docker/Dockerfile-live @@ -34,6 +34,13 @@ RUN --mount=type=secret,id=webswing_url \ # Remove Webswing bundled examples rm -Rf webswing/apps/ +# Fetch packaged scan HttpSender scripts from community-scripts +RUN mkdir -p /httpsender-scripts && \ + curl -fsSL "https://raw.githubusercontent.com/zaproxy/community-scripts/main/httpsender/AlertOnHttpResponseCodeErrors.js" \ + -o "/httpsender-scripts/AlertOnHttpResponseCodeErrors.js" && \ + curl -fsSL "https://raw.githubusercontent.com/zaproxy/community-scripts/main/httpsender/AlertOnUnexpectedContentTypes.js" \ + -o "/httpsender-scripts/AlertOnUnexpectedContentTypes.js" + FROM debian:trixie-slim AS final LABEL maintainer="psiinon@gmail.com" @@ -101,7 +108,7 @@ COPY --link --chown=1000:1000 webswing.config /zap/webswing/ COPY --link --chown=1000:1000 webswing.properties /zap/webswing/ COPY --link --chown=1000:1000 policies /home/zap/.ZAP_D/policies/ COPY --link --chown=1000:1000 policies /root/.ZAP_D/policies/ -COPY --link --chown=1000:1000 scripts /home/zap/.ZAP_D/scripts/ +COPY --link --from=builder --chown=1000:1000 /httpsender-scripts/ /home/zap/.ZAP_D/scripts/scripts/httpsender/ COPY --link --chown=1000:1000 .xinitrc /home/zap/ COPY --link --chown=1000:1000 firefox /home/zap/.mozilla/firefox/ diff --git a/docker/Dockerfile-stable b/docker/Dockerfile-stable index 0ce9d286584..2d853f3d16e 100644 --- a/docker/Dockerfile-stable +++ b/docker/Dockerfile-stable @@ -34,6 +34,13 @@ RUN --mount=type=secret,id=webswing_url \ # Remove Webswing bundled examples rm -Rf webswing/apps/ +# Fetch packaged scan HttpSender scripts from community-scripts +RUN mkdir -p /httpsender-scripts && \ + curl -fsSL "https://raw.githubusercontent.com/zaproxy/community-scripts/main/httpsender/AlertOnHttpResponseCodeErrors.js" \ + -o "/httpsender-scripts/AlertOnHttpResponseCodeErrors.js" && \ + curl -fsSL "https://raw.githubusercontent.com/zaproxy/community-scripts/main/httpsender/AlertOnUnexpectedContentTypes.js" \ + -o "/httpsender-scripts/AlertOnUnexpectedContentTypes.js" + FROM debian:bookworm-slim AS final LABEL maintainer="psiinon@gmail.com" @@ -105,7 +112,7 @@ COPY --link --chown=1000:1000 webswing.properties /zap/webswing/ COPY --link --chown=1000:1000 policies /home/zap/.ZAP/policies/ COPY --link --chown=1000:1000 policies /root/.ZAP/policies/ # The scan script loads the scripts from dev home dir. -COPY --link --chown=1000:1000 scripts /home/zap/.ZAP_D/scripts/ +COPY --link --from=builder --chown=1000:1000 /httpsender-scripts/ /home/zap/.ZAP_D/scripts/scripts/httpsender/ COPY --link --chown=1000:1000 .xinitrc /home/zap/ COPY --link --chown=1000:1000 firefox /home/zap/.mozilla/firefox/ diff --git a/docker/Dockerfile-weekly b/docker/Dockerfile-weekly index 387e8a7c61f..ccbe189edb7 100644 --- a/docker/Dockerfile-weekly +++ b/docker/Dockerfile-weekly @@ -21,6 +21,13 @@ RUN --mount=type=secret,id=webswing_url \ # Remove Webswing bundled examples rm -Rf webswing/apps/ +# Fetch packaged scan HttpSender scripts from community-scripts +RUN mkdir -p /httpsender-scripts && \ + curl -fsSL "https://raw.githubusercontent.com/zaproxy/community-scripts/main/httpsender/AlertOnHttpResponseCodeErrors.js" \ + -o "/httpsender-scripts/AlertOnHttpResponseCodeErrors.js" && \ + curl -fsSL "https://raw.githubusercontent.com/zaproxy/community-scripts/main/httpsender/AlertOnUnexpectedContentTypes.js" \ + -o "/httpsender-scripts/AlertOnUnexpectedContentTypes.js" + FROM debian:trixie-slim AS final LABEL maintainer="psiinon@gmail.com" @@ -84,7 +91,7 @@ COPY --link --chown=1000:1000 webswing.config /zap/webswing/ COPY --link --chown=1000:1000 webswing.properties /zap/webswing/ COPY --link --chown=1000:1000 policies /home/zap/.ZAP_D/policies/ COPY --link --chown=1000:1000 policies /root/.ZAP_D/policies/ -COPY --link --chown=1000:1000 scripts /home/zap/.ZAP_D/scripts/ +COPY --link --from=builder --chown=1000:1000 /httpsender-scripts/ /home/zap/.ZAP_D/scripts/scripts/httpsender/ COPY --link --chown=1000:1000 .xinitrc /home/zap/ COPY --link --chown=1000:1000 firefox /home/zap/.mozilla/firefox/ diff --git a/docker/scripts/scripts/httpsender/Alert_on_HTTP_Response_Code_Errors.js b/docker/scripts/scripts/httpsender/Alert_on_HTTP_Response_Code_Errors.js deleted file mode 100644 index d421b38ffb5..00000000000 --- a/docker/scripts/scripts/httpsender/Alert_on_HTTP_Response_Code_Errors.js +++ /dev/null @@ -1,104 +0,0 @@ -// A script which will raise alerts based on HTTP Response codes -// By default it will raise 'Info' level alerts for Client Errors (4xx) (apart from 404s) and 'Low' Level alerts for Server Errors (5xx) -// But it can be easily changed. - -const Integer = Java.type("java.lang.Integer") -const Pattern = Java.type("java.util.regex.Pattern") - -const Alert = Java.type("org.parosproxy.paros.core.scanner.Alert") -const ExtensionAlert = Java.type("org.zaproxy.zap.extension.alert.ExtensionAlert") -const HistoryReference = Java.type("org.parosproxy.paros.model.HistoryReference") - -const extensionAlert = control.getExtensionLoader().getExtension(ExtensionAlert.NAME) - -pluginid = 100000 // https://github.com/zaproxy/zaproxy/blob/main/docs/scanners.md - -function sendingRequest(msg, initiator, helper) { - // Nothing to do -} - -function responseReceived(msg, initiator, helper) { - if (isGloballyExcluded(msg)) { - // Not of interest. - return - } - - if (extensionAlert != null) { - var code = msg.getResponseHeader().getStatusCode() - if (code < 400 || code >= 600) { - // Do nothing - } else { - var risk = 0 // Info - var alertRef = 1 - var title = "A Client Error response code was returned by the server" - if (code >= 500) { - // Server error - risk = 1 // Low - title = "A Server Error response code was returned by the server" - alertRef = 2 - } - // CONFIDENCE_HIGH = 3 (we can be pretty sure we're right) - var alert = new Alert(pluginid, risk, 3, title) - var ref = msg.getHistoryRef() - if (ref != null && HistoryReference.getTemporaryTypes().contains(Integer.valueOf(ref.getHistoryType()))) { - // Dont use temporary types as they will get deleted - ref = null - } - if (ref == null) { - // map the initiator - var type - switch (initiator) { - case 1: // PROXY_INITIATOR - type = 1 // Proxied - break - case 2: // ACTIVE_SCANNER_INITIATOR - type = 3 // Scanner - break - case 3: // SPIDER_INITIATOR - type = 2 // Spider - break - case 4: // FUZZER_INITIATOR - type = 8 // Fuzzer - break - case 5: // AUTHENTICATION_INITIATOR - type = 15 // User - break - case 6: // MANUAL_REQUEST_INITIATOR - type = 15 // User - break - case 8: // BEAN_SHELL_INITIATOR - type = 15 // User - break - case 9: // ACCESS_CONTROL_SCANNER_INITIATOR - type = 13 // Access control - break - default: - type = 15 // User - fallback - break - } - ref = new HistoryReference(model.getSession(), type, msg) - } - alert.setMessage(msg) - alert.setUri(msg.getRequestHeader().getURI().toString()) - alert.setDescription("A response code of " + code + " was returned by the server.\n" + - "This may indicate that the application is failing to handle unexpected input correctly.\n" + - "Raised by the 'Alert on HTTP Response Code Error' script"); - alert.setEvidence(code.toString()) - alert.setAlertRef(pluginid + "-" + alertRef) - alert.setCweId(388) // CWE CATEGORY: Error Handling - alert.setWascId(20) // WASC Improper Input Handling - extensionAlert.alertFound(alert , ref) - } - } -} - -function isGloballyExcluded(msg) { - var url = msg.getRequestHeader().getURI().toString() - var regexes = model.getSession().getGlobalExcludeURLRegexs() - for (var i in regexes) { - if (Pattern.compile(regexes[i], Pattern.CASE_INSENSITIVE).matcher(url).matches()) { - return true - } - } - return false -} diff --git a/docker/scripts/scripts/httpsender/Alert_on_Unexpected_Content_Types.js b/docker/scripts/scripts/httpsender/Alert_on_Unexpected_Content_Types.js deleted file mode 100644 index 73a871a270d..00000000000 --- a/docker/scripts/scripts/httpsender/Alert_on_Unexpected_Content_Types.js +++ /dev/null @@ -1,106 +0,0 @@ -// A script which will raise alerts based on unexpected Content-Types -// By default it will raise 'Low' level alerts for content types that are not expected to be returned by APIs. -// But it can be easily changed. - -const Integer = Java.type("java.lang.Integer") -const Pattern = Java.type("java.util.regex.Pattern") - -const Alert = Java.type("org.parosproxy.paros.core.scanner.Alert") -const ExtensionAlert = Java.type("org.zaproxy.zap.extension.alert.ExtensionAlert") -const HistoryReference = Java.type("org.parosproxy.paros.model.HistoryReference") - -const extensionAlert = control.getExtensionLoader().getExtension(ExtensionAlert.NAME) - -var pluginid = 100001 // https://github.com/zaproxy/zaproxy/blob/main/docs/scanners.md - -var expectedTypes = [ - "application/octet-stream", - "text/plain" - ] - -var expectedTypeGroups = ["json", "yaml", "xml"] - -function sendingRequest(msg, initiator, helper) { - // Nothing to do -} - -function responseReceived(msg, initiator, helper) { - if (isGloballyExcluded(msg)) { - // Not of interest. - return - } - - if (extensionAlert != null) { - var ctype = msg.getResponseHeader().getHeader("Content-Type") - if (ctype != null) { - if (ctype.indexOf(";") > 0) { - ctype = ctype.substring(0, ctype.indexOf(";")) - } - if (!msg.getResponseHeader().hasContentType(expectedTypeGroups) && expectedTypes.indexOf(ctype) < 0) { - // Another rule will complain if theres no type - - var risk = 1 // Low - var title = "Unexpected Content-Type was returned" - // CONFIDENCE_HIGH = 3 (we can be pretty sure we're right) - var alert = new Alert(pluginid, risk, 3, title) - var ref = msg.getHistoryRef() - if (ref != null && HistoryReference.getTemporaryTypes().contains(Integer.valueOf(ref.getHistoryType()))) { - // Dont use temporary types as they will get deleted - ref = null - } - if (ref == null) { - // map the initiator - var type - switch (initiator) { - case 1: // PROXY_INITIATOR - type = 1 // Proxied - break - case 2: // ACTIVE_SCANNER_INITIATOR - type = 3 // Scanner - break - case 3: // SPIDER_INITIATOR - type = 2 // Spider - break - case 4: // FUZZER_INITIATOR - type = 8 // Fuzzer - break - case 5: // AUTHENTICATION_INITIATOR - type = 15 // User - break - case 6: // MANUAL_REQUEST_INITIATOR - type = 15 // User - break - case 8: // BEAN_SHELL_INITIATOR - type = 15 // User - break - case 9: // ACCESS_CONTROL_SCANNER_INITIATOR - type = 13 // Access control - break - default: - type = 15 // User - fallback - break - } - ref = new HistoryReference(model.getSession(), type, msg) - } - alert.setMessage(msg) - alert.setUri(msg.getRequestHeader().getURI().toString()) - alert.setDescription("A Content-Type of " + ctype + " was returned by the server.\n" + - "This is not one of the types expected to be returned by an API.\n" + - "Raised by the 'Alert on Unexpected Content Types' script"); - alert.setEvidence(ctype) - extensionAlert.alertFound(alert , ref) - } - } - } -} - -function isGloballyExcluded(msg) { - var url = msg.getRequestHeader().getURI().toString() - var regexes = model.getSession().getGlobalExcludeURLRegexs() - for (var i in regexes) { - if (Pattern.compile(regexes[i], Pattern.CASE_INSENSITIVE).matcher(url).matches()) { - return true - } - } - return false -} diff --git a/docker/zap-api-scan.py b/docker/zap-api-scan.py index acf0a52f95b..56ed5c46cdc 100755 --- a/docker/zap-api-scan.py +++ b/docker/zap-api-scan.py @@ -385,9 +385,6 @@ def main(argv): # Copy across the files that may not be in all of the docker images try: - subprocess.check_output(['docker', 'exec', '-t', cid, 'mkdir', '-p', '/home/zap/.ZAP_D/scripts/scripts/httpsender/']) - cp_to_docker(cid, 'scripts/scripts/httpsender/Alert_on_HTTP_Response_Code_Errors.js', '/home/zap/.ZAP_D/') - cp_to_docker(cid, 'scripts/scripts/httpsender/Alert_on_Unexpected_Content_Types.js', '/home/zap/.ZAP_D/') cp_to_docker(cid, 'policies/API-Minimal.policy', '/home/zap/.ZAP_D/') if target_file: cp_to_docker(cid, target_file, '/zap/') @@ -418,10 +415,10 @@ def main(argv): # Enable scripts script_engine = get_script_engine(zap, ['Oracle Nashorn', 'Graal.js']) - zap.script.load('Alert_on_HTTP_Response_Code_Errors.js', 'httpsender', script_engine, '/home/zap/.ZAP_D/scripts/scripts/httpsender/Alert_on_HTTP_Response_Code_Errors.js') - zap.script.enable('Alert_on_HTTP_Response_Code_Errors.js') - zap.script.load('Alert_on_Unexpected_Content_Types.js', 'httpsender', script_engine, '/home/zap/.ZAP_D/scripts/scripts/httpsender/Alert_on_Unexpected_Content_Types.js') - zap.script.enable('Alert_on_Unexpected_Content_Types.js') + zap.script.load('AlertOnHttpResponseCodeErrors.js', 'httpsender', script_engine, '/home/zap/.ZAP_D/scripts/scripts/httpsender/AlertOnHttpResponseCodeErrors.js') + zap.script.enable('AlertOnHttpResponseCodeErrors.js') + zap.script.load('AlertOnUnexpectedContentTypes.js', 'httpsender', script_engine, '/home/zap/.ZAP_D/scripts/scripts/httpsender/AlertOnUnexpectedContentTypes.js') + zap.script.enable('AlertOnUnexpectedContentTypes.js') # Import the API defn if format == 'openapi':