Back to Blog
critical SEVERITY4 min read

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.

O
By Orbis AppSec
•Technically reviewed by Anupam Mediratta•Published October 4, 2026•Reviewed October 4, 2026

Answer Summary

The `checkAndUpdate()` function in Lampa Desktop's auto-update module fetches `app.min.js` and CSS from `raw.githubusercontent.com` URLs without cryptographic verification. An attacker with MITM capabilities or a compromised CDN could substitute malicious JavaScript that satisfies the heuristic checks (file size >500KB, starts with `(function`, contains `app_version:`), achieving arbitrary code execution with full Electron renderer privileges on the next application launch. The fix adds `verifyIntegrity()` which computes SHA-1 git blob hashes of downloaded content and validates them against the GitHub Contents API before writing files to disk. CWE-494: Download of Code Without Integrity Check.

Vulnerability at a Glance

cweCWE-494
fixGit blob hash verification against GitHub Contents API before disk write and execution
riskCritical — arbitrary code execution in Electron renderer context
languageJavaScript (Node.js/Electron)
root cause`checkAndUpdate()` trusted heuristics over cryptography for code provenance
vulnerabilityDownload of Code Without Integrity Check

Affected Versions

Affected unknown (first-party code)
Fixed in unknown (see fix commit)
Ecosystem N/A (Electron application, first-party code)
CVE / GHSA not assigned
CWE CWE-494: Download of Code Without Integrity Check

The Vulnerability Explained

Lampa Desktop's auto-update mechanism in checkAndUpdate() fetched JavaScript and CSS updates from raw.githubusercontent.com URLs constructed from user-configurable repository settings. The validation logic relied entirely on heuristics that sound plausible but collapse under adversarial scrutiny:

// Vulnerable pattern: heuristic validation only
if (js.length > 500000 &&                    // File size >500KB
    js.trimStart().startsWith('(function') && // Starts with IIFE wrapper
    js.includes("app_version:")) {            // Contains version string
    // Write to disk and execute on next launch
}

These checks are trivially forgeable. An attacker in a MITM position or controlling a compromised CDN edge node could serve:

(function(){
// 500KB+ of padding
/* ... 500,001 bytes of 'x' characters ... */
app_version: '1.0.0'
// Malicious payload here
const { exec } = require('child_process');
exec('malicious-command');
})();

The code satisfies all three heuristics—size threshold exceeded, correct wrapper prefix, version string present—while containing arbitrary malicious logic. Once written to disk and loaded on the next Electron launch, this code executes with full renderer process privileges, enabling system compromise, credential theft, or persistent access.

The vulnerability was particularly insidious because the cfg object—containing repo, branch, and derived rawBase URL—could be influenced through the application's configuration store, potentially allowing attackers to redirect updates to attacker-controlled repositories if configuration integrity was also compromised.

The Fix

The fix introduces verifyIntegrity(), a cryptographic verification function that establishes an independent trust anchor:

const verifyIntegrity = async (cfg, filePath, content) => {
  const api = `https://api.github.com/repos/${cfg.repo}/contents/${filePath}?ref=${cfg.branch}`;
  const res = await fetch(api, { headers: { 'User-Agent': 'lampa-desktop', Accept: 'application/vnd.github+json' } });
  if (!res.ok) throw new Error(`integrity check HTTP ${res.status} for ${filePath}`);
  const meta = await res.json();
  const expected = String(meta.sha || '');
  const actual = crypto.createHash('sha1')
    .update(`blob ${Buffer.byteLength(content, 'utf-8')}\0`)
    .update(Buffer.from(content, 'utf-8'))
    .digest('hex');
  if (!expected || expected !== actual) throw new Error(`integrity mismatch for ${filePath}`);
};

This function implements Git's blob hashing algorithm—blob ${size}\0${content}—to compute a hash comparable to the sha field returned by GitHub's Contents API. The verification creates a split-horizon security model: even if raw.githubusercontent.com is compromised, the attacker cannot predict or forge the corresponding API response without also compromising api.github.com infrastructure.

The checkAndUpdate() function now gates all file writes through this verification:

// Before writing to disk:
try {
  await Promise.all([
    verifyIntegrity(cfg, 'app.min.js', js),
    verifyIntegrity(cfg, 'css/app.css', css)
  ]);
} catch (error) {
  log.warn('[lampa-core] integrity verification failed:', error.message);
  return { status: 'error' };
}
// Only then: write verified content to disk

The Promise.all parallelization maintains update speed while ensuring both artifacts are verified before either is persisted, preventing partial update attacks.

Key Takeaways

  • Heuristic validation is not integrity verification: Size checks, magic bytes, and string matching can always be satisfied by attackers who understand the validation logic. The original js.length > 500000 && js.includes("app_version:") pattern offered no cryptographic assurance.

  • Electron renderer code execution equals system compromise: The renderer process has access to Node.js APIs including fs, child_process, and require. Downloaded code execution must be treated with the same rigor as native binary updates.

  • Split-horizon verification raises attack cost: By verifying against an independent API endpoint with different infrastructure, attackers must compromise multiple systems rather than a single CDN edge.

  • Git blob hashes enable practical verification: Rather than maintaining a separate signature infrastructure, the fix leverages Git's native content-addressable storage model, using SHA-1 blob hashes already exposed by GitHub's API.

  • Configuration-driven update URLs require additional hardening: The cfg.repo and cfg.branch parameters that construct update URLs should be integrity-protected themselves, or bounded to authorized values, to prevent redirection attacks.

How Orbis AppSec Detected This

Source: The cfg configuration object containing repo, branch, and derived rawBase URL used to construct raw.githubusercontent.com download URLs.

Sink: The fs.writeFile() operations (implied by "written to disk and executed") following successful heuristic validation in checkAndUpdate().

Missing control: No cryptographic signature verification, hash comparison, or content-addressable integrity check before persisting downloaded code for execution.

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

Fix: Added verifyIntegrity() function computing Git blob SHA-1 hashes and validating against GitHub Contents API responses before disk write operations.

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

This vulnerability demonstrates how "reasonable" validation—size thresholds, structural patterns, version strings—creates a false sense of security in auto-update mechanisms. The Lampa Desktop fix replaces heuristic trust with cryptographic verification, using Git's native content-addressing to establish code provenance without introducing new key management infrastructure. For Electron applications and other desktop frameworks with automatic update capabilities, this pattern of split-horizon verification should be considered a baseline security requirement.

Prevention and further reading

Frequently Asked Questions

Why does the fix use SHA-1 git blob hashes instead of SHA-256 or another modern hash?

The GitHub Contents API exposes `sha` fields containing Git's native blob hash format. The `verifyIntegrity()` function replicates Git's blob hashing—`blob ${size}\0${content}`—to enable direct comparison with the API response, creating an independent verification channel separate from the raw content download path.

Could an attacker bypass the new integrity check by compromising both `raw.githubusercontent.com` and `api.github.com`?

Yes, which is why defense-in-depth matters. The fix creates a *split-horizon* verification: the raw content CDN and the API endpoint are separate infrastructure paths. An attacker must compromise both to forge code, significantly raising the attack cost compared to the original single-point-of-failure design.

Does the heuristic validation (file size >500KB, function wrapper) remain in the fixed code?

The PR description indicates these checks were the *only* validation before. The fix replaces this trust model entirely—the `verifyIntegrity()` function now gates all execution paths, and the git blob hash verification is authoritative. The heuristics may persist as preliminary filters, but they no longer constitute security boundaries.

View the Security Fix

Check out the pull request that fixed this vulnerability

View PR #5

Related Articles

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.

critical

Discord Bot Auto-Role CWE-269: Privilege Escalation via role.position

A Discord.js bot's auto-role feature allowed privilege escalation where any user with ManageRoles permission could add Administrator roles to automatic assignment lists, regardless of their own role hierarchy. The vulnerability existed because role.position validation checked only the bot's permissions, not the invoking user's role hierarchy relative to the target role.