Files
OmniRoute/tests/unit/guardrails/videoBridgeRuntime.test.ts
Diego Rodrigues de Sa e Souza 5379493bed feat: add Video Bridge frame sampling (#10483)
Implements the secure, opt-in Video Bridge for issue #9760, including bounded FFmpeg frame extraction, capability-aware routing, telemetry, settings UI, localization, documentation, and regression coverage.
2026-08-15 14:23:29 -03:00

458 lines
14 KiB
TypeScript

import assert from "node:assert/strict";
import { access, mkdtemp, rm, writeFile } from "node:fs/promises";
import { tmpdir } from "node:os";
import { join } from "node:path";
import test from "node:test";
import {
calculateFrameTimestamps,
extractFramesFromLocalVideo,
extractVideoFramesFromBytes,
probeLocalVideo,
probeVideoRuntime,
readBoundedExtractedFrames,
resetVideoRuntimeProbeCacheForTests,
type VideoCommandRunner,
} from "../../../src/lib/guardrails/videoBridgeRuntime.ts";
test("calculates uniform midpoint timestamps", () => {
assert.deepEqual(calculateFrameTimestamps(8, 4), [1, 3, 5, 7]);
assert.deepEqual(calculateFrameTimestamps(0.4, 8), [0.2]);
});
test("probes and extracts a local video using shell-free bounded commands", async () => {
const calls: Array<{ executable: string; args: string[]; timeoutMs: number }> = [];
const runner: VideoCommandRunner = async (executable, args, options) => {
calls.push({ executable, args: [...args], timeoutMs: options.timeoutMs });
if (executable === "ffprobe") {
return {
stdout: JSON.stringify({
format: { duration: "8.0", format_name: "mov,mp4,m4a,3gp,3g2,mj2" },
streams: [{ index: 0, codec_type: "video", width: 1920, height: 1080 }],
}),
stderr: "",
};
}
return { stdout: "", stderr: "" };
};
const metadata = await probeLocalVideo("/tmp/input.mp4", {
maxDurationSeconds: 600,
runner,
timeoutMs: 5_000,
});
const frames = await extractFramesFromLocalVideo("/tmp/input.mp4", "/tmp/frames", {
durationSeconds: metadata.durationSeconds,
frameCount: 4,
runner,
streamIndex: metadata.streamIndex,
timeoutMs: 10_000,
});
assert.equal(metadata.durationSeconds, 8);
assert.deepEqual(
frames.map((frame) => frame.timestampSeconds),
[1, 3, 5, 7]
);
assert.equal(calls[0].executable, "ffprobe");
assert.equal(calls[0].timeoutMs, 5_000);
assert.deepEqual(calls[0].args.slice(-2), ["json", "/tmp/input.mp4"]);
assert.deepEqual(
calls[0].args.slice(
calls[0].args.indexOf("-protocol_whitelist"),
calls[0].args.indexOf("-protocol_whitelist") + 2
),
["-protocol_whitelist", "file"]
);
assert.ok(calls[0].args.includes("-format_whitelist"));
assert.equal(
calls[0].args[calls[0].args.indexOf("-show_entries") + 1],
"format=duration,format_name:stream=index,codec_type,width,height:stream_disposition=default,attached_pic"
);
assert.equal(
calls.slice(1).every((call) => call.executable === "ffmpeg"),
true
);
assert.equal(
calls.slice(1).every((call) => call.args.includes("-nostdin")),
true
);
assert.equal(
calls.slice(1).every((call) => call.args.includes("-protocol_whitelist")),
true
);
assert.equal(
calls.slice(1).every((call) => call.args.includes("-format_whitelist")),
true
);
assert.equal(
calls.slice(1).every((call) => call.args.includes("-threads") && call.args.includes("1")),
true
);
assert.equal(
calls
.slice(1)
.every((call) =>
call.args.some(
(arg) =>
arg.includes("min(1024,iw)") &&
arg.includes("min(1024,ih)") &&
arg.includes("force_original_aspect_ratio=decrease")
)
),
true
);
assert.equal(
calls.slice(1).every((call) => !call.args.some((arg) => arg.includes("://"))),
true
);
});
test("rejects remote process inputs and videos beyond the duration bound", async () => {
const runner: VideoCommandRunner = async () => ({
stdout: JSON.stringify({
format: { duration: "601", format_name: "mp4" },
streams: [{ index: 0, codec_type: "video", width: 1280, height: 720 }],
}),
stderr: "private upstream details",
});
await assert.rejects(
() => probeLocalVideo("https://example.test/video.mp4", { runner }),
/local path/
);
await assert.rejects(
() => probeLocalVideo("/tmp/input.mp4", { maxDurationSeconds: 600, runner }),
/maximum duration/
);
});
test("rejects reference-bearing formats before extraction and confines both tools to local files", async () => {
const calls: Array<{ executable: string; args: string[] }> = [];
const runner: VideoCommandRunner = async (executable, args) => {
calls.push({ executable, args: [...args] });
return {
stdout: JSON.stringify({
format: { duration: "10", format_name: "hls" },
streams: [{ index: 0, codec_type: "video", width: 640, height: 360 }],
}),
stderr: "http://169.254.169.254/latest/meta-data",
};
};
await assert.rejects(() => probeLocalVideo("/tmp/malicious.m3u8", { runner }), /format/);
assert.equal(calls.length, 1, "a rejected manifest must never reach ffmpeg");
assert.deepEqual(
calls[0].args.slice(
calls[0].args.indexOf("-protocol_whitelist"),
calls[0].args.indexOf("-protocol_whitelist") + 2
),
["-protocol_whitelist", "file"]
);
assert.equal(
calls[0].args.some((arg) => arg.includes("169.254.169.254")),
false
);
});
test("safe containers may contain URL or traversal-like compressed bytes without false rejection", async () => {
const calls: string[] = [];
const runner: VideoCommandRunner = async (executable, args) => {
calls.push(executable);
if (executable === "ffprobe") {
return {
stdout: JSON.stringify({
format: { duration: "2", format_name: "mp4" },
streams: [{ index: 0, codec_type: "video", width: 640, height: 360 }],
}),
stderr: "",
};
}
await writeFile(args.at(-1) ?? "", Buffer.from([0xff, 0xd8, 0xff, 0xd9]));
return { stdout: "", stderr: "" };
};
const validContainerBytes = Buffer.concat([
Buffer.from([0, 0, 0, 24, 0x66, 0x74, 0x79, 0x70]),
Buffer.from("compressed-chunk:http://127.0.0.1/../not-a-reference"),
]);
const result = await extractVideoFramesFromBytes(validContainerBytes, {
frameCount: 1,
maxDurationSeconds: 600,
runner,
timeoutMs: 5_000,
});
assert.deepEqual(calls, ["ffprobe", "ffmpeg"]);
assert.equal(result.frames.length, 1);
});
test("rejects oversized dimensions and pixel counts from sanitized probe metadata", async () => {
const runner: VideoCommandRunner = async () => ({
stdout: JSON.stringify({
format: { duration: "2", format_name: "mp4" },
streams: [{ index: 0, codec_type: "video", width: 16384, height: 16384 }],
}),
stderr: "private path",
});
await assert.rejects(() => probeLocalVideo("/tmp/oversized.mp4", { runner }), /dimensions/);
});
test("rejects a container when any video stream exceeds dimension or pixel limits", async () => {
const runner: VideoCommandRunner = async () => ({
stdout: JSON.stringify({
format: { duration: "2", format_name: "mp4" },
streams: [
{ index: 0, codec_type: "video", width: 640, height: 360 },
{ index: 1, codec_type: "video", width: 16384, height: 16384 },
],
}),
stderr: "",
});
await assert.rejects(
() => probeLocalVideo("/tmp/multiple-streams.mp4", { runner }),
/dimensions/
);
});
test("selects the lowest validated video stream index and maps it explicitly in ffmpeg", async () => {
const calls: Array<{ executable: string; args: string[] }> = [];
const runner: VideoCommandRunner = async (executable, args) => {
calls.push({ executable, args: [...args] });
return executable === "ffprobe"
? {
stdout: JSON.stringify({
format: { duration: "4", format_name: "mp4" },
streams: [
{ index: 3, codec_type: "video", width: 1280, height: 720 },
{ index: 1, codec_type: "video", width: 640, height: 360 },
],
}),
stderr: "",
}
: { stdout: "", stderr: "" };
};
const metadata = await probeLocalVideo("/tmp/multiple-safe.mp4", { runner });
await extractFramesFromLocalVideo("/tmp/multiple-safe.mp4", "/tmp/frames", {
durationSeconds: metadata.durationSeconds,
frameCount: 1,
runner,
streamIndex: metadata.streamIndex,
});
assert.equal(metadata.streamIndex, 1);
const ffmpegArgs = calls.find((call) => call.executable === "ffmpeg")?.args ?? [];
const mapIndex = ffmpegArgs.indexOf("-map");
assert.deepEqual(ffmpegArgs.slice(mapIndex, mapIndex + 2), ["-map", "0:1"]);
});
test("ignores an attached cover and maps the preferred playable default stream", async () => {
const calls: Array<{ executable: string; args: string[] }> = [];
const runner: VideoCommandRunner = async (executable, args) => {
calls.push({ executable, args: [...args] });
return executable === "ffprobe"
? {
stdout: JSON.stringify({
format: { duration: "4", format_name: "mp4" },
streams: [
{
index: 0,
codec_type: "video",
width: 20000,
height: 20000,
disposition: { attached_pic: 1, default: 0 },
},
{
index: 1,
codec_type: "video",
width: 640,
height: 360,
disposition: { attached_pic: 0, default: 0 },
},
{
index: 2,
codec_type: "video",
width: 1280,
height: 720,
disposition: { attached_pic: 0, default: 1 },
},
],
}),
stderr: "",
}
: { stdout: "", stderr: "" };
};
const metadata = await probeLocalVideo("/tmp/cover-and-video.mp4", { runner });
await extractFramesFromLocalVideo("/tmp/cover-and-video.mp4", "/tmp/frames", {
durationSeconds: metadata.durationSeconds,
frameCount: 1,
runner,
streamIndex: metadata.streamIndex,
});
assert.equal(metadata.streamIndex, 2);
assert.equal(metadata.width, 1280);
assert.equal(metadata.height, 720);
const ffmpegArgs = calls.find((call) => call.executable === "ffmpeg")?.args ?? [];
const mapIndex = ffmpegArgs.indexOf("-map");
assert.deepEqual(ffmpegArgs.slice(mapIndex, mapIndex + 2), ["-map", "0:2"]);
});
test("rejects a container whose only video stream is an attached picture", async () => {
const runner: VideoCommandRunner = async () => ({
stdout: JSON.stringify({
format: { duration: "4", format_name: "mp4" },
streams: [
{ index: 0, codec_type: "audio" },
{
index: 1,
codec_type: "video",
width: 600,
height: 600,
disposition: { attached_pic: 1, default: 1 },
},
],
}),
stderr: "",
});
await assert.rejects(
() => probeLocalVideo("/tmp/audio-with-cover.mp4", { runner }),
/playable video stream/
);
});
test("malformed playable stream disposition or index fails closed without selecting a cover", async () => {
const runner: VideoCommandRunner = async () => ({
stdout: JSON.stringify({
format: { duration: "4", format_name: "mp4" },
streams: [
{
index: 0,
codec_type: "video",
width: 300,
height: 300,
disposition: { attached_pic: "1", default: "not-a-flag" },
},
{
index: "bad",
codec_type: "video",
width: 1280,
height: 720,
disposition: { attached_pic: 0, default: 1 },
},
],
}),
stderr: "",
});
await assert.rejects(
() => probeLocalVideo("/tmp/malformed-stream.mp4", { runner }),
/dimensions|stream metadata/
);
});
test("runtime status exposes sanitized versions and a sanitized unavailable reason", async () => {
resetVideoRuntimeProbeCacheForTests();
const ready = await probeVideoRuntime({
cacheTtlMs: 0,
runner: async (executable) => ({
stdout:
executable === "ffmpeg" ? "ffmpeg version 6.1.1 secret" : "ffprobe version 6.1.1 secret",
stderr: "",
}),
});
assert.deepEqual(ready, {
available: true,
ffmpegVersion: "6.1.1",
ffprobeVersion: "6.1.1",
});
resetVideoRuntimeProbeCacheForTests();
const unavailable = await probeVideoRuntime({
cacheTtlMs: 0,
runner: async () => {
throw new Error("spawn /private/operator/path ENOENT");
},
});
assert.deepEqual(unavailable, {
available: false,
ffmpegVersion: null,
ffprobeVersion: null,
reason: "FFmpeg and ffprobe are not available on PATH",
});
});
test("runtime probe uses its short cache instead of spawning on every status read", async () => {
resetVideoRuntimeProbeCacheForTests();
let calls = 0;
const runner: VideoCommandRunner = async (executable) => {
calls += 1;
return {
stdout: `${executable} version 7.0`,
stderr: "",
};
};
const first = await probeVideoRuntime({ cacheTtlMs: 30_000, runner });
const second = await probeVideoRuntime({ cacheTtlMs: 30_000, runner });
assert.deepEqual(second, first);
assert.equal(calls, 2, "one ffmpeg + one ffprobe process should serve both reads");
});
test("checks individual and aggregate frame byte caps before returning broker output", async () => {
const directory = await mkdtemp(join(tmpdir(), "video-frame-caps-"));
const first = join(directory, "first.jpg");
const second = join(directory, "second.jpg");
await writeFile(first, Buffer.alloc(3));
await writeFile(second, Buffer.alloc(3));
const frames = [
{ path: first, timestampSeconds: 1 },
{ path: second, timestampSeconds: 2 },
];
try {
await assert.rejects(
() => readBoundedExtractedFrames(frames, { maxFrameBytes: 2, maxTotalBytes: 8 }),
/frame byte limit/
);
await assert.rejects(
() => readBoundedExtractedFrames(frames, { maxFrameBytes: 4, maxTotalBytes: 5 }),
/total frame byte limit/
);
const result = await readBoundedExtractedFrames(frames, {
maxFrameBytes: 4,
maxTotalBytes: 6,
});
assert.equal(result.length, 2);
assert.equal(
result.reduce((sum, frame) => sum + frame.byteLength, 0),
6
);
} finally {
await rm(directory, { recursive: true, force: true });
}
});
test("byte extraction removes its private temporary tree after a subprocess failure", async () => {
let temporaryInput = "";
const runner: VideoCommandRunner = async (_executable, args) => {
temporaryInput = args.at(-1) ?? "";
throw Object.assign(new Error("private ffprobe path"), { code: "ENOENT" });
};
await assert.rejects(
() =>
extractVideoFramesFromBytes(Buffer.from("video"), {
frameCount: 1,
maxDurationSeconds: 600,
runner,
timeoutMs: 5_000,
}),
/private ffprobe path/
);
assert.notEqual(temporaryInput, "");
await assert.rejects(() => access(temporaryInput));
});