diff --git a/google/detectors/exposedui/nodered/README.md b/google/detectors/exposedui/nodered/README.md deleted file mode 100644 index f27486b5f..000000000 --- a/google/detectors/exposedui/nodered/README.md +++ /dev/null @@ -1,14 +0,0 @@ -# NodeRED unprotected instance - -This detector checks whether a NodeRED instance is available without -authentication (which allows very easy RCE). - -## Build jar file for this plugin - -Using `gradlew`: - -```shell -./gradlew jar -``` - -Tsunami identifiable jar file is located at `build/libs` directory. diff --git a/google/detectors/exposedui/nodered/build.gradle b/google/detectors/exposedui/nodered/build.gradle deleted file mode 100644 index be7c05b17..000000000 --- a/google/detectors/exposedui/nodered/build.gradle +++ /dev/null @@ -1,83 +0,0 @@ -plugins { - id 'java-library' -} - -description = 'Tsunami VulnDetector plugin for exposed NodeRED.' -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() -} - -java { - sourceCompatibility = JavaVersion.VERSION_11 - targetCompatibility = JavaVersion.VERSION_11 - - jar.manifest { - attributes('Implementation-Title': name, - 'Implementation-Version': version, - 'Built-By': System.getProperty('user.name'), - 'Built-JDK': System.getProperty('java.version'), - 'Source-Compatibility': sourceCompatibility, - 'Target-Compatibility': targetCompatibility) - } - - javadoc.options { - encoding = 'UTF-8' - use = true - links 'https://docs.oracle.com/javase/8/docs/api/' - source = '8' - } - - // Log stacktrace to console when test fails. - test { - testLogging { - exceptionFormat = 'full' - showExceptions true - showCauses true - showStackTraces true - } - maxHeapSize = '1500m' - } -} - -ext { - floggerVersion = '0.5.1' - guavaVersion = '28.2-jre' - javaxInjectVersion = '1' - jsoupVersion = '1.9.2' - okhttpVersion = '3.12.0' - protobufVersion = '3.11.4' - tsunamiVersion = 'latest.release' - - junitVersion = '4.13' - mockitoVersion = '2.28.2' - truthVersion = '1.0.1' -} - -dependencies { - implementation "com.google.flogger:flogger:${floggerVersion}" - implementation "com.google.flogger:google-extensions:${floggerVersion}" - implementation "com.google.flogger:flogger-system-backend:${floggerVersion}" - implementation "com.google.guava:guava:${guavaVersion}" - implementation "com.google.protobuf:protobuf-java:${protobufVersion}" - implementation "com.google.protobuf:protobuf-javalite:${protobufVersion}" - implementation "com.google.protobuf:protobuf-java-util:${protobufVersion}" - implementation "com.google.tsunami:tsunami-common:${tsunamiVersion}" - implementation "com.google.tsunami:tsunami-plugin:${tsunamiVersion}" - implementation "com.google.tsunami:tsunami-proto:${tsunamiVersion}" - implementation "javax.inject:javax.inject:${javaxInjectVersion}" - implementation "org.jsoup:jsoup:${jsoupVersion}" - - testImplementation "com.google.truth:truth:${truthVersion}" - testImplementation "com.google.truth.extensions:truth-java8-extension:${truthVersion}" - testImplementation "com.google.truth.extensions:truth-proto-extension:${truthVersion}" - testImplementation "com.squareup.okhttp3:mockwebserver:${okhttpVersion}" - testImplementation "junit:junit:${junitVersion}" - testImplementation "org.mockito:mockito-core:${mockitoVersion}" -} diff --git a/google/detectors/exposedui/nodered/settings.gradle b/google/detectors/exposedui/nodered/settings.gradle deleted file mode 100644 index 6a54ff043..000000000 --- a/google/detectors/exposedui/nodered/settings.gradle +++ /dev/null @@ -1 +0,0 @@ -rootProject.name = 'exposed_nodered' diff --git a/google/detectors/exposedui/nodered/src/main/java/com/google/tsunami/plugins/detectors/exposedui/nodered/NodeRedExposedUiDetector.java b/google/detectors/exposedui/nodered/src/main/java/com/google/tsunami/plugins/detectors/exposedui/nodered/NodeRedExposedUiDetector.java deleted file mode 100644 index 60edbbf8c..000000000 --- a/google/detectors/exposedui/nodered/src/main/java/com/google/tsunami/plugins/detectors/exposedui/nodered/NodeRedExposedUiDetector.java +++ /dev/null @@ -1,155 +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.exposedui.nodered; - -import static com.google.common.base.Preconditions.checkNotNull; -import static com.google.common.collect.ImmutableList.toImmutableList; -import static com.google.tsunami.common.net.http.HttpRequest.get; - -import com.google.common.collect.ImmutableList; -import com.google.common.flogger.GoogleLogger; -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.HttpResponse; -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.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.Vulnerability; -import com.google.tsunami.proto.VulnerabilityId; -import java.io.IOException; -import java.time.Clock; -import java.time.Instant; -import javax.inject.Inject; - -/** A {@link VulnDetector} that detects exposed NodeRED instances. */ -@PluginInfo( - type = PluginType.VULN_DETECTION, - name = "NodeRedExposedUiDetector", - version = "0.1", - description = "Detects exposed NodeRED instances", - author = "Pierre Precourt (pprecourt@google.com)", - bootstrapModule = NodeRedExposedUiDetectorBootstrapModule.class) -public final class NodeRedExposedUiDetector implements VulnDetector { - private static final GoogleLogger logger = GoogleLogger.forEnclosingClass(); - - private final Clock utcClock; - private final HttpClient httpClient; - - @Inject - NodeRedExposedUiDetector(@UtcClock Clock utcClock, HttpClient httpClient) { - this.utcClock = checkNotNull(utcClock); - this.httpClient = checkNotNull(httpClient).modify().build(); - } - - @Override - public DetectionReportList detect( - TargetInfo targetInfo, ImmutableList matchedServices) { - logger.atInfo().log("Starting detection: exposed NodeRED instances"); - DetectionReportList detectionReports = - DetectionReportList.newBuilder() - .addAllDetectionReports( - matchedServices.stream() - .filter(NetworkServiceUtils::isWebService) - .filter(this::isServiceVulnerable) - .map(networkService -> buildDetectionReport(targetInfo, networkService)) - .collect(toImmutableList())) - .build(); - - logger.atInfo().log( - "NodeRedExposedUiDetector finished, detected '%d' vulns.", - detectionReports.getDetectionReportsCount()); - return detectionReports; - } - - /* - * Checks if the settings are accessible. The /settings page will either return a JSON content or - * a permission denied error depending on the configuration for authentication. - * Because /settings can be a pretty common endpoint, we want to ensure that this is a rednode - * instance whilst not really performing JSON parsing hence the pattern matching instead. - */ - private boolean settingsAreAccessible(NetworkService networkService) { - String targetUri = NetworkServiceUtils.buildWebApplicationRootUrl(networkService) + "settings"; - - try { - HttpResponse response = httpClient.send(get(targetUri).withEmptyHeaders().build()); - - return response.status().isSuccess() - && response - .bodyString() - .map( - body -> - body.contains("\"httpNodeRoot\"") - && body.contains("\"version\"") - && body.contains("\"workflow\"")) - .orElse(false); - } catch (IOException e) { - logger.atWarning().withCause(e).log("Unable to query '%s'.", targetUri); - return false; - } - } - - private boolean isNodeRedInstance(NetworkService networkService) { - String targetUri = - NetworkServiceUtils.buildWebApplicationRootUrl(networkService) + "/red/tours/welcome.js"; - - try { - HttpResponse response = httpClient.send(get(targetUri).withEmptyHeaders().build()); - - return response.status().isSuccess() - && response.bodyString().map(body -> body.contains("Welcome to Node-RED")).orElse(false); - } catch (IOException e) { - logger.atWarning().withCause(e).log("Unable to query '%s'.", targetUri); - return false; - } - } - - private boolean isServiceVulnerable(NetworkService networkService) { - return isNodeRedInstance(networkService) && settingsAreAccessible(networkService); - } - - private DetectionReport buildDetectionReport( - TargetInfo scannedTarget, NetworkService vulnerableNetworkService) { - return DetectionReport.newBuilder() - .setTargetInfo(scannedTarget) - .setNetworkService(vulnerableNetworkService) - .setDetectionTimestamp(Timestamps.fromMillis(Instant.now(utcClock).toEpochMilli())) - .setDetectionStatus(DetectionStatus.VULNERABILITY_VERIFIED) - .setVulnerability( - Vulnerability.newBuilder() - .setMainId( - VulnerabilityId.newBuilder() - .setPublisher("GOOGLE") - .setValue("NODERED_EXPOSED_UI")) - .setSeverity(Severity.CRITICAL) - .setTitle("Exposed NodeRED instance") - .setRecommendation( - "Configure authentication or ensure the NodeRED instance is not exposed to the" - + " network. See" - + " https://nodered.org/docs/user-guide/runtime/securing-node-red for" - + " details") - .setDescription( - "NodeRED instance is exposed and can be used to compromise the system.")) - .build(); - } -} diff --git a/google/detectors/exposedui/nodered/src/main/java/com/google/tsunami/plugins/detectors/exposedui/nodered/NodeRedExposedUiDetectorBootstrapModule.java b/google/detectors/exposedui/nodered/src/main/java/com/google/tsunami/plugins/detectors/exposedui/nodered/NodeRedExposedUiDetectorBootstrapModule.java deleted file mode 100644 index 98babf6bc..000000000 --- a/google/detectors/exposedui/nodered/src/main/java/com/google/tsunami/plugins/detectors/exposedui/nodered/NodeRedExposedUiDetectorBootstrapModule.java +++ /dev/null @@ -1,27 +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.exposedui.nodered; - -import com.google.tsunami.plugin.PluginBootstrapModule; - -/** A {@link PluginBootstrapModule} for {@link NodeRedExposedUiDetector}. */ -public final class NodeRedExposedUiDetectorBootstrapModule extends PluginBootstrapModule { - - @Override - protected void configurePlugin() { - registerPlugin(NodeRedExposedUiDetector.class); - } -} diff --git a/google/detectors/exposedui/nodered/src/test/java/com/google/tsunami/plugins/detectors/exposedui/nodered/NodeRedExposedUiDetectorTest.java b/google/detectors/exposedui/nodered/src/test/java/com/google/tsunami/plugins/detectors/exposedui/nodered/NodeRedExposedUiDetectorTest.java deleted file mode 100644 index df81fbcc1..000000000 --- a/google/detectors/exposedui/nodered/src/test/java/com/google/tsunami/plugins/detectors/exposedui/nodered/NodeRedExposedUiDetectorTest.java +++ /dev/null @@ -1,190 +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.exposedui.nodered; - -import static com.google.common.truth.extensions.proto.ProtoTruth.assertThat; -import static com.google.tsunami.common.data.NetworkEndpointUtils.forHostname; -import static com.google.tsunami.common.data.NetworkEndpointUtils.forHostnameAndPort; - -import com.google.common.collect.ImmutableList; -import com.google.inject.Guice; -import com.google.protobuf.util.Timestamps; -import com.google.tsunami.common.net.http.HttpClientModule; -import com.google.tsunami.common.time.testing.FakeUtcClock; -import com.google.tsunami.common.time.testing.FakeUtcClockModule; -import com.google.tsunami.proto.DetectionReport; -import com.google.tsunami.proto.DetectionStatus; -import com.google.tsunami.proto.NetworkEndpoint; -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 com.google.tsunami.proto.Vulnerability; -import com.google.tsunami.proto.VulnerabilityId; -import java.io.IOException; -import java.time.Instant; -import javax.inject.Inject; -import okhttp3.mockwebserver.MockResponse; -import okhttp3.mockwebserver.MockWebServer; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.JUnit4; - -/** Tests for {@link NodeRedExposedUiDetector}. */ -@RunWith(JUnit4.class) -public final class NodeRedExposedUiDetectorTest { - - private final FakeUtcClock fakeUtcClock = - FakeUtcClock.create().setNow(Instant.parse("2020-01-01T00:00:00.00Z")); - - private static final String VULN_SETTINGS_PAGE = - "{\"httpNodeRoot\":\"/\",\"version\":\"3.1.5\",\"context\":{\"default\":\"memory\",\"stores\":[\"memory\"]},\"codeEditor\":{\"lib\":\"monaco\",\"options\":{}},\"markdownEditor\":{\"mermaid\":{\"enabled\":true}},\"libraries\":[{\"id\":\"local\",\"label\":\"editor:library.types.local\",\"user\":false,\"icon\":\"font-awesome/fa-hdd-o\"},{\"id\":\"examples\",\"label\":\"editor:library.types.examples\",\"user\":false,\"icon\":\"font-awesome/fa-life-ring\",\"types\":[\"flows\"],\"readOnly\":true}],\"flowFilePretty\":true,\"externalModules\":{},\"flowEncryptionType\":\"system\",\"diagnostics\":{\"enabled\":true,\"ui\":true},\"runtimeState\":{\"enabled\":false,\"ui\":false},\"functionExternalModules\":true,\"functionTimeout\":0,\"tlsConfigDisableLocalFiles\":false,\"editorTheme\":{\"palette\":{},\"projects\":{\"enabled\":false,\"workflow\":{\"mode\":\"manual\"}},\"languages\":[\"de\",\"en-US\",\"es-ES\",\"fr\",\"ja\",\"ko\",\"pt-BR\",\"ru\",\"zh-CN\",\"zh-TW\"]}}"; - private static final String VULN_TOUR_PAGE = "\"en-US\": \"Welcome to Node-RED 3.1!\","; - private static final Vulnerability EXPECTED_VULN = - Vulnerability.newBuilder() - .setMainId( - VulnerabilityId.newBuilder() - .setPublisher("GOOGLE") - .setValue("NODERED_EXPOSED_UI")) - .setSeverity(Severity.CRITICAL) - .setTitle("Exposed NodeRED instance") - .setRecommendation( - "Configure authentication or ensure the NodeRED instance is not exposed to the" - + " network. See" - + " https://nodered.org/docs/user-guide/runtime/securing-node-red for" - + " details") - .setDescription("NodeRED instance is exposed and can be used to compromise the system.") - .build(); - - private MockWebServer mockWebServer; - - @Inject private NodeRedExposedUiDetector detector; - - @Before - public void setUp() { - mockWebServer = new MockWebServer(); - - Guice.createInjector( - new FakeUtcClockModule(fakeUtcClock), - new HttpClientModule.Builder().build(), - new NodeRedExposedUiDetectorBootstrapModule()) - .injectMembers(this); - } - - @After - public void tearDown() throws IOException { - mockWebServer.shutdown(); - } - - @Test - public void detect_whenVulnerable_reportsVuln() throws IOException { - mockWebServer.enqueue(new MockResponse().setResponseCode(200).setBody(VULN_TOUR_PAGE)); - mockWebServer.enqueue(new MockResponse().setResponseCode(200).setBody(VULN_SETTINGS_PAGE)); - mockWebServer.start(); - - ImmutableList httpServices = - ImmutableList.of( - NetworkService.newBuilder() - .setNetworkEndpoint( - forHostnameAndPort(mockWebServer.getHostName(), mockWebServer.getPort())) - .setTransportProtocol(TransportProtocol.TCP) - .setServiceName("http") - .build()); - - assertThat( - detector - .detect(buildTargetInfo(forHostname(mockWebServer.getHostName())), httpServices) - .getDetectionReportsList()) - .containsExactly( - DetectionReport.newBuilder() - .setTargetInfo(buildTargetInfo(forHostname(mockWebServer.getHostName()))) - .setNetworkService(httpServices.get(0)) - .setDetectionTimestamp(Timestamps.fromMillis(fakeUtcClock.millis())) - .setDetectionStatus(DetectionStatus.VULNERABILITY_VERIFIED) - .setVulnerability(EXPECTED_VULN) - .build()); - } - - @Test - public void detect_whenSettingsDenied_reportsNothing() throws IOException { - mockWebServer.enqueue(new MockResponse().setResponseCode(200).setBody(VULN_TOUR_PAGE)); - mockWebServer.enqueue(new MockResponse().setResponseCode(401).setBody("Unauthorized")); - mockWebServer.start(); - - ImmutableList httpServices = - ImmutableList.of( - NetworkService.newBuilder() - .setNetworkEndpoint( - forHostnameAndPort(mockWebServer.getHostName(), mockWebServer.getPort())) - .setTransportProtocol(TransportProtocol.TCP) - .setServiceName("http") - .build()); - assertThat( - detector - .detect(buildTargetInfo(forHostname(mockWebServer.getHostName())), httpServices) - .getDetectionReportsList()) - .isEmpty(); - } - - @Test - public void detect_whenIsNotNodeRed_reportsNothing() throws IOException { - mockWebServer.enqueue(new MockResponse().setResponseCode(200).setBody("Apache server")); - mockWebServer.start(); - - ImmutableList httpServices = - ImmutableList.of( - NetworkService.newBuilder() - .setNetworkEndpoint( - forHostnameAndPort(mockWebServer.getHostName(), mockWebServer.getPort())) - .setTransportProtocol(TransportProtocol.TCP) - .setServiceName("http") - .build()); - assertThat( - detector - .detect(buildTargetInfo(forHostname(mockWebServer.getHostName())), httpServices) - .getDetectionReportsList()) - .isEmpty(); - } - - @Test - public void detect_whenNonHttpNetworkService_ignoresServices() { - ImmutableList nonHttpServices = - ImmutableList.of( - NetworkService.newBuilder().setServiceName("ssh").build(), - NetworkService.newBuilder().setServiceName("rdp").build()); - assertThat( - detector - .detect(buildTargetInfo(forHostname(mockWebServer.getHostName())), nonHttpServices) - .getDetectionReportsList()) - .isEmpty(); - } - - @Test - public void detect_whenEmptyNetworkService_generatesEmptyDetectionReports() { - assertThat( - detector - .detect( - buildTargetInfo(forHostname(mockWebServer.getHostName())), ImmutableList.of()) - .getDetectionReportsList()) - .isEmpty(); - } - - private static TargetInfo buildTargetInfo(NetworkEndpoint networkEndpoint) { - return TargetInfo.newBuilder().addNetworkEndpoints(networkEndpoint).build(); - } -} diff --git a/templated/templateddetector/plugins/exposedui/NodeRED_ExposedUI.textproto b/templated/templateddetector/plugins/exposedui/NodeRED_ExposedUI.textproto new file mode 100644 index 000000000..d145b828d --- /dev/null +++ b/templated/templateddetector/plugins/exposedui/NodeRED_ExposedUI.textproto @@ -0,0 +1,78 @@ +# proto-file: proto/templated_plugin.proto +# proto-message: TemplatedPlugin + +############### +# PLUGIN INFO # +############### + +info: { + type: VULN_DETECTION + name: "NodeRED_ExposedUI" + author: "Nicolas Golubovic (ngolubovic@google.com)" + version: "0.2" +} + +finding: { + main_id: { + publisher: "GOOGLE" + value: "NODERED_EXPOSED_UI" + } + title: "Exposed Node-RED instance" + description: "Node-RED instance is exposed and can be used to compromise the system." + recommendation: + "Configure authentication or ensure the Node-RED instance is not exposed " + "to the network. See " + "https://nodered.org/docs/user-guide/runtime/securing-node-red for details." + severity: CRITICAL +} + +########### +# ACTIONS # +########### + +actions: { + name: "is_nodered" + http_request: { + method: GET + uri: "/red/tours/welcome.js" + response: { + http_status: 200 + expect_all: { + conditions: { body: {} contains: "Welcome to Node-RED" } + } + } + } +} + + +# Checks if the settings are accessible. The /settings page will either return a +# JSON content or a permission denied error depending on the configuration for +# authentication. Because /settings can be a pretty common endpoint, we want to +# ensure that this is a rednode instance whilst not really performing JSON +# parsing hence the pattern matching instead. +actions: { + name: "is_settings_available" + http_request: { + method: GET + uri: "/settings" + response: { + http_status: 200 + expect_all: { + conditions: { body {} contains: '"httpNodeRoot"' } + conditions: { body {} contains: '"version"' } + conditions: { body {} contains: '"workflow"' } + } + } + } +} + +############# +# WORKFLOWS # +############# + +workflows: { + actions: [ + "is_nodered", + "is_settings_available" + ] +} diff --git a/templated/templateddetector/plugins/exposedui/NodeRED_ExposedUI_test.textproto b/templated/templateddetector/plugins/exposedui/NodeRED_ExposedUI_test.textproto new file mode 100644 index 000000000..397399477 --- /dev/null +++ b/templated/templateddetector/plugins/exposedui/NodeRED_ExposedUI_test.textproto @@ -0,0 +1,87 @@ +# proto-file: proto/templated_plugin_tests.proto +# proto-message: TemplatedPluginTests + +config: { + tested_plugin: "NodeRED_ExposedUI" +} + +tests: { + name: "whenVulnerable_returnsVuln" + expect_vulnerability: true + + mock_http_server: { + mock_responses: [ + { + uri: "/red/tours/welcome.js" + status: 200 + body_content: + 'export default { version: "3.1.0", steps: [{' + 'titleIcon: "fa fa-map-o", title: { ' + '"en-US": "Welcome to Node-RED 3.1!", ...' + }, + { + uri: "/settings" + status: 200 + body_content: + '{"httpNodeRoot":"/","version":"3.1.5","context":{"default":"memory",' + '"stores":["memory"]},"codeEditor":{"lib":"monaco","options":{}},' + '"markdownEditor":{"mermaid":{"enabled":true}},"libraries":[{"id":' + '"local","label":"editor:library.types.local","user":false,"icon":' + '"font-awesome/fa-hdd-o"},{"id":"examples","label":' + '"editor:library.types.examples","user":false,"icon":' + '"font-awesome/fa-life-ring","types":["flows"],"readOnly":true}],' + '"flowFilePretty":true,"externalModules":{},"flowEncryptionType":' + '"system","diagnostics":{"enabled":true,"ui":true},"runtimeState":' + '{"enabled":false,"ui":false},"functionExternalModules":true,' + '"functionTimeout":0,"tlsConfigDisableLocalFiles":false,"editorTheme"' + ':{"palette":{},"projects":{"enabled":false,"workflow":{"mode":' + '"manual"}},"languages":["de","en-US","es-ES","fr","ja","ko","pt-BR",' + '"ru","zh-CN","zh-TW"]}}%' + } + ] + } +} + + +tests: { + name: "whenNotNodeRed_returnsNoVuln" + expect_vulnerability: false + + mock_http_server: { + mock_responses: [ + { + uri: "/red/tours/welcome.js" + status: 200 + body_content: "Joomla" + }, + { + uri: "/settings" + status: 200 + body_content: "irrelevant" + } + ] + } +} + +tests: { + name: "whenNotNodeRedSettings_returnsNoVuln" + expect_vulnerability: false + + mock_http_server: { + mock_responses: [ + { + uri: "/red/tours/welcome.js" + status: 200 + body_content: + 'export default { version: "3.1.0", steps: [{' + 'titleIcon: "fa fa-map-o", title: { ' + '"en-US": "Welcome to Node-RED 3.1!", ...' + }, + { + uri: "/settings" + status: 200 + body_content: "not node red settings" + } + ] + } +} \ No newline at end of file