harden code: error in case of bad zip on update
Massimo Melina committed
Dec 12, 2025 at 23:56 UTC
5d85bdaa68f6558035b96585d834ba1f99bab4bf
3 files changed
+11
-6
src/github.ts
+1
-1
@@ -139,7 +139,7 @@ export async function downloadPlugin(repo: Repo, { branch='', overwrite=false }=
139
if (wasRunning)
140
if (await waitFor(() => getPluginInfo(folder), { timeout: 10_000 }))
141
void startPlugin(folder) // don't wait, in case it fails to start. We still use startPlugin instead of enablePlugin, as it will take care of disabling other themes.
142
- .catch(console.warn)
142
+ .catch(e => console.warn(String(e)))
143
events.emit('pluginDownloaded', { id: folder, repo })
144
return folder
145
}
src/update.ts
+9
-4
@@ -7,7 +7,7 @@ import { spawn, spawnSync } from 'child_process'
7
import { DAY, exists, debounceAsync, unzip, prefix, xlate, HOUR, httpWithBody, statWithTimeout } from './misc'
8
import { createReadStream, existsSync, renameSync, unlinkSync, writeFileSync } from 'fs'
9
import { pluginsWatcher } from './plugins'
10
-import { chmod, rename, writeFile } from 'fs/promises'
10
+import { chmod, rename, writeFile, rm } from 'fs/promises'
11
import open from 'open'
12
import { currentVersion, defineConfig, versionToScalar } from './config'
13
import { cmdEscape, RUNNING_AS_SERVICE } from './util-os'
@@ -150,12 +150,17 @@ export async function update(tagOrUrl: string='') {
150
await unzip(createReadStream(LOCAL_UPDATE), path =>
151
join(binPath, path === binFile ? newBinFile : path))
152
const newBin = join(binPath, newBinFile)
153
+ if (!existsSync(newBin)) {
154
+ if (url) // the file was downloaded, and the UI would show the "update from local file" button until we remove it
155
+ await rm(LOCAL_UPDATE).catch(e => console.warn(String(e)))
156
+ throw "Missing executable in the archive"
157
+ }
158
if (!IS_WINDOWS) {
159
const { mode } = await statWithTimeout(bin)
160
await chmod(newBin, mode).catch(console.error)
161
}
162
await rename(INSTALLED_FN, PREVIOUS_FN).catch(e => e?.code !== 'ENOENT' && console.warn(String(e)))
158
- await rename(LOCAL_UPDATE, INSTALLED_FN).catch(console.warn)
163
+ await rename(LOCAL_UPDATE, INSTALLED_FN).catch(e => console.warn(String(e)))
164
onProcessExit(() => {
165
const oldBinFile = 'old-' + binFile
166
const oldBin = join(binPath, oldBinFile)
@@ -182,8 +187,8 @@ if (argv.updating) { // we were launched with a temporary name, restore original
187
const bin = process.execPath
188
const dest = join(dirname(bin), argv.updating)
189
renameSync(bin, dest)
185
- // have to relaunch with new name, or otherwise next update will fail with EBUSY on hfs.exe
186
- console.log("renamed binary file to", argv.updating, "and restarting")
190
+ // have to relaunch with the new name, or otherwise next update will fail with EBUSY on hfs.exe
191
+ console.log(`renamed binary file to "${argv.updating}" and now restarting`)
192
// be sure to test launching both double-clicking and in a terminal
193
if (IS_WINDOWS) // this method on Mac works only once, and without console
194
onProcessExit(() =>
src/upload.ts
+1
-1
@@ -186,7 +186,7 @@ export function uploadWriter(base: VfsNode, baseUri: string, path: string, ctx:
186
let dest = fullPath // final destination, considering numbering if necessary
187
if (dontOverwriteUploading.get() && !await overwriteAnyway() && fs.existsSync(dest)) {
188
if (overwriteRequestedButForbidden) {
189
- await rm(tempName).catch(console.warn)
189
+ await rm(tempName).catch(e => console.warn(String(e)))
190
releaseFile()
191
return fail() // status code set by overwriteAnyway
192
}