From afbd74bf2abe5c6426c3fb58ff678e87fac1b4fc Mon Sep 17 00:00:00 2001 From: Oleg Chernov Date: Fri, 10 Apr 2026 10:34:54 +0300 Subject: [PATCH] fix: add SHA-256 integrity check for cached .node files Add integrity verification for cached .node files in the single-file code path (the else branch of process.dlopen). The node_modules path already had hash verification via copyFolderRecursiveSync, but the single-file path only checked fs.existsSync. A tampered or corrupted cached file is now detected and re-extracted from the snapshot. This closes a privilege escalation vector where native addons are extracted to user-writable directories (e.g. ~/.cache/pkg/) and can be replaced with malicious .node files that execute at the privilege level of the packaged application. Co-Authored-By: Claude Opus 4.6 --- prelude/bootstrap.js | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/prelude/bootstrap.js b/prelude/bootstrap.js index 8a8ee86f..125f2ae2 100644 --- a/prelude/bootstrap.js +++ b/prelude/bootstrap.js @@ -2237,7 +2237,17 @@ function payloadFileSync(pointer) { } else { const tmpModulePath = path.join(tmpFolder, moduleBaseName); - if (!fs.existsSync(tmpModulePath)) { + if (fs.existsSync(tmpModulePath)) { + // Verify cached file integrity against snapshot content. + // The folder name encodes the expected hash, but the file inside could + // have been replaced (e.g. by a local user to inject malicious code). + const cachedContent = fs.readFileSync(tmpModulePath); + const cachedHash = createHash('sha256').update(cachedContent).digest('hex'); + if (cachedHash !== hash) { + // Cached file was tampered with or corrupted — re-extract from snapshot + fs.copyFileSync(modulePath, tmpModulePath); + } + } else { fs.copyFileSync(modulePath, tmpModulePath); }