mirror of
https://github.com/4gray/iptvnator.git
synced 2026-10-13 04:26:16 -08:00
fix(packaging): validate Flatpak launcher ELF
This commit is contained in:
1 parent
52e248ebf5
commit
1a5ab50872
4 files changed
+165
-6
No files matched your search
@@ -308,6 +308,49 @@ test('package layout verifier uses canonical helpers and direct dependencies', (
|
||||
assert.match(packageLayoutVerifier, /dirArch !== 'x64'/);
|
||||
assert.doesNotMatch(packageLayoutVerifier, /getEmbeddedMpvAddonArch/);
|
||||
assert.match(electronAfterPackSource, /targetArch !== 'x64'/);
|
||||
assert.match(
|
||||
packageLayoutVerifier,
|
||||
/const\s*{\s*resolveLinuxLauncherLayout\s*}\s*=\s*require\(['"]\.\/linux-launcher-layout\.cjs['"]\)/
|
||||
);
|
||||
assert.match(
|
||||
packageLayoutVerifier,
|
||||
/function verifyLinuxLauncher\(\s*resourceDir,\s*targetNames,\s*errors\s*\)/
|
||||
);
|
||||
assert.match(
|
||||
packageLayoutVerifier,
|
||||
/resolveLinuxLauncherLayout\(\s*targetNames,\s*linuxExecutableName\s*\)/
|
||||
);
|
||||
assert.match(
|
||||
packageLayoutVerifier,
|
||||
/verifyLinuxLauncher\(\s*resourceDir,\s*linuxTargetNames,\s*errors\s*\)/
|
||||
);
|
||||
assert.match(packageLayoutVerifier, /const elfMagic = Buffer\.alloc\(4\)/);
|
||||
assert.match(
|
||||
packageLayoutVerifier,
|
||||
/fs\.openSync\(launcherPath,\s*['"]r['"]\)/
|
||||
);
|
||||
assert.match(
|
||||
packageLayoutVerifier,
|
||||
/fs\.readSync\(\s*descriptor,\s*elfMagic,\s*0,\s*elfMagic\.length,\s*0\s*\)/
|
||||
);
|
||||
assert.match(packageLayoutVerifier, /fs\.closeSync\(descriptor\)/);
|
||||
|
||||
const launcherVerifier = packageLayoutVerifier.match(
|
||||
/function verifyLinuxLauncher\([\s\S]*?\n}\n\nfunction verifyFlatpakPermissions/
|
||||
)?.[0];
|
||||
assert.ok(launcherVerifier);
|
||||
assert.ok(
|
||||
launcherVerifier.indexOf('resolveLinuxLauncherLayout(') <
|
||||
launcherVerifier.indexOf('const launcherBinaryPath')
|
||||
);
|
||||
assert.match(
|
||||
launcherVerifier,
|
||||
/if \(!launcherLayout\.wrapperRequired\) \{[\s\S]*?return;[\s\S]*?\}\s*const launcherBinaryPath[\s\S]*?fs\.readFileSync\(launcherPath,\s*['"]utf8['"]\)/
|
||||
);
|
||||
assert.match(
|
||||
launcherVerifier,
|
||||
/fs\.existsSync\(flatpakBinarySiblingPath\)/
|
||||
);
|
||||
});
|
||||
|
||||
test('nx-electron packaging does not copy duplicate root package metadata', () => {
|
||||
|
||||
@@ -279,6 +279,7 @@ function pureValidationOptions(options = {}) {
|
||||
function validElfInspector(nativeDir, manifest, overrides = {}) {
|
||||
const libDir = join(nativeDir, 'lib');
|
||||
const records = new Map([
|
||||
['iptvnator', { needed: ['libc.so.6'], rpath: [], runpath: [] }],
|
||||
['iptvnator.bin', { needed: ['libc.so.6'], rpath: [], runpath: [] }],
|
||||
[
|
||||
FRAME_COPY_ARTIFACTS.addon,
|
||||
@@ -598,6 +599,10 @@ test('prepares a normalized system profile with no private runtime', (t) => {
|
||||
test('prepares portable and Flatpak manifests with the exact bundled closure', (t) => {
|
||||
const portable = createNativeFixture();
|
||||
const flatpak = createNativeFixture();
|
||||
fs.renameSync(
|
||||
join(flatpak.appOutDir, 'iptvnator.bin'),
|
||||
join(flatpak.appOutDir, 'iptvnator')
|
||||
);
|
||||
t.after(() => {
|
||||
for (const fixture of [portable, flatpak]) {
|
||||
fs.rmSync(fixture.fixtureRoot, { recursive: true, force: true });
|
||||
@@ -699,6 +704,11 @@ test('prepares portable and Flatpak manifests with the exact bundled closure', (
|
||||
pureValidationOptions({
|
||||
profile: 'flatpak',
|
||||
targetNames: ['flatpak'],
|
||||
hostPlatform: 'linux',
|
||||
elfInspector: validElfInspector(
|
||||
flatpak.nativeDir,
|
||||
flatpakManifest
|
||||
),
|
||||
})
|
||||
),
|
||||
[]
|
||||
|
||||
@@ -25,6 +25,7 @@ const {
|
||||
resolveLinuxFrameCopyProfile,
|
||||
validateLinuxProfileTargets,
|
||||
} = require('./linux-frame-copy-profile.cjs');
|
||||
const { resolveLinuxLauncherLayout } = require('./linux-launcher-layout.cjs');
|
||||
|
||||
const forbiddenRuntimePathPrefixes = ['/opt/homebrew/', '/usr/local/'];
|
||||
const systemRuntimePathPrefixes = ['/System/Library/', '/usr/lib/'];
|
||||
@@ -1380,9 +1381,26 @@ function inspectLinuxElfIsolation(
|
||||
resourceDir,
|
||||
nativeDir,
|
||||
manifest,
|
||||
targetNames,
|
||||
options,
|
||||
errors
|
||||
) {
|
||||
const executableName = options.executableName ?? 'iptvnator';
|
||||
let launcherLayout;
|
||||
try {
|
||||
launcherLayout = resolveLinuxLauncherLayout(
|
||||
targetNames,
|
||||
executableName
|
||||
);
|
||||
} catch (error) {
|
||||
errors.push(
|
||||
`Unable to resolve Linux launcher layout: ${
|
||||
error instanceof Error ? error.message : String(error)
|
||||
}`
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
const hostPlatform = options.hostPlatform ?? process.platform;
|
||||
let inspectElf = options.elfInspector;
|
||||
if (!inspectElf) {
|
||||
@@ -1403,9 +1421,11 @@ function inspectLinuxElfIsolation(
|
||||
return;
|
||||
}
|
||||
|
||||
const executableName = options.executableName ?? 'iptvnator';
|
||||
const inspectedPaths = {
|
||||
electron: path.join(path.dirname(resourceDir), `${executableName}.bin`),
|
||||
electron: path.join(
|
||||
path.dirname(resourceDir),
|
||||
launcherLayout.electronBinaryName
|
||||
),
|
||||
addon: path.join(nativeDir, linuxFrameCopyArtifacts.addon.name),
|
||||
reader: path.join(nativeDir, linuxFrameCopyArtifacts.frameReader.name),
|
||||
helper: path.join(nativeDir, linuxFrameCopyArtifacts.helper.name),
|
||||
@@ -1770,7 +1790,14 @@ function validateLinuxPackagedEmbeddedMpv(resourceDir, options) {
|
||||
} else {
|
||||
validateBundledLinuxRuntime(nativeDir, manifest, errors);
|
||||
}
|
||||
inspectLinuxElfIsolation(resourceDir, nativeDir, manifest, options, errors);
|
||||
inspectLinuxElfIsolation(
|
||||
resourceDir,
|
||||
nativeDir,
|
||||
manifest,
|
||||
targetNames,
|
||||
options,
|
||||
errors
|
||||
);
|
||||
return errors;
|
||||
}
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ const {
|
||||
const {
|
||||
validateLinuxProfileTargets,
|
||||
} = require('./linux-frame-copy-profile.cjs');
|
||||
const { resolveLinuxLauncherLayout } = require('./linux-launcher-layout.cjs');
|
||||
const args = process.argv.slice(2);
|
||||
const normalizedArgs = args[0] === '--' ? args.slice(1) : args;
|
||||
const [platform, arch = ''] = normalizedArgs;
|
||||
@@ -468,10 +469,88 @@ function verifyPackagedPackageMetadata(resourceDir, errors) {
|
||||
}
|
||||
}
|
||||
|
||||
function verifyLinuxLauncher(resourceDir, errors) {
|
||||
function verifyLinuxLauncher(resourceDir, targetNames, errors) {
|
||||
let launcherLayout;
|
||||
try {
|
||||
launcherLayout = resolveLinuxLauncherLayout(
|
||||
targetNames,
|
||||
linuxExecutableName
|
||||
);
|
||||
} catch (error) {
|
||||
errors.push(
|
||||
`Unable to resolve Linux launcher layout: ${
|
||||
error instanceof Error ? error.message : String(error)
|
||||
}`
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
const appDir = path.dirname(resourceDir);
|
||||
const launcherPath = path.join(appDir, linuxExecutableName);
|
||||
const launcherBinaryPath = `${launcherPath}.bin`;
|
||||
|
||||
if (!launcherLayout.wrapperRequired) {
|
||||
if (!fileExists(launcherPath)) {
|
||||
errors.push(
|
||||
`Missing Flatpak Electron ELF in ${appDir}: ${path.basename(launcherPath)}`
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
const flatpakBinarySiblingPath = `${launcherPath}.bin`;
|
||||
if (fs.existsSync(flatpakBinarySiblingPath)) {
|
||||
errors.push(
|
||||
`Flatpak Electron layout must not include a launcher binary sibling: ${flatpakBinarySiblingPath}`
|
||||
);
|
||||
}
|
||||
|
||||
const expectedElfMagic = Buffer.from([0x7f, 0x45, 0x4c, 0x46]);
|
||||
const elfMagic = Buffer.alloc(4);
|
||||
let descriptor;
|
||||
try {
|
||||
descriptor = fs.openSync(launcherPath, 'r');
|
||||
const bytesRead = fs.readSync(
|
||||
descriptor,
|
||||
elfMagic,
|
||||
0,
|
||||
elfMagic.length,
|
||||
0
|
||||
);
|
||||
if (
|
||||
bytesRead !== expectedElfMagic.length ||
|
||||
!elfMagic.equals(expectedElfMagic)
|
||||
) {
|
||||
errors.push(
|
||||
`Flatpak Electron launcher must be an ELF binary: ${launcherPath}`
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
errors.push(
|
||||
`Unable to inspect Flatpak Electron ELF at ${launcherPath}: ${
|
||||
error instanceof Error ? error.message : String(error)
|
||||
}`
|
||||
);
|
||||
} finally {
|
||||
if (descriptor !== undefined) {
|
||||
try {
|
||||
fs.closeSync(descriptor);
|
||||
} catch (error) {
|
||||
errors.push(
|
||||
`Unable to close Flatpak Electron ELF at ${launcherPath}: ${
|
||||
error instanceof Error
|
||||
? error.message
|
||||
: String(error)
|
||||
}`
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
const launcherBinaryPath = path.join(
|
||||
appDir,
|
||||
launcherLayout.electronBinaryName
|
||||
);
|
||||
|
||||
if (!fileExists(launcherBinaryPath)) {
|
||||
errors.push(
|
||||
@@ -733,7 +812,7 @@ function verifyResourceDir(resourceDir) {
|
||||
verifyLinuxExecutableArgs(errors);
|
||||
verifyFlatpakPermissions(errors);
|
||||
verifySnapPackagingConfig(errors);
|
||||
verifyLinuxLauncher(resourceDir, errors);
|
||||
verifyLinuxLauncher(resourceDir, linuxTargetNames, errors);
|
||||
}
|
||||
|
||||
errors.push(
|
||||
|
||||
Reference in new issue
Block a user