const { test } = require('node:test'); const assert = require('node:assert/strict'); const { spawnSync } = require('node:child_process'); const fs = require('node:fs'); const os = require('node:os'); const path = require('node:path'); const ROOT = path.join(__dirname, '..'); function copyTree(source, destination) { fs.cpSync(source, destination, { recursive: true }); } function buildPackage(umask) { const root = fs.mkdtempSync(path.join(os.tmpdir(), 'stoke-build-deb-test-')); const bin = path.join(root, 'bin'); fs.mkdirSync(path.join(root, 'scripts')); fs.mkdirSync(bin); fs.copyFileSync(path.join(ROOT, 'scripts', 'build-deb.sh'), path.join(root, 'scripts', 'build-deb.sh')); copyTree(path.join(ROOT, 'src'), path.join(root, 'src')); fs.copyFileSync(path.join(ROOT, 'package.json'), path.join(root, 'package.json')); fs.copyFileSync(path.join(ROOT, 'package-lock.json'), path.join(root, 'package-lock.json')); const npm = path.join(bin, 'npm'); fs.writeFileSync(npm, '#!/usr/bin/env bash\nexit 0\n'); fs.chmodSync(npm, 0o755); const result = spawnSync( 'bash', ['-c', 'umask "$1"; exec bash "$2"', 'build-deb-test', umask, path.join(root, 'scripts', 'build-deb.sh')], { encoding: 'utf8', env: { ...process.env, PATH: `${bin}:${process.env.PATH}` }, }, ); assert.equal(result.status, 0, result.stderr); const deb = path.join(root, 'dist', 'stoke_1.5.0_all.deb'); const listing = spawnSync('dpkg-deb', ['-c', deb], { encoding: 'utf8' }); assert.equal(listing.status, 0, listing.stderr); const modes = new Map(); for (const line of listing.stdout.trim().split('\n')) { const fields = line.trim().split(/\s+/); const archivePath = fields.find((field) => field.startsWith('./usr/')); if (archivePath && (fields[0].startsWith('d') || fields[0].startsWith('-'))) { modes.set(archivePath, fields[0]); } } return { root, modes }; } test('Debian payload modes are identical under umask 077 and 022', (t) => { const restrictive = buildPackage('077'); const standard = buildPackage('022'); t.after(() => { fs.rmSync(restrictive.root, { recursive: true, force: true }); fs.rmSync(standard.root, { recursive: true, force: true }); }); assert.deepEqual(restrictive.modes, standard.modes); for (const [archivePath, mode] of restrictive.modes) { assert.equal(mode, archivePath.endsWith('/') ? 'drwxr-xr-x' : archivePath === './usr/lib/stoke/src/cli.js' ? '-rwxr-xr-x' : '-rw-r--r--', archivePath); } });