Skip to content

Instantly share code, notes, and snippets.

@0xdevalias
Created August 13, 2026 10:43
Show Gist options
  • Select an option

  • Save 0xdevalias/b28e27ea290913bbe4510eeaad7edaf3 to your computer and use it in GitHub Desktop.

Select an option

Save 0xdevalias/b28e27ea290913bbe4510eeaad7edaf3 to your computer and use it in GitHub Desktop.
Electron ASAR CopyFileOut stale temporary-path cache reproduction
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();
});
{
"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"
}
}
@0xdevalias

Copy link
Copy Markdown
Author

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment