From 4a3fece22b4e9cd407bf298f727ff2f43eddb4e9 Mon Sep 17 00:00:00 2001 From: NimBold Date: Wed, 15 Jul 2026 08:40:12 +0330 Subject: [PATCH] fix(release): harden package and engine verifier cleanup --- scripts/verify-binaries.js | 58 +++++++++++++++++++--- scripts/verify-linux-packages.js | 12 ++++- scripts/verify-linux-packages.node-test.js | 15 +++++- 3 files changed, 75 insertions(+), 10 deletions(-) diff --git a/scripts/verify-binaries.js b/scripts/verify-binaries.js index a540e07..bf7c612 100644 --- a/scripts/verify-binaries.js +++ b/scripts/verify-binaries.js @@ -317,6 +317,54 @@ function runEngine(label, engine, args, timeout = 30000) { } } +function waitForProcessExit(proc, timeoutMs) { + if (proc.exitCode !== null || proc.signalCode !== null) { + return Promise.resolve(true); + } + + return new Promise(resolve => { + let settled = false; + let timer; + const finish = value => { + if (settled) return; + settled = true; + clearTimeout(timer); + proc.removeListener('exit', onExit); + resolve(value); + }; + const onExit = () => finish(true); + + timer = setTimeout(() => finish(false), timeoutMs); + proc.once('exit', onExit); + if (proc.exitCode !== null || proc.signalCode !== null) { + finish(true); + } + }); +} + +async function terminateProcess(proc, label) { + if (proc.exitCode === null && proc.signalCode === null) { + try { + proc.kill('SIGTERM'); + } catch {} + if (await waitForProcessExit(proc, 2000)) { + return true; + } + + try { + proc.kill('SIGKILL'); + } catch {} + if (await waitForProcessExit(proc, 2000)) { + return true; + } + + fail(`${label} did not terminate after SIGTERM and SIGKILL.`); + return false; + } + + return true; +} + const coldStartTimeout = isMacOS ? 120000 : 30000; if (canExecuteTarget) { runEngine('yt-dlp cold start', 'yt-dlp', ['--version'], coldStartTimeout); @@ -397,13 +445,9 @@ if (canExecuteTarget) { tryFetch(); }); - // Clean up - proc.kill('SIGTERM'); - setTimeout(() => { - try { - proc.kill('SIGKILL'); - } catch {} - }, 2000); + // Clean up and wait before the verifier exits so a stuck daemon cannot be + // left behind on a runner and contaminate later package or smoke checks. + await terminateProcess(proc, 'aria2 RPC verifier process'); if (result.ok) { try { diff --git a/scripts/verify-linux-packages.js b/scripts/verify-linux-packages.js index 4d5682d..e8c9add 100644 --- a/scripts/verify-linux-packages.js +++ b/scripts/verify-linux-packages.js @@ -96,6 +96,15 @@ export function parseDebianPackagePath(line) { return match[1].replace(/^\.\//, ''); } +export function isSafePackagePath(packagePath) { + if (packagePath === '') { + return true; + } + + const parts = packagePath.split('/'); + return !parts.includes('..') && (parts[0] === 'usr' || packagePath === 'usr'); +} + function assertSafePackageListing(listing, packageType) { const lines = listing.split('\n').filter(Boolean); const paths = packageType === 'deb' @@ -109,8 +118,7 @@ function assertSafePackageListing(listing, packageType) { : lines.map(line => line.replace(/^\/+/, '')); for (const packagePath of paths) { - const parts = packagePath.split('/'); - if (parts.includes('..') || (parts[0] !== 'usr' && packagePath !== 'usr')) { + if (!isSafePackagePath(packagePath)) { fail(`${packageType} package contains an unsafe path: ${packagePath}`); } } diff --git a/scripts/verify-linux-packages.node-test.js b/scripts/verify-linux-packages.node-test.js index 3507132..40234a9 100644 --- a/scripts/verify-linux-packages.node-test.js +++ b/scripts/verify-linux-packages.node-test.js @@ -1,7 +1,7 @@ import assert from 'node:assert/strict'; import test from 'node:test'; -import { parseDebianPackagePath } from './verify-linux-packages.js'; +import { isSafePackagePath, parseDebianPackagePath } from './verify-linux-packages.js'; test('parses current dpkg-deb listings without a ./ prefix', () => { assert.equal( @@ -17,6 +17,19 @@ test('parses legacy dpkg-deb listings with a ./ prefix', () => { ); }); +test('accepts the package root in legacy dpkg-deb listings', () => { + assert.equal( + parseDebianPackagePath('drwxr-xr-x root/root 0 2026-07-12 07:24 ./'), + '' + ); + assert.equal(isSafePackagePath(''), true); +}); + +test('rejects paths outside the package usr tree', () => { + assert.equal(isSafePackagePath('../tmp/firelink'), false); + assert.equal(isSafePackagePath('etc/firelink'), false); +}); + test('rejects malformed dpkg-deb listing lines', () => { assert.throws( () => parseDebianPackagePath('not a dpkg-deb listing'),