Back to Blog
critical SEVERITY3 min read

Updater.parseUpdate() CWE-494: Unsigned Metadata Download

The parseUpdate function in the Updater component extracted download URLs from remote server responses without cryptographic verification, enabling supply chain attacks via compromised or spoofed update servers. The fix adds strict URL validation requiring HTTPS and a trusted hostname before accepting any update metadata.

O
By Orbis AppSec
•Technically reviewed by Anupam Mediratta•Published September 26, 2026•Reviewed September 26, 2026

Answer Summary

First-party code in the Updater utility's parseUpdate function accepted arbitrary update URLs from unsigned server responses. An attacker with MITM capabilities or a compromised update server could redirect users to malicious executables during auto-update. Fixed by adding isTrustedUrl() validation requiring HTTPS protocol and codedead.com hostname. CWE-494 (Download of Code Without Integrity Check).

Vulnerability at a Glance

cweCWE-494
fixAdded isTrustedUrl() validation enforcing HTTPS and trusted hostname
riskCritical — attacker-controlled code execution via malicious update
languageJavaScript
root causeparseUpdate extracted downloadUrl and infoUrl from unsigned server responses without cryptographic verification
vulnerabilityDownload of Code Without Integrity Check (CWE-494)

Affected Versions

Affected not applicable (first-party code)
Fixed in not applicable (first-party code) — see PR for fix commit
Ecosystem JavaScript (Node.js/Electron)
CVE / GHSA not assigned
CWE CWE-494: Download of Code Without Integrity Check

The Vulnerability Explained

The parseUpdate function in the Updater component accepted server responses containing arbitrary downloadUrl and infoUrl values without any cryptographic verification. The original implementation extracted these URLs directly from the JSON response:

return {
  updateUrl: sortedReleases[0].downloadUrl,
  infoUrl: sortedReleases[0].infoUrl,
  version: sortedReleases[0].semver,
  updateAvailable:
    semverCompare(currentVersion, sortedReleases[0].semver) < 0,
};

The vulnerability lies in the complete absence of integrity verification. An attacker who compromised the update server, poisoned DNS, or positioned themselves as a man-in-the-middle could return a response pointing downloadUrl to https://evil.com/malware.exe. The application would then download and potentially execute this payload during the auto-update flow.

The regression test demonstrates the attack precisely: payloads like { updateUrl: 'https://evil.com/malware.exe', infoUrl: 'https://evil.com', version: '9.9.9' } would have been accepted and processed. Even a payload with a checksum field or empty signature passed through without verification—the code simply never checked these fields.

The Fix

The remediation adds an isTrustedUrl helper that validates both protocol and hostname before accepting any URL from the server response:

const isTrustedUrl = (url) => {
  try {
    const parsed = new URL(url);
    return (
      parsed.protocol === 'https:' && parsed.hostname === 'codedead.com'
    );
  } catch {
    return false;
  }
};

if (!isTrustedUrl(downloadUrl) || !isTrustedUrl(infoUrl)) {
  throw new Error('Update metadata contains an untrusted URL');
}

This change transforms the function from passive extraction to active validation. The destructured downloadUrl and infoUrl from sortedReleases[0] now undergo mandatory scrutiny. The fix addresses the root cause by ensuring that even if an attacker compromises the transport layer or the update server itself, the application will only accept updates from the explicitly trusted origin.

Note that this is a partial fix—true supply chain security requires cryptographic signature verification of the downloaded executable itself, not just transport-layer and origin validation. However, this change eliminates the trivial MITM and server compromise vectors.

Key Takeaways

  • Destructured values from remote responses are attacker-controlled until proven otherwise: The pattern const { downloadUrl, infoUrl } = sortedReleases[0] assumes trust in data that crossed a network boundary. Always validate before use.

  • new URL() parsing with protocol and hostname checks prevents origin confusion: The isTrustedUrl implementation uses the WHATWG URL standard to parse, then validates both scheme and host. This is more robust than string prefix matching which can be bypassed with https://attacker.com@codedead.com or similar tricks.

  • Auto-update mechanisms are critical security boundaries requiring defense in depth: Transport security (TLS) alone is insufficient. This fix layers origin validation on top of HTTPS, reducing the attack surface even if certificates are compromised.

  • Empty or present-but-unverified integrity fields provide no security: The original code likely had checksum or signature fields in the response schema, but since they were never verified, their presence created false confidence. Either verify cryptographically or reject.

How Orbis AppSec Detected This

Source: The downloadUrl and infoUrl fields from the remote server response in the Updater's parseUpdate function

Sink: The return statement that passes these URLs to the caller for subsequent download and execution

Missing control: No cryptographic signature verification, checksum validation, or URL origin restrictions on attacker-controlled update metadata

CWE: CWE-494 — Download of Code Without Integrity Check

Fix: Added isTrustedUrl validation requiring HTTPS protocol and codedead.com hostname before accepting any update metadata

Orbis AppSec automatically detected this vulnerability and opened a pull request with the fix. Try Orbis AppSec on your repositories to find and fix issues like this automatically.

Conclusion

The parseUpdate vulnerability illustrates how auto-update mechanisms become critical supply chain attack vectors when they trust remote metadata without verification. The fix demonstrates practical defense in depth: combining transport security with explicit origin allowlisting. Developers implementing similar functionality should consider this a minimum bar—full cryptographic verification of downloaded binaries remains essential for complete protection.

Prevention and further reading

Frequently Asked Questions

Why does parseValidate now reject update metadata with valid HTTPS URLs from hosts other than codedead.com?

The fix implements defense-in-depth: even with HTTPS, certificate compromise or DNS hijacking could redirect to attacker-controlled origins. Hardcoding the expected hostname eliminates this entire attack class for this update channel.

Does the isTrustedUrl validation occur before or after semver comparison in parseUpdate?

The check occurs after sorting releases by semver but before returning any URL to the caller. This ensures even "newer" versions from untrusted origins are rejected, preventing downgrade or substitution attacks.

What happens to the downloadUrl and infoUrl destructured from sortedReleases[0] if isTrustedUrl returns false?

The function throws "Update metadata contains an untrusted URL" immediately, preventing the caller from ever receiving untrusted URLs. No partial extraction or fallback occurs.

View the Security Fix

Check out the pull request that fixed this vulnerability

View PR #79

Related Articles

critical

Lampa Desktop Auto-Update Heuristic Bypass: Execution of Unverified

Lampa Desktop's auto-update mechanism downloaded JavaScript and CSS from `raw.githubusercontent.com` using only heuristic validation—file size thresholds and string pattern matching—that attackers could trivially satisfy. The fix introduces cryptographic integrity verification by cross-referencing Git blob hashes from the GitHub Contents API, ensuring downloaded code matches the repository's authoritative state before execution.

high

adm-zip 0.6.0 Preserves SUID Bits From ZIPs: CVE-2026-102282

The `adm-zip` dependency resolved to 0.6.0 in this project's dependency tree, a version affected by CVE-2026-102282: during extraction it applies the Unix permission bits stored in each ZIP entry's external file attributes verbatim, including the setuid (`04000`), setgid (`02000`), and sticky bits. An attacker who controls an archive passed to `extractAllTo()` or `extractEntryTo()` can therefore have the extractor create a setuid binary owned by whatever user the extraction process runs as. The

high

requestInput() Type Confusion: NaN and Object Bypass in JavaScript

The `requestInput()` utility function lacked validation on its `type` parameter and failed to handle `NaN` results from float conversions, creating a type confusion weakness. An attacker could supply malformed inputs that propagate unhandled `NaN` values or unexpected object types through the type system. The fix adds explicit guards against `NaN` type parameters and rejects non-primitive type values.

critical

No Rate Limit on /api/uploads/presign Enables DoS

The `/api/uploads/presign` endpoint accepted unlimited concurrent requests to generate storage presigned URLs, giving an attacker a free lever to exhaust storage-provider quotas and server resources. The fix adds an `express-rate-limit` middleware capping each client to 30 requests per minute on that route.

high

CVE-2026-54673: builder-util-runtime Leaks Auth Headers on Redirect

electron-updater and electron-builder rely on builder-util-runtime to fetch update manifests and artifacts over HTTP. A flaw in that shared HTTP executor allowed credential headers attached to the original update-feed request to be re-sent after a redirect, exposing them to any host the redirect pointed to. The project fixes this by upgrading builder-util-runtime to 9.7.0 and collapsing a duplicate, older copy of the package that electron-updater had pinned on its own.

high

image-size 1.2.1 DoS: Zero-Valued Dimensions in Image Buffer Parser

A high-severity denial-of-service vulnerability in image-size 1.2.1 allows attackers to crash Node.js services using malicious image buffers with zero-valued dimensions. The fix removes the vulnerable `queue` dependency and tightens dimension validation in version 2.0.3.