@studiomeyer/mcp-video 1.0.1 → 1.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +34 -0
- package/README.md +9 -1
- package/dist/handlers/dispatch.test.d.ts +1 -0
- package/dist/handlers/dispatch.test.js +49 -0
- package/dist/handlers/dispatch.test.js.map +1 -0
- package/dist/handlers/index.js +5 -0
- package/dist/handlers/index.js.map +1 -1
- package/dist/handlers/smart-screenshot.js +3 -3
- package/dist/handlers/smart-screenshot.js.map +1 -1
- package/dist/handlers/tts.js +2 -2
- package/dist/handlers/tts.js.map +1 -1
- package/dist/handlers/video.js +4 -4
- package/dist/handlers/video.js.map +1 -1
- package/dist/lib/ffmpeg-run.test.js +44 -1
- package/dist/lib/ffmpeg-run.test.js.map +1 -1
- package/dist/lib/sanitize-tool-paths.d.ts +35 -0
- package/dist/lib/sanitize-tool-paths.js +130 -0
- package/dist/lib/sanitize-tool-paths.js.map +1 -0
- package/dist/lib/sanitize-tool-paths.test.d.ts +1 -0
- package/dist/lib/sanitize-tool-paths.test.js +134 -0
- package/dist/lib/sanitize-tool-paths.test.js.map +1 -0
- package/dist/lib/url-guard-resolve.test.d.ts +12 -0
- package/dist/lib/url-guard-resolve.test.js +106 -0
- package/dist/lib/url-guard-resolve.test.js.map +1 -0
- package/package.json +5 -4
- package/server.json +21 -0
- package/src/handlers/dispatch.test.ts +55 -0
- package/src/handlers/index.ts +5 -0
- package/src/handlers/smart-screenshot.ts +3 -3
- package/src/handlers/tts.ts +2 -2
- package/src/handlers/video.ts +4 -4
- package/src/lib/ffmpeg-run.test.ts +50 -1
- package/src/lib/sanitize-tool-paths.test.ts +185 -0
- package/src/lib/sanitize-tool-paths.ts +154 -0
- package/src/lib/url-guard-resolve.test.ts +116 -0
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,40 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## Unreleased
|
|
9
9
|
|
|
10
|
+
### Security — wire path/SSRF guards into the tool boundary (2026-06-21)
|
|
11
|
+
|
|
12
|
+
The prior sweeps *built* `validateFfmpegPath` and `resolveAndGuardUrl` but
|
|
13
|
+
left them mostly unconnected: only `narrated-video.ts` validated paths, and
|
|
14
|
+
**no** handler used the DNS-resolving URL guard. This release connects the
|
|
15
|
+
existing defenses to the actual tool entry points.
|
|
16
|
+
|
|
17
|
+
- **Flag-injection choke point (`src/lib/sanitize-tool-paths.ts`, NEW).**
|
|
18
|
+
Every path-like argument (`inputPath`, `outputPath`, `videoPath`,
|
|
19
|
+
`musicPath`, `subtitlePath`, `mainVideo`/`overlayVideo`, concat
|
|
20
|
+
`clips[].path`, mixer `tracks[].path`, beat-sync `clips[]`,
|
|
21
|
+
template `clips{}`, chroma-key `background`) is now validated in
|
|
22
|
+
`handleToolCall` before any handler can forward it to ffmpeg/ffprobe.
|
|
23
|
+
A path beginning with `-` (e.g. `outputPath: "-y"`) or containing a NUL
|
|
24
|
+
byte is rejected with a structured tool error instead of being passed to
|
|
25
|
+
ffmpeg as a flag. Output paths previously had **no** existence check, so
|
|
26
|
+
this was a live gap; engine `assertExists` only ever guarded inputs.
|
|
27
|
+
Chroma-key `background` keeps accepting a bare 6-digit hex colour.
|
|
28
|
+
- **DNS-rebinding SSRF upgrade.** `record_website_video`,
|
|
29
|
+
`record_website_scroll`, `record_multi_device`, `create_narrated_video`,
|
|
30
|
+
`screenshot_element` and `detect_page_features` now use the async
|
|
31
|
+
`resolveAndGuardUrl` (DNS-resolves the host and rejects internal IPs)
|
|
32
|
+
instead of the sync `guardUrl` (literal-host only). Closes the bypass
|
|
33
|
+
where a public hostname resolves to `127.0.0.1` / `169.254.169.254`.
|
|
34
|
+
`MCP_VIDEO_ALLOW_INTERNAL=1` still bypasses for local dev and is now
|
|
35
|
+
documented in the README config table.
|
|
36
|
+
|
|
37
|
+
**Tests:** +41 (104 → 145, all green), tsc clean. New
|
|
38
|
+
`sanitize-tool-paths.test.ts` (attack-blocked + benign-allowed per field
|
|
39
|
+
shape) + `handlers/dispatch.test.ts` (end-to-end choke point) +
|
|
40
|
+
`url-guard-resolve.test.ts` (DNS-rebind, mocked `node:dns/promises`) +
|
|
41
|
+
`runFfprobe` coverage added to `ffmpeg-run.test.ts` (the security-critical
|
|
42
|
+
probe runner was previously untested).
|
|
43
|
+
|
|
10
44
|
### Fixed — Cross-platform ffmpeg detection (issue #11, 2026-05-07)
|
|
11
45
|
|
|
12
46
|
- **Windows startup failure**: `checkDependencies()` previously called
|
package/README.md
CHANGED
|
@@ -6,7 +6,14 @@
|
|
|
6
6
|
|
|
7
7
|
# mcp-video
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
|
|
10
|
+
<!-- badges -->
|
|
11
|
+
[](https://www.npmjs.com/package/mcp-video)
|
|
12
|
+
[](https://www.npmjs.com/package/mcp-video)
|
|
13
|
+

|
|
14
|
+

|
|
15
|
+

|
|
16
|
+
<!-- /badges -->**Cinema-grade video production for AI agents.**
|
|
10
17
|
|
|
11
18
|

|
|
12
19
|

|
|
@@ -122,6 +129,7 @@ MCP_HTTP=1 MCP_PORT=9847 npx mcp-video
|
|
|
122
129
|
| `MCP_PORT` | `9847` | HTTP port |
|
|
123
130
|
| `MCP_HOST` | `127.0.0.1` | HTTP bind address |
|
|
124
131
|
| `MCP_VIDEO_DEBUG` | `false` | Enable debug logging |
|
|
132
|
+
| `MCP_VIDEO_ALLOW_INTERNAL` | `false` | Set to `1` to allow URLs that resolve to localhost / private / metadata IPs. **Local dev only** — leave unset in production (SSRF guard). |
|
|
125
133
|
|
|
126
134
|
## What You Can Build
|
|
127
135
|
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { describe, it, expect, vi } from 'vitest';
|
|
2
|
+
// Logger writes to stderr; silence it so test output stays clean.
|
|
3
|
+
vi.mock('../lib/logger.js', () => ({
|
|
4
|
+
logger: { info: vi.fn(), logError: vi.fn(), warn: vi.fn(), error: vi.fn(), debug: vi.fn() },
|
|
5
|
+
}));
|
|
6
|
+
import { handleToolCall } from './index.js';
|
|
7
|
+
function parse(res) {
|
|
8
|
+
return JSON.parse(res.content[0].text);
|
|
9
|
+
}
|
|
10
|
+
describe('handleToolCall — path-injection choke point', () => {
|
|
11
|
+
it('blocks a flag-injection outputPath before reaching the engine', async () => {
|
|
12
|
+
const res = await handleToolCall('crop_video', {
|
|
13
|
+
inputPath: 'in.mp4',
|
|
14
|
+
outputPath: '-y',
|
|
15
|
+
width: 100,
|
|
16
|
+
height: 100,
|
|
17
|
+
});
|
|
18
|
+
expect(res.isError).toBe(true);
|
|
19
|
+
const body = parse(res);
|
|
20
|
+
expect(String(body.error)).toMatch(/outputPath.*flag/);
|
|
21
|
+
});
|
|
22
|
+
it('blocks a flag-injection clip path in concatenate_videos', async () => {
|
|
23
|
+
const res = await handleToolCall('concatenate_videos', {
|
|
24
|
+
outputPath: 'out.mp4',
|
|
25
|
+
clips: [{ path: 'a.mp4' }, { path: '-protocol_whitelist' }],
|
|
26
|
+
});
|
|
27
|
+
expect(res.isError).toBe(true);
|
|
28
|
+
expect(String(parse(res).error)).toMatch(/clips\[1\]\.path.*flag/);
|
|
29
|
+
});
|
|
30
|
+
it('returns a structured error (never throws) for an unknown tool', async () => {
|
|
31
|
+
const res = await handleToolCall('does_not_exist', { outputPath: '-y' });
|
|
32
|
+
expect(res.isError).toBe(true);
|
|
33
|
+
expect(res.content[0].text).toContain('Unknown tool');
|
|
34
|
+
});
|
|
35
|
+
it('lets benign args pass the choke point (failure, if any, is not flag-injection)', async () => {
|
|
36
|
+
// No ffmpeg binary in this environment, so the engine will fail at
|
|
37
|
+
// assertExists/spawn — but crucially NOT with a flag-injection error,
|
|
38
|
+
// which proves sanitizeToolPaths did not false-positive on a valid path.
|
|
39
|
+
const res = await handleToolCall('crop_video', {
|
|
40
|
+
inputPath: 'definitely-missing-input.mp4',
|
|
41
|
+
outputPath: 'out.mp4',
|
|
42
|
+
width: 100,
|
|
43
|
+
height: 100,
|
|
44
|
+
});
|
|
45
|
+
expect(res.isError).toBe(true);
|
|
46
|
+
expect(String(parse(res).error)).not.toMatch(/flag/);
|
|
47
|
+
});
|
|
48
|
+
});
|
|
49
|
+
//# sourceMappingURL=dispatch.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dispatch.test.js","sourceRoot":"","sources":["../../src/handlers/dispatch.test.ts"],"names":[],"mappings":"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"}
|
package/dist/handlers/index.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { logger } from '../lib/logger.js';
|
|
2
|
+
import { sanitizeToolPaths } from '../lib/sanitize-tool-paths.js';
|
|
2
3
|
import { videoHandlers } from './video.js';
|
|
3
4
|
import { postProductionHandlers } from './post-production.js';
|
|
4
5
|
import { ttsHandlers } from './tts.js';
|
|
@@ -19,6 +20,10 @@ export async function handleToolCall(name, args) {
|
|
|
19
20
|
return { content: [{ type: 'text', text: `Unknown tool: ${name}` }], isError: true };
|
|
20
21
|
}
|
|
21
22
|
try {
|
|
23
|
+
// Choke point: reject flag-injection / NUL-byte path arguments before any
|
|
24
|
+
// handler can forward them to ffmpeg/ffprobe. No-op for tools that take no
|
|
25
|
+
// path args. See lib/sanitize-tool-paths.ts for the threat model.
|
|
26
|
+
sanitizeToolPaths(name, args);
|
|
22
27
|
return await handler(args);
|
|
23
28
|
}
|
|
24
29
|
catch (error) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/handlers/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAC1C,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAC3C,OAAO,EAAE,sBAAsB,EAAE,MAAM,sBAAsB,CAAC;AAC9D,OAAO,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AACvC,OAAO,EAAE,uBAAuB,EAAE,MAAM,uBAAuB,CAAC;AAChE,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAC/C,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAE7C,MAAM,gBAAgB,GAAgC;IACpD,GAAG,aAAa;IAChB,GAAG,sBAAsB;IACzB,GAAG,WAAW;IACd,GAAG,uBAAuB;IAC1B,GAAG,eAAe;IAClB,GAAG,cAAc;CAClB,CAAC;AAEF,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,IAAY,EAAE,IAAa;IAC9D,MAAM,OAAO,GAAG,gBAAgB,CAAC,IAAI,CAAC,CAAC;IACvC,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,iBAAiB,IAAI,EAAE,EAAE,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;IACvF,CAAC;IACD,IAAI,CAAC;QACH,OAAO,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7B,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,CAAC,QAAQ,CAAC,uBAAuB,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;QAChE,MAAM,OAAO,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QACvE,OAAO;YACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,gBAAgB,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC;YACtH,OAAO,EAAE,IAAI;SACd,CAAC;IACJ,CAAC;AACH,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/handlers/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAC1C,OAAO,EAAE,iBAAiB,EAAE,MAAM,+BAA+B,CAAC;AAClE,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAC3C,OAAO,EAAE,sBAAsB,EAAE,MAAM,sBAAsB,CAAC;AAC9D,OAAO,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AACvC,OAAO,EAAE,uBAAuB,EAAE,MAAM,uBAAuB,CAAC;AAChE,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAC/C,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAE7C,MAAM,gBAAgB,GAAgC;IACpD,GAAG,aAAa;IAChB,GAAG,sBAAsB;IACzB,GAAG,WAAW;IACd,GAAG,uBAAuB;IAC1B,GAAG,eAAe;IAClB,GAAG,cAAc;CAClB,CAAC;AAEF,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,IAAY,EAAE,IAAa;IAC9D,MAAM,OAAO,GAAG,gBAAgB,CAAC,IAAI,CAAC,CAAC;IACvC,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,iBAAiB,IAAI,EAAE,EAAE,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;IACvF,CAAC;IACD,IAAI,CAAC;QACH,0EAA0E;QAC1E,2EAA2E;QAC3E,kEAAkE;QAClE,iBAAiB,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;QAC9B,OAAO,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7B,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,CAAC,QAAQ,CAAC,uBAAuB,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;QAChE,MAAM,OAAO,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QACvE,OAAO;YACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,gBAAgB,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC;YACtH,OAAO,EAAE,IAAI;SACd,CAAC;IACJ,CAAC;AACH,CAAC"}
|
|
@@ -4,14 +4,14 @@
|
|
|
4
4
|
import { jsonResponse } from '../lib/types.js';
|
|
5
5
|
import { logger } from '../lib/logger.js';
|
|
6
6
|
import { smartScreenshot } from '../tools/engine/smart-screenshot.js';
|
|
7
|
-
import {
|
|
7
|
+
import { resolveAndGuardUrl } from '../lib/url-guard.js';
|
|
8
8
|
export const smartScreenshotHandlers = {
|
|
9
9
|
/**
|
|
10
10
|
* Take element-aware screenshots of specific page features
|
|
11
11
|
*/
|
|
12
12
|
screenshot_element: async (args) => {
|
|
13
13
|
try {
|
|
14
|
-
const guard =
|
|
14
|
+
const guard = await resolveAndGuardUrl(args.url);
|
|
15
15
|
if (!guard.ok)
|
|
16
16
|
return jsonResponse({ success: false, error: guard.reason }, true);
|
|
17
17
|
const config = {
|
|
@@ -37,7 +37,7 @@ export const smartScreenshotHandlers = {
|
|
|
37
37
|
*/
|
|
38
38
|
detect_page_features: async (args) => {
|
|
39
39
|
try {
|
|
40
|
-
const guard =
|
|
40
|
+
const guard = await resolveAndGuardUrl(args.url);
|
|
41
41
|
if (!guard.ok)
|
|
42
42
|
return jsonResponse({ success: false, error: guard.reason }, true);
|
|
43
43
|
const config = {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"smart-screenshot.js","sourceRoot":"","sources":["../../src/handlers/smart-screenshot.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,YAAY,EAAoB,MAAM,iBAAiB,CAAC;AACjE,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAC1C,OAAO,EAAE,eAAe,EAAE,MAAM,qCAAqC,CAAC;AAEtE,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"smart-screenshot.js","sourceRoot":"","sources":["../../src/handlers/smart-screenshot.ts"],"names":[],"mappings":"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"}
|
package/dist/handlers/tts.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
import { jsonResponse } from '../lib/types.js';
|
|
5
5
|
import { logger } from '../lib/logger.js';
|
|
6
|
-
import {
|
|
6
|
+
import { resolveAndGuardUrl } from '../lib/url-guard.js';
|
|
7
7
|
import { generateSpeech, listElevenLabsVoices, createNarratedVideo, } from '../tools/index.js';
|
|
8
8
|
const OUTPUT_DIR = process.env.VIDEO_OUTPUT_DIR || './output';
|
|
9
9
|
export const ttsHandlers = {
|
|
@@ -52,7 +52,7 @@ export const ttsHandlers = {
|
|
|
52
52
|
},
|
|
53
53
|
create_narrated_video: async (args) => {
|
|
54
54
|
try {
|
|
55
|
-
const guard =
|
|
55
|
+
const guard = await resolveAndGuardUrl(args.url);
|
|
56
56
|
if (!guard.ok)
|
|
57
57
|
return jsonResponse({ success: false, error: guard.reason }, true);
|
|
58
58
|
const segments = args.segments.map((s) => ({
|
package/dist/handlers/tts.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tts.js","sourceRoot":"","sources":["../../src/handlers/tts.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,YAAY,EAAoB,MAAM,iBAAiB,CAAC;AACjE,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAC1C,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"tts.js","sourceRoot":"","sources":["../../src/handlers/tts.ts"],"names":[],"mappings":"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"}
|
package/dist/handlers/video.js
CHANGED
|
@@ -5,7 +5,7 @@ import { jsonResponse } from '../lib/types.js';
|
|
|
5
5
|
import { logger } from '../lib/logger.js';
|
|
6
6
|
import { recordWebsite } from '../tools/index.js';
|
|
7
7
|
import * as path from 'path';
|
|
8
|
-
import {
|
|
8
|
+
import { resolveAndGuardUrl } from '../lib/url-guard.js';
|
|
9
9
|
const OUTPUT_DIR = process.env.VIDEO_OUTPUT_DIR || './output';
|
|
10
10
|
export const videoHandlers = {
|
|
11
11
|
/**
|
|
@@ -13,7 +13,7 @@ export const videoHandlers = {
|
|
|
13
13
|
*/
|
|
14
14
|
record_website_video: async (args) => {
|
|
15
15
|
try {
|
|
16
|
-
const guard =
|
|
16
|
+
const guard = await resolveAndGuardUrl(args.url);
|
|
17
17
|
if (!guard.ok)
|
|
18
18
|
return jsonResponse({ success: false, error: guard.reason }, true);
|
|
19
19
|
const config = {
|
|
@@ -45,7 +45,7 @@ export const videoHandlers = {
|
|
|
45
45
|
*/
|
|
46
46
|
record_website_scroll: async (args) => {
|
|
47
47
|
try {
|
|
48
|
-
const guard =
|
|
48
|
+
const guard = await resolveAndGuardUrl(args.url);
|
|
49
49
|
if (!guard.ok)
|
|
50
50
|
return jsonResponse({ success: false, error: guard.reason }, true);
|
|
51
51
|
const duration = args.duration ?? 12;
|
|
@@ -77,7 +77,7 @@ export const videoHandlers = {
|
|
|
77
77
|
*/
|
|
78
78
|
record_multi_device: async (args) => {
|
|
79
79
|
try {
|
|
80
|
-
const guard =
|
|
80
|
+
const guard = await resolveAndGuardUrl(args.url);
|
|
81
81
|
if (!guard.ok)
|
|
82
82
|
return jsonResponse({ success: false, error: guard.reason }, true);
|
|
83
83
|
const devices = args.devices ?? ['desktop', 'tablet', 'mobile'];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"video.js","sourceRoot":"","sources":["../../src/handlers/video.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,YAAY,EAAoB,MAAM,iBAAiB,CAAC;AACjE,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAC1C,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAElD,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC;AAC7B,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"video.js","sourceRoot":"","sources":["../../src/handlers/video.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,YAAY,EAAoB,MAAM,iBAAiB,CAAC;AACjE,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAC1C,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAElD,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC;AAC7B,OAAO,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AAEzD,MAAM,UAAU,GAAG,OAAO,CAAC,GAAG,CAAC,gBAAgB,IAAI,UAAU,CAAC;AAE9D,MAAM,CAAC,MAAM,aAAa,GAAgC;IACxD;;OAEG;IACH,oBAAoB,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE;QACnC,IAAI,CAAC;YACH,MAAM,KAAK,GAAG,MAAM,kBAAkB,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YACjD,IAAI,CAAC,KAAK,CAAC,EAAE;gBAAE,OAAO,YAAY,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,CAAC,MAAM,EAAE,EAAE,IAAI,CAAC,CAAC;YAClF,MAAM,MAAM,GAAoB;gBAC9B,GAAG,EAAE,KAAK,CAAC,GAAG;gBACd,UAAU,EAAE,IAAI,CAAC,UAAU,IAAI,kBAAkB,CAAC,KAAK,CAAC,GAAG,EAAE,OAAO,CAAC;gBACrE,QAAQ,EAAE,IAAI,CAAC,QAA0B,IAAI,SAAS;gBACtD,GAAG,EAAE,IAAI,CAAC,GAAG,IAAI,EAAE;gBACnB,MAAM,EAAE,IAAI,CAAC,MAA6B;gBAC1C,MAAM,EAAE,IAAI,CAAC,MAAM,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE;gBACxC,QAAQ,EAAE;oBACR,KAAK,EAAE,IAAI,CAAC,KAAK,IAAI,MAAM;oBAC3B,GAAG,EAAE,IAAI,CAAC,OAAO,IAAI,EAAE;iBACxB;gBACD,QAAQ,EAAE,IAAI,CAAC,QAAQ,IAAI,KAAK;gBAChC,cAAc,EAAE,IAAI,CAAC,cAAc,IAAI,IAAI;gBAC3C,eAAe,EAAE,IAAI,CAAC,eAAe,IAAI,IAAI;aAC9C,CAAC;YAEF,MAAM,MAAM,GAAG,MAAM,aAAa,CAAC,MAAM,CAAC,CAAC;YAC3C,OAAO,YAAY,CAAC,MAAM,CAAC,CAAC;QAC9B,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,OAAO,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YACvE,MAAM,CAAC,KAAK,CAAC,gCAAgC,OAAO,EAAE,CAAC,CAAC;YACxD,OAAO,YAAY,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE,IAAI,CAAC,CAAC;QAChE,CAAC;IACH,CAAC;IAED;;OAEG;IACH,qBAAqB,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE;QACpC,IAAI,CAAC;YACH,MAAM,KAAK,GAAG,MAAM,kBAAkB,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YACjD,IAAI,CAAC,KAAK,CAAC,EAAE;gBAAE,OAAO,YAAY,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,CAAC,MAAM,EAAE,EAAE,IAAI,CAAC,CAAC;YAClF,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,IAAI,EAAE,CAAC;YACrC,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,IAAI,UAAU,CAAC;YAEzC,MAAM,MAAM,GAAoB;gBAC9B,GAAG,EAAE,KAAK,CAAC,GAAG;gBACd,UAAU,EAAE,IAAI,CAAC,UAAU,IAAI,kBAAkB,CAAC,KAAK,CAAC,GAAG,EAAE,QAAQ,CAAC;gBACtE,QAAQ,EAAG,IAAI,CAAC,QAA2B,IAAI,SAAS;gBACxD,GAAG,EAAE,EAAE;gBACP,MAAM,EAAE;oBACN,EAAE,IAAI,EAAE,OAAO,EAAE,QAAQ,EAAE,GAAG,EAAE;oBAChC,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,EAAE;oBAClD,EAAE,IAAI,EAAE,OAAO,EAAE,QAAQ,EAAE,CAAC,EAAE;iBAC/B;gBACD,MAAM,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE;gBAC1B,QAAQ,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,EAAE,EAAE;aACrC,CAAC;YAEF,MAAM,MAAM,GAAG,MAAM,aAAa,CAAC,MAAM,CAAC,CAAC;YAC3C,OAAO,YAAY,CAAC,MAAM,CAAC,CAAC;QAC9B,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,OAAO,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YACvE,MAAM,CAAC,KAAK,CAAC,iCAAiC,OAAO,EAAE,CAAC,CAAC;YACzD,OAAO,YAAY,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE,IAAI,CAAC,CAAC;QAChE,CAAC;IACH,CAAC;IAED;;OAEG;IACH,mBAAmB,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE;QAClC,IAAI,CAAC;YACH,MAAM,KAAK,GAAG,MAAM,kBAAkB,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YACjD,IAAI,CAAC,KAAK,CAAC,EAAE;gBAAE,OAAO,YAAY,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,CAAC,MAAM,EAAE,EAAE,IAAI,CAAC,CAAC;YAClF,MAAM,OAAO,GAAqB,IAAI,CAAC,OAAO,IAAI,CAAC,SAAS,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAC;YAClF,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,IAAI,EAAE,CAAC;YACrC,MAAM,SAAS,GAAG,IAAI,CAAC,SAAS,IAAI,UAAU,CAAC;YAC/C,MAAM,OAAO,GAA4B,EAAE,CAAC;YAE5C,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;gBAC7B,MAAM,CAAC,IAAI,CAAC,aAAa,MAAM,iBAAiB,KAAK,CAAC,GAAG,KAAK,CAAC,CAAC;gBAEhE,MAAM,MAAM,GAAoB;oBAC9B,GAAG,EAAE,KAAK,CAAC,GAAG;oBACd,UAAU,EAAE,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,kBAAkB,CAAC,KAAK,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;oBACvE,QAAQ,EAAE,MAAM;oBAChB,GAAG,EAAE,EAAE;oBACP,MAAM,EAAE;wBACN,EAAE,IAAI,EAAE,OAAO,EAAE,QAAQ,EAAE,CAAC,EAAE;wBAC9B,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE;wBAC9D,EAAE,IAAI,EAAE,OAAO,EAAE,QAAQ,EAAE,GAAG,EAAE;qBACjC;oBACD,MAAM,EAAE,EAAE,OAAO,EAAE,MAAM,KAAK,QAAQ,IAAI,MAAM,KAAK,kBAAkB,EAAE;oBACzE,QAAQ,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,EAAE,EAAE;iBACrC,CAAC;gBAEF,MAAM,MAAM,GAAG,MAAM,aAAa,CAAC,MAAM,CAAC,CAAC;gBAC3C,OAAO,CAAC,MAAM,CAAC,GAAG,MAAM,CAAC;YAC3B,CAAC;YAED,OAAO,YAAY,CAAC;gBAClB,OAAO,EAAE,IAAI;gBACb,OAAO,EAAE,OAAO,CAAC,MAAM;gBACvB,OAAO;aACR,CAAC,CAAC;QACL,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,OAAO,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YACvE,MAAM,CAAC,KAAK,CAAC,+BAA+B,OAAO,EAAE,CAAC,CAAC;YACvD,OAAO,YAAY,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE,IAAI,CAAC,CAAC;QAChE,CAAC;IACH,CAAC;CACF,CAAC;AAEF;;GAEG;AACH,SAAS,kBAAkB,CAAC,GAAW,EAAE,MAAc;IACrD,MAAM,IAAI,GAAG,kBAAkB,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;IAC7C,OAAO,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,IAAI,CAAC,CAAC;AACrC,CAAC;AAED,SAAS,kBAAkB,CAAC,GAAW,EAAE,MAAc;IACrD,IAAI,CAAC;QACH,MAAM,QAAQ,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC,QAAQ;aACnC,OAAO,CAAC,QAAQ,EAAE,EAAE,CAAC;aACrB,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;QACvB,MAAM,SAAS,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QACxD,OAAO,GAAG,QAAQ,IAAI,MAAM,IAAI,SAAS,EAAE,CAAC;IAC9C,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,WAAW,MAAM,IAAI,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC;IAC3C,CAAC;AACH,CAAC"}
|
|
@@ -3,7 +3,7 @@ import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
|
3
3
|
const execFileMock = vi.hoisted(() => vi.fn());
|
|
4
4
|
vi.mock('node:child_process', () => ({ execFile: execFileMock }));
|
|
5
5
|
// IMPORTANT: import AFTER vi.mock so the mock is bound.
|
|
6
|
-
import { runFfmpeg } from './ffmpeg-run.js';
|
|
6
|
+
import { runFfmpeg, runFfprobe } from './ffmpeg-run.js';
|
|
7
7
|
describe('ffmpeg-run — runFfmpeg', () => {
|
|
8
8
|
beforeEach(() => {
|
|
9
9
|
execFileMock.mockReset();
|
|
@@ -63,4 +63,47 @@ describe('ffmpeg-run — runFfmpeg', () => {
|
|
|
63
63
|
await expect(runFfmpeg([], { label: 'lut-preset' })).rejects.toThrow(/lut-preset/);
|
|
64
64
|
});
|
|
65
65
|
});
|
|
66
|
+
describe('ffmpeg-run — runFfprobe', () => {
|
|
67
|
+
beforeEach(() => {
|
|
68
|
+
execFileMock.mockReset();
|
|
69
|
+
});
|
|
70
|
+
it('prepends -protocol_whitelist on every probe (closes the "just probe first" SSRF bypass)', async () => {
|
|
71
|
+
execFileMock.mockImplementationOnce((_bin, _args, _opts, cb) => {
|
|
72
|
+
cb(null, '12.34', '');
|
|
73
|
+
});
|
|
74
|
+
await runFfprobe(['-show_entries', 'format=duration', 'in.mp4']);
|
|
75
|
+
const args = execFileMock.mock.calls[0][1];
|
|
76
|
+
expect(args[0]).toBe('-protocol_whitelist');
|
|
77
|
+
expect(args[1]).toBe('file,pipe,crypto,cache,fd');
|
|
78
|
+
expect(args.slice(2)).toEqual(['-show_entries', 'format=duration', 'in.mp4']);
|
|
79
|
+
});
|
|
80
|
+
it('never includes http in the default probe whitelist', async () => {
|
|
81
|
+
execFileMock.mockImplementationOnce((_bin, _args, _opts, cb) => {
|
|
82
|
+
cb(null, '', '');
|
|
83
|
+
});
|
|
84
|
+
await runFfprobe(['x']);
|
|
85
|
+
const protocols = execFileMock.mock.calls[0][1][1].split(',');
|
|
86
|
+
expect(protocols).not.toContain('http');
|
|
87
|
+
expect(protocols).not.toContain('rtsp');
|
|
88
|
+
});
|
|
89
|
+
it('resolves with stdout by default (probe output is on stdout)', async () => {
|
|
90
|
+
execFileMock.mockImplementationOnce((_bin, _args, _opts, cb) => {
|
|
91
|
+
cb(null, '1920\n1080', 'noise');
|
|
92
|
+
});
|
|
93
|
+
const out = await runFfprobe(['x']);
|
|
94
|
+
expect(out).toBe('1920\n1080');
|
|
95
|
+
});
|
|
96
|
+
it('rejects with a sanitized message when ffprobe fails', async () => {
|
|
97
|
+
execFileMock.mockImplementationOnce((_bin, _args, _opts, cb) => {
|
|
98
|
+
cb(new Error('exit 1'), '', 'x-api-key: super-secret-value');
|
|
99
|
+
});
|
|
100
|
+
await expect(runFfprobe(['x'])).rejects.toThrow(/\[REDACTED\]/);
|
|
101
|
+
});
|
|
102
|
+
it('includes the label in the rejection message', async () => {
|
|
103
|
+
execFileMock.mockImplementationOnce((_bin, _args, _opts, cb) => {
|
|
104
|
+
cb(new Error('x'), '', 'boom');
|
|
105
|
+
});
|
|
106
|
+
await expect(runFfprobe([], { label: 'audio-probe' })).rejects.toThrow(/audio-probe/);
|
|
107
|
+
});
|
|
108
|
+
});
|
|
66
109
|
//# sourceMappingURL=ffmpeg-run.test.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ffmpeg-run.test.js","sourceRoot":"","sources":["../../src/lib/ffmpeg-run.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAC;AAE9D,mDAAmD;AACnD,MAAM,YAAY,GAAG,EAAE,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC;AAE/C,EAAE,CAAC,IAAI,CAAC,oBAAoB,EAAE,GAAG,EAAE,CAAC,CAAC,EAAE,QAAQ,EAAE,YAAY,EAAE,CAAC,CAAC,CAAC;AAElE,wDAAwD;AACxD,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"ffmpeg-run.test.js","sourceRoot":"","sources":["../../src/lib/ffmpeg-run.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Central path-argument sanitizer for MCP tool handlers.
|
|
3
|
+
*
|
|
4
|
+
* The threat (already documented in ffmpeg-safety.ts) is real: every tool
|
|
5
|
+
* that shells out to ffmpeg/ffprobe passes user-supplied path strings as
|
|
6
|
+
* positional arguments. ffmpeg treats any argument that begins with `-` as
|
|
7
|
+
* a *flag*, not a filename — so a caller (or a confused LLM following an
|
|
8
|
+
* injected instruction) that sets `outputPath` to `-y`, `-f`, or
|
|
9
|
+
* `-protocol_whitelist` can rewrite the command instead of naming a file.
|
|
10
|
+
* Strings containing NUL bytes are equally dangerous (C-string truncation).
|
|
11
|
+
*
|
|
12
|
+
* `validateFfmpegPath` was written precisely to stop this, but until now it
|
|
13
|
+
* was wired into a single engine (narrated-video). The engines validate
|
|
14
|
+
* *input* existence (`assertExists`) which incidentally blocks some flag
|
|
15
|
+
* inputs, but they never check *output* paths — and several inputs flow
|
|
16
|
+
* through `fs.copyFileSync` / the concat demuxer without an existence check
|
|
17
|
+
* at all. The defense therefore had a wide bypass.
|
|
18
|
+
*
|
|
19
|
+
* This module closes the gap at the handler boundary — the one place every
|
|
20
|
+
* untrusted MCP argument enters — so no engine logic (filter graph building,
|
|
21
|
+
* arg ordering) has to change. Each tool declares which argument keys are
|
|
22
|
+
* path-like; nested arrays of paths (concat clips, audio-mixer tracks) are
|
|
23
|
+
* walked too.
|
|
24
|
+
*/
|
|
25
|
+
/**
|
|
26
|
+
* Validate every path-like argument for the named tool. Throws the same
|
|
27
|
+
* error class `validateFfmpegPath` throws (TypeError / Error) on the first
|
|
28
|
+
* offending value, which the handler's try/catch turns into a structured
|
|
29
|
+
* tool error. Tools not in the registry are left untouched.
|
|
30
|
+
*
|
|
31
|
+
* Only *present* values are checked — optional paths that the caller omitted
|
|
32
|
+
* (and the handler will default) are skipped so behaviour is unchanged for
|
|
33
|
+
* benign callers.
|
|
34
|
+
*/
|
|
35
|
+
export declare function sanitizeToolPaths(toolName: string, args: unknown): void;
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Central path-argument sanitizer for MCP tool handlers.
|
|
3
|
+
*
|
|
4
|
+
* The threat (already documented in ffmpeg-safety.ts) is real: every tool
|
|
5
|
+
* that shells out to ffmpeg/ffprobe passes user-supplied path strings as
|
|
6
|
+
* positional arguments. ffmpeg treats any argument that begins with `-` as
|
|
7
|
+
* a *flag*, not a filename — so a caller (or a confused LLM following an
|
|
8
|
+
* injected instruction) that sets `outputPath` to `-y`, `-f`, or
|
|
9
|
+
* `-protocol_whitelist` can rewrite the command instead of naming a file.
|
|
10
|
+
* Strings containing NUL bytes are equally dangerous (C-string truncation).
|
|
11
|
+
*
|
|
12
|
+
* `validateFfmpegPath` was written precisely to stop this, but until now it
|
|
13
|
+
* was wired into a single engine (narrated-video). The engines validate
|
|
14
|
+
* *input* existence (`assertExists`) which incidentally blocks some flag
|
|
15
|
+
* inputs, but they never check *output* paths — and several inputs flow
|
|
16
|
+
* through `fs.copyFileSync` / the concat demuxer without an existence check
|
|
17
|
+
* at all. The defense therefore had a wide bypass.
|
|
18
|
+
*
|
|
19
|
+
* This module closes the gap at the handler boundary — the one place every
|
|
20
|
+
* untrusted MCP argument enters — so no engine logic (filter graph building,
|
|
21
|
+
* arg ordering) has to change. Each tool declares which argument keys are
|
|
22
|
+
* path-like; nested arrays of paths (concat clips, audio-mixer tracks) are
|
|
23
|
+
* walked too.
|
|
24
|
+
*/
|
|
25
|
+
import { validateFfmpegPath } from './ffmpeg-safety.js';
|
|
26
|
+
/**
|
|
27
|
+
* Per-tool path-field registry. Only ffmpeg/ffprobe-shelling tools are listed
|
|
28
|
+
* here — tools that merely write via fs/Playwright (generate_speech,
|
|
29
|
+
* screenshot_element) are handled separately because a leading-`-` there is a
|
|
30
|
+
* file-write, not flag injection. Keep this in sync with the handler args.
|
|
31
|
+
*/
|
|
32
|
+
const TOOL_PATH_FIELDS = {
|
|
33
|
+
// post-production
|
|
34
|
+
add_background_music: { scalar: ['videoPath', 'musicPath', 'outputPath'] },
|
|
35
|
+
concatenate_videos: { scalar: ['outputPath'], objectArrayPath: ['clips'] },
|
|
36
|
+
generate_intro: { scalar: ['outputPath'] },
|
|
37
|
+
convert_social_format: { scalar: ['inputPath', 'outputPath'] },
|
|
38
|
+
convert_all_social_formats: { scalar: ['inputPath', 'outputDir'] },
|
|
39
|
+
add_text_overlay: { scalar: ['inputPath', 'outputPath'] },
|
|
40
|
+
// editing
|
|
41
|
+
adjust_video_speed: { scalar: ['inputPath', 'outputPath'] },
|
|
42
|
+
apply_color_grade: { scalar: ['inputPath', 'outputPath'] },
|
|
43
|
+
apply_video_effect: { scalar: ['inputPath', 'outputPath'] },
|
|
44
|
+
crop_video: { scalar: ['inputPath', 'outputPath'] },
|
|
45
|
+
reverse_clip: { scalar: ['inputPath', 'outputPath'] },
|
|
46
|
+
extract_audio: { scalar: ['inputPath', 'outputPath'] },
|
|
47
|
+
burn_subtitles: { scalar: ['inputPath', 'outputPath', 'subtitlePath'] },
|
|
48
|
+
auto_caption: { scalar: ['inputPath', 'outputPath'] },
|
|
49
|
+
add_keyframe_animation: { scalar: ['inputPath', 'outputPath'] },
|
|
50
|
+
compose_picture_in_pip: { scalar: ['mainVideo', 'overlayVideo', 'outputPath'] },
|
|
51
|
+
add_audio_ducking: { scalar: ['inputPath', 'outputPath'] },
|
|
52
|
+
// capcut-tier
|
|
53
|
+
apply_lut_preset: { scalar: ['inputPath', 'outputPath'] },
|
|
54
|
+
apply_voice_effect: { scalar: ['inputPath', 'outputPath'] },
|
|
55
|
+
apply_chroma_key: { scalar: ['inputPath', 'outputPath'], pathOrHex: ['background'] },
|
|
56
|
+
sync_to_beat: { scalar: ['audioPath', 'outputPath'], stringArray: ['clips'] },
|
|
57
|
+
animate_text: { scalar: ['inputPath', 'outputPath'] },
|
|
58
|
+
mix_audio_tracks: { scalar: ['outputPath'], objectArrayPath: ['tracks'] },
|
|
59
|
+
render_template: { scalar: ['outputPath', 'musicPath'], recordValues: ['clips'] },
|
|
60
|
+
// tts (narrated video also shells out via the recording + concat pipeline)
|
|
61
|
+
create_narrated_video: { scalar: ['outputPath', 'backgroundMusicPath'] },
|
|
62
|
+
};
|
|
63
|
+
const HEX_COLOR = /^(?:#|0x)?[0-9a-fA-F]{6}$/;
|
|
64
|
+
/** A value is "present" for validation if it is a non-empty string. */
|
|
65
|
+
function isPresentString(v) {
|
|
66
|
+
return typeof v === 'string' && v.length > 0;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Validate every path-like argument for the named tool. Throws the same
|
|
70
|
+
* error class `validateFfmpegPath` throws (TypeError / Error) on the first
|
|
71
|
+
* offending value, which the handler's try/catch turns into a structured
|
|
72
|
+
* tool error. Tools not in the registry are left untouched.
|
|
73
|
+
*
|
|
74
|
+
* Only *present* values are checked — optional paths that the caller omitted
|
|
75
|
+
* (and the handler will default) are skipped so behaviour is unchanged for
|
|
76
|
+
* benign callers.
|
|
77
|
+
*/
|
|
78
|
+
export function sanitizeToolPaths(toolName, args) {
|
|
79
|
+
const spec = TOOL_PATH_FIELDS[toolName];
|
|
80
|
+
if (!spec)
|
|
81
|
+
return;
|
|
82
|
+
if (typeof args !== 'object' || args === null)
|
|
83
|
+
return;
|
|
84
|
+
const record = args;
|
|
85
|
+
for (const key of spec.scalar ?? []) {
|
|
86
|
+
const value = record[key];
|
|
87
|
+
if (isPresentString(value))
|
|
88
|
+
validateFfmpegPath(value, key);
|
|
89
|
+
}
|
|
90
|
+
for (const key of spec.stringArray ?? []) {
|
|
91
|
+
const value = record[key];
|
|
92
|
+
if (!Array.isArray(value))
|
|
93
|
+
continue;
|
|
94
|
+
value.forEach((entry, i) => {
|
|
95
|
+
if (isPresentString(entry))
|
|
96
|
+
validateFfmpegPath(entry, `${key}[${i}]`);
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
for (const key of spec.objectArrayPath ?? []) {
|
|
100
|
+
const value = record[key];
|
|
101
|
+
if (!Array.isArray(value))
|
|
102
|
+
continue;
|
|
103
|
+
value.forEach((entry, i) => {
|
|
104
|
+
if (entry && typeof entry === 'object' && 'path' in entry) {
|
|
105
|
+
const p = entry.path;
|
|
106
|
+
if (isPresentString(p))
|
|
107
|
+
validateFfmpegPath(p, `${key}[${i}].path`);
|
|
108
|
+
}
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
for (const key of spec.recordValues ?? []) {
|
|
112
|
+
const value = record[key];
|
|
113
|
+
if (!value || typeof value !== 'object' || Array.isArray(value))
|
|
114
|
+
continue;
|
|
115
|
+
for (const [slot, p] of Object.entries(value)) {
|
|
116
|
+
if (isPresentString(p))
|
|
117
|
+
validateFfmpegPath(p, `${key}.${slot}`);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
for (const key of spec.pathOrHex ?? []) {
|
|
121
|
+
const value = record[key];
|
|
122
|
+
// A bare 6-digit hex colour (e.g. "00FF00") is a legitimate non-path
|
|
123
|
+
// value for chroma-key backgrounds — skip those. Anything else is treated
|
|
124
|
+
// as a path and must pass the flag-injection / NUL-byte check.
|
|
125
|
+
if (isPresentString(value) && !HEX_COLOR.test(value)) {
|
|
126
|
+
validateFfmpegPath(value, key);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
//# sourceMappingURL=sanitize-tool-paths.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sanitize-tool-paths.js","sourceRoot":"","sources":["../../src/lib/sanitize-tool-paths.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|