From 064a72d2562c9e5db34d45fe0fdd6132f418a887 Mon Sep 17 00:00:00 2001 From: 4gray Date: Fri, 17 Jul 2026 18:25:18 +0200 Subject: [PATCH] fix(embedded-mpv): require Linux source packages --- tools/embedded-mpv/linux-runtime-manifest.cjs | 28 ++++++++++++++-- .../linux-runtime-manifest.test.mjs | 32 +++++++++++++++++++ 2 files changed, 58 insertions(+), 2 deletions(-) diff --git a/tools/embedded-mpv/linux-runtime-manifest.cjs b/tools/embedded-mpv/linux-runtime-manifest.cjs index 44218fd13..9fe55f2d2 100644 --- a/tools/embedded-mpv/linux-runtime-manifest.cjs +++ b/tools/embedded-mpv/linux-runtime-manifest.cjs @@ -8,6 +8,7 @@ const GIT_COMMIT_PATTERN = /^[a-f0-9]{40,64}$/; const SAFE_BASENAME_PATTERN = /^[A-Za-z0-9_+.-]+$/; const SHARED_LIBRARY_PATTERN = /\.so(?:\.\d+)*$/; const VERSIONED_LIBMPV_PATTERN = /^libmpv\.so\.\d+(?:\.\d+)*$/; +const REQUIRED_SOURCE_PACKAGES = ['ffmpeg', 'mpv']; function isObject(value) { return value !== null && typeof value === 'object' && !Array.isArray(value); @@ -70,18 +71,41 @@ function validateSourceMetadata(errors, value, label) { } function validatePackages(errors, packages) { - if (!isObject(packages) || Object.keys(packages).length === 0) { + if (!isObject(packages)) { errors.push( 'Linux runtime manifest packages must contain source package metadata.' ); return; } + if (Object.keys(packages).length === 0) { + errors.push( + 'Linux runtime manifest packages must contain source package metadata.' + ); + } + + const invalidRequiredPackages = new Set(); + for (const packageName of REQUIRED_SOURCE_PACKAGES) { + if ( + !Object.prototype.hasOwnProperty.call(packages, packageName) || + !isObject(packages[packageName]) + ) { + errors.push( + `Linux runtime manifest packages.${packageName} must be an object.` + ); + invalidRequiredPackages.add(packageName); + } + } + for (const packageName of Object.keys(packages).sort()) { const packageMetadata = packages[packageName]; const label = `packages.${packageName}`; if (!isObject(packageMetadata)) { - errors.push(`Linux runtime manifest ${label} must be an object.`); + if (!invalidRequiredPackages.has(packageName)) { + errors.push( + `Linux runtime manifest ${label} must be an object.` + ); + } continue; } diff --git a/tools/embedded-mpv/linux-runtime-manifest.test.mjs b/tools/embedded-mpv/linux-runtime-manifest.test.mjs index 0879ef841..8b15f0346 100644 --- a/tools/embedded-mpv/linux-runtime-manifest.test.mjs +++ b/tools/embedded-mpv/linux-runtime-manifest.test.mjs @@ -204,6 +204,38 @@ test('requires schema, provenance, target, package, and source metadata', () => ); }); +test('requires exact FFmpeg and mpv source package records', () => { + const missingFfmpeg = createValidManifest(); + missingFfmpeg.packages.libavcodec = missingFfmpeg.packages.ffmpeg; + delete missingFfmpeg.packages.ffmpeg; + assert.match( + validateLinuxRuntimeManifest(missingFfmpeg).join('\n'), + /packages\.ffmpeg must be an object/ + ); + + const missingMpv = createValidManifest(); + missingMpv.packages.libmpv = missingMpv.packages.mpv; + delete missingMpv.packages.mpv; + assert.match( + validateLinuxRuntimeManifest(missingMpv).join('\n'), + /packages\.mpv must be an object/ + ); + + const substitutedPackages = createValidManifest(); + substitutedPackages.packages = { + multimediaRuntime: { + version: '1.0.0', + sourceUrl: 'https://example.test/multimedia-runtime.tar.xz', + sourceSha256: 'c'.repeat(64), + license: 'LGPL-2.1-or-later', + }, + }; + assert.deepEqual(validateLinuxRuntimeManifest(substitutedPackages), [ + 'Linux runtime manifest packages.ffmpeg must be an object.', + 'Linux runtime manifest packages.mpv must be an object.', + ]); +}); + test('rejects GPL and nonfree FFmpeg configurations', () => { const cases = [ {