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, andrequire. 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.repoandcfg.branchparameters 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.