Spaces:
Sleeping
Sleeping
Create safety.py
Browse files
safety.py
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import shlex
|
| 2 |
+
from pathlib import Path
|
| 3 |
+
|
| 4 |
+
DISALLOWED_TOKENS = [";", "&&", "||", "|", ">", ">>", "<", "`", "$(", "${"]
|
| 5 |
+
DISALLOWED_STRINGS = [
|
| 6 |
+
"pipe:", "concat:", "subfile:", "crypto:", "data:", "ftp:", "gopher:", "rtmp:", "rtsp:",
|
| 7 |
+
"-filter_script", "-f tee", "tee:"
|
| 8 |
+
]
|
| 9 |
+
|
| 10 |
+
def parse_ffmpeg_cmd(cmd: str) -> list[str]:
|
| 11 |
+
if not cmd or not cmd.strip():
|
| 12 |
+
raise ValueError("Empty ffmpeg command.")
|
| 13 |
+
|
| 14 |
+
for t in DISALLOWED_TOKENS:
|
| 15 |
+
if t in cmd:
|
| 16 |
+
raise ValueError(f"Unsafe token found: {t}")
|
| 17 |
+
|
| 18 |
+
lowered = cmd.lower()
|
| 19 |
+
for s in DISALLOWED_STRINGS:
|
| 20 |
+
if s in lowered:
|
| 21 |
+
raise ValueError(f"Disallowed ffmpeg feature/protocol: {s}")
|
| 22 |
+
|
| 23 |
+
parts = shlex.split(cmd)
|
| 24 |
+
if not parts or parts[0] != "ffmpeg":
|
| 25 |
+
raise ValueError("Command must start with 'ffmpeg'.")
|
| 26 |
+
return parts
|
| 27 |
+
|
| 28 |
+
def enforce_local_inputs(parts: list[str], job_dir: Path):
|
| 29 |
+
input_dir = (job_dir / "input").resolve()
|
| 30 |
+
i = 0
|
| 31 |
+
while i < len(parts):
|
| 32 |
+
if parts[i] == "-i" and i + 1 < len(parts):
|
| 33 |
+
p = parts[i + 1]
|
| 34 |
+
if "://" in p:
|
| 35 |
+
raise ValueError("FFmpeg must not fetch URLs. Provide URLs via file_urls (server downloads them).")
|
| 36 |
+
|
| 37 |
+
abs_p = (job_dir / p).resolve() if not Path(p).is_absolute() else Path(p).resolve()
|
| 38 |
+
if not str(abs_p).startswith(str(input_dir)):
|
| 39 |
+
raise ValueError("FFmpeg inputs must be within ./input/")
|
| 40 |
+
i += 2
|
| 41 |
+
else:
|
| 42 |
+
i += 1
|
| 43 |
+
|
| 44 |
+
def make_exec_args(parts: list[str], forced_output: Path) -> list[str]:
|
| 45 |
+
# ensure no interaction / overwrite
|
| 46 |
+
if "-nostdin" not in parts:
|
| 47 |
+
parts.insert(1, "-nostdin")
|
| 48 |
+
if "-y" not in parts:
|
| 49 |
+
parts.insert(1, "-y")
|
| 50 |
+
|
| 51 |
+
# remove last arg if it looks like output
|
| 52 |
+
if len(parts) > 1 and not parts[-1].startswith("-"):
|
| 53 |
+
parts = parts[:-1]
|
| 54 |
+
|
| 55 |
+
parts.append(str(forced_output))
|
| 56 |
+
return parts
|