Created
August 13, 2026 10:43
-
-
Save 0xdevalias/b28e27ea290913bbe4510eeaad7edaf3 to your computer and use it in GitHub Desktop.
Electron ASAR CopyFileOut stale temporary-path cache reproduction
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| const { app } = require('electron'); | |
| const asar = require('@electron/asar'); | |
| const fs = require('node:fs'); | |
| const os = require('node:os'); | |
| const path = require('node:path'); | |
| const marker = 'electron-asar-stale-cache-repro-7e8fa4f9\n'; | |
| function readStream(file) { | |
| return new Promise((resolve, reject) => { | |
| const chunks = []; | |
| fs.createReadStream(file) | |
| .on('data', (chunk) => chunks.push(chunk)) | |
| .once('error', reject) | |
| .once('end', () => resolve(Buffer.concat(chunks).toString())); | |
| }); | |
| } | |
| function findExtractedCopy() { | |
| for (const name of fs.readdirSync(os.tmpdir())) { | |
| const candidate = path.join(os.tmpdir(), name); | |
| try { | |
| if (fs.statSync(candidate).isFile() && fs.readFileSync(candidate, 'utf8') === marker) { | |
| return candidate; | |
| } | |
| } catch { | |
| // Ignore unrelated temporary entries that disappear or cannot be read. | |
| } | |
| } | |
| return null; | |
| } | |
| app.whenReady().then(async () => { | |
| const fixtureDirectory = path.join(__dirname, 'fixture'); | |
| const archive = path.join(__dirname, 'fixture.asar'); | |
| const packedFile = path.join(archive, 'payload.txt'); | |
| fs.mkdirSync(fixtureDirectory, { recursive: true }); | |
| fs.writeFileSync(path.join(fixtureDirectory, 'payload.txt'), marker); | |
| await asar.createPackage(fixtureDirectory, archive); | |
| const first = await readStream(packedFile); | |
| if (first !== marker) throw new Error('First ASAR stream returned unexpected data'); | |
| const extracted = findExtractedCopy(); | |
| if (!extracted) throw new Error('Could not locate the extracted temporary copy'); | |
| console.log(`Extracted copy: ${extracted}`); | |
| fs.unlinkSync(extracted); | |
| console.log('Deleted the extracted copy while Electron remains running.'); | |
| // Buffer reads still work because they read packed bytes from the archive. | |
| console.log(`readFileSync after deletion: ${JSON.stringify(fs.readFileSync(packedFile, 'utf8'))}`); | |
| try { | |
| await readStream(packedFile); | |
| console.error('UNEXPECTED: second createReadStream succeeded'); | |
| process.exitCode = 2; | |
| } catch (error) { | |
| console.log(`REPRODUCED: ${error.code} opening cached path ${error.path}`); | |
| process.exitCode = error.code === 'ENOENT' && error.path === extracted ? 0 : 3; | |
| } finally { | |
| app.quit(); | |
| } | |
| }).catch((error) => { | |
| console.error(error); | |
| process.exitCode = 1; | |
| app.quit(); | |
| }); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| { | |
| "name": "electron-asar-stale-copyfileout-cache-repro", | |
| "version": "1.0.0", | |
| "private": true, | |
| "main": "main.js", | |
| "scripts": { | |
| "start": "electron ." | |
| }, | |
| "devDependencies": { | |
| "@electron/asar": "latest", | |
| "electron": "43.4.0" | |
| } | |
| } |
Author
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
See: