CVE-2024-28237: Insufficient Information (NVD-CWE-noinfo)
value: OctoPrint provides a web interface for controlling consumer 3D printers. OctoPrint versions up until and including 1.9.3 contain a vulnerability that allows malicious admins to configure or talk a victim with administrator rights into configuring a webcam snapshot URL which when tested through the "Test" button included in the web interface will execute JavaScript code in the victims browser when attempting to render the snapshot image. An attacker who successfully talked a victim with admin rights into performing a snapshot test with such a crafted URL could use this to retrieve or modify sensitive configuration settings, interrupt prints or otherwise interact with the OctoPrint instance in a malicious way. The vulnerability is patched in version 1.10.0rc3. OctoPrint administrators are strongly advised to thoroughly vet who has admin access to their installation and what settings they modify based on instructions by strangers.
CWE description: There is insufficient information about the issue to classify it; details are unkown or unspecified.
Published: 2024-03-18T22:15Z
Repository: https://github.com/OctoPrint/OctoPrint
Language: JavaScript
Files changed: src/octoprint/plugins/classicwebcam/static/js/classicwebcam_settings.js
Commit date: 2024-03-12 17:53:42 +0100
Commit message: 🔒️ Fix a XSS in the webcam snapshot test

See GHSA-x7mf-wrh9-r76c

Diff: diff --git a/src/octoprint/plugins/classicwebcam/static/js/classicwebcam_settings.js b/src/octoprint/plugins/classicwebcam/static/js/classicwebcam_settings.js
index 7511565c6..15cc6ded3 100644
--- a/src/octoprint/plugins/classicwebcam/static/js/classicwebcam_settings.js
+++ b/src/octoprint/plugins/classicwebcam/static/js/classicwebcam_settings.js
@@ -191,32 +191,29 @@ $(function () {
                             }
                         });
                         return;
                     }
 
-                    var content = response.response.content;
-                    var contentType = response.response.assumed_content_type;
+                    const content = response.response.content;
+                    const contentType = response.response.assumed_content_type;
 
-                    var mimeType = "image/jpeg";
-                    if (contentType) {
-                        mimeType = contentType.split(";")[0];
-                    }
-
-                    var text = gettext(
+                    const text = gettext(
                         "If you see your webcam snapshot picture below, the entered snapshot URL is ok."
                     );
+                    const mimeType = contentType
+                        ? contentType.split(";")[0]
+                        : "image/jpeg";
+
+                    const textElement = $("<p></p>").text(text);
+                    const imgElement = $("<img>")
+                        .attr("src", "data:" + mimeType + ";base64," + content)
+                        .css("border", "1px solid black");
+                    const message = $("<p></p>").append(textElement).append(imgElement);
+
                     showMessageDialog({
                         title: gettext("Snapshot test"),
-                        message: $(
-                            "<p>" +
-                                text +
-                                '</p><p><img src="data:' +
-                                mimeType +
-                                ";base64," +
-                                content +
-                                '" style="border: 1px solid black" /></p>'
-                        ),
+                        message: message,
                         onclose: function () {
                             self.testWebcamSnapshotUrlBusy(false);
                         }
                     });
                 })

Vulnerable code:                     var content = response.response.content;
                    var contentType = response.response.assumed_content_type;
                    var mimeType = "image/jpeg";
                    if (contentType) {
                        mimeType = contentType.split(";")[0];
                    }

                    var text = gettext(
                        message: $(
                            "<p>" +
                                text +
                                '</p><p><img src="data:' +
                                mimeType +
                                ";base64," +
                                content +
                                '" style="border: 1px solid black" /></p>'
                        ),
Fixed code:                     const content = response.response.content;
                    const contentType = response.response.assumed_content_type;
                    const text = gettext(
                    const mimeType = contentType
                        ? contentType.split(";")[0]
                        : "image/jpeg";

                    const textElement = $("<p></p>").text(text);
                    const imgElement = $("<img>")
                        .attr("src", "data:" + mimeType + ";base64," + content)
                        .css("border", "1px solid black");
                    const message = $("<p></p>").append(textElement).append(imgElement);

                        message: message,