CVE-2026-63640 Overview
CVE-2026-63640 is an information disclosure vulnerability [CWE-200] in MagicMirror², an open source modular smart mirror platform. Versions prior to 2.37.0 are affected when the hideConfigSecrets option is enabled. The catch-all socket dispatcher in js/node_helper.js passes every inbound object payload through replaceSecretPlaceholder in js/server_functions.js before invoking socketNotificationReceived. An adjacent-network client connected to a loaded module namespace can submit a SECRET_API_KEY placeholder and receive the expanded value back through a reflected error path. This reverses the intended one-way redaction boundary that protects SECRET_ environment variables.
Critical Impact
Attackers on the adjacent network can extract API tokens, credentials, and service keys stored in SECRET_ environment variables by abusing the default weather helper's echo path.
Affected Products
- MagicMirror² versions prior to 2.37.0
- Default weather module (INIT_WEATHER / WEATHER_ERROR handlers)
- Any module invoking socketNotificationReceived with hideConfigSecrets enabled
Discovery Timeline
- 2026-08-18 - CVE-2026-63640 published to NVD
- 2026-08-18 - Last updated in NVD database
Technical Details for CVE-2026-63640
Vulnerability Analysis
MagicMirror² uses a socket-based communication channel between the browser client and Node.js helper modules. When hideConfigSecrets is enabled, the server redacts SECRET_ values from the configuration sent to the client. However, the catch-all dispatcher in js/node_helper.js applies replaceSecretPlaceholder to inbound payloads before passing them to module handlers.
This means any client that can reach the socket namespace of a loaded module can submit a payload containing a **SECRET_API_KEY** placeholder. The server expands the placeholder using the corresponding process.env value before the module processes the payload. The redaction boundary that was intended to flow only server-to-client is now traversable in reverse.
Root Cause
The root cause is unconditional secret expansion on inbound socket payloads without verifying which secrets the requesting module is authorized to restore. The replaceSecretPlaceholder function trusts every field of the incoming object, treating client-controlled placeholders identically to server-side configuration references.
Attack Vector
The default weather helper accepts an INIT_WEATHER notification, copies the attacker-controlled instanceId field into its response, and returns it in a WEATHER_ERROR notification. Because the instanceId is passed through replaceSecretPlaceholder before the helper reads it, the expanded secret is echoed back to the client. An attacker submits a placeholder such as **SECRET_API_KEY** as the instanceId and receives the plaintext value in the error reflection.
// Security patch in js/node_helper.js — restricts secret expansion to placeholders
// that appear in the module's own redacted config.
const Log = require("logger");
const { replaceSecretPlaceholder } = require("#server_functions");
/**
* Determine which secrets a module is allowed to restore. A module may only
* restore the `**SECRET_***` placeholders that appear in its own config — the
* exact inverse of how the config is redacted before it is sent to the browser.
* @param {string} moduleName - Name of the module.
* @returns {Set<string>} The secret names the module may restore.
*/
function getAllowedSecrets (moduleName) {
const modules = global.configRedacted?.modules || [];
const moduleConfig = modules.find((m) => m.module === moduleName);
const allowed = new Set();
if (moduleConfig) {
// Stringify the config to easily find all expected **SECRET_*** placeholders
for (const [, secretName] of JSON.stringify(moduleConfig).matchAll(/\*\*(SECRET_[^*]+)\*\*/g)) {
allowed.add(secretName);
}
}
return allowed;
}
Source: GitHub Commit ca7b752
Detection Methods for CVE-2026-63640
Indicators of Compromise
- Inbound socket payloads containing **SECRET_* placeholder strings originating from client connections
- WEATHER_ERROR notifications carrying values that match known SECRET_ environment variable content
- Unexpected socket clients connected to MagicMirror² module namespaces from adjacent network hosts
Detection Strategies
- Inspect the MagicMirror² Node.js process logs for INIT_WEATHER notifications with instanceId values matching the pattern **SECRET_[A-Z0-9_]+**
- Monitor Socket.IO traffic between browser clients and the MagicMirror² server for placeholder strings in inbound object fields
- Audit third-party module handlers that reflect any portion of an inbound payload back to the client
Monitoring Recommendations
- Alert on outbound socket messages containing high-entropy strings that resemble API keys or bearer tokens
- Log the source IP of every Socket.IO connection to the MagicMirror² instance and flag non-loopback origins
- Review SECRET_ environment variable usage and rotate any credentials suspected to have been exposed
How to Mitigate CVE-2026-63640
Immediate Actions Required
- Upgrade MagicMirror² to version 2.37.0 or later, which introduces per-module secret authorization
- Restrict network exposure of the MagicMirror² server to trusted loopback or isolated VLAN interfaces
- Rotate any API tokens, credentials, or service keys previously stored in SECRET_ environment variables
Patch Information
The fix landed in MagicMirror² Release v2.37.0 via Pull Request #4184. The patch introduces a configRedacted global and a getAllowedSecrets function so that inbound placeholders can only be expanded when the target module's own config declares that secret. Full technical details are documented in GitHub Security Advisory GHSA-q4gh-4ffp-5cg8.
Workarounds
- Disable hideConfigSecrets and remove SECRET_ placeholders from module configs until the upgrade is applied
- Bind the MagicMirror² server to 127.0.0.1 and require an SSH tunnel or reverse proxy for remote access
- Remove or disable the default weather module and any third-party modules that reflect inbound payload fields
# Upgrade MagicMirror² to the patched release
cd ~/MagicMirror
git fetch --tags
git checkout v2.37.0
npm install --only=prod --omit=dev
# Restrict server binding to loopback in config/config.js
# address: "127.0.0.1",
# ipWhitelist: ["127.0.0.1", "::ffff:127.0.0.1", "::1"],
Disclaimer: This content was generated using AI. While we strive for accuracy, please verify critical information with official sources.

