{"record":{"id":"3a777a708fc943aa","repo":"microsoft/autogen","slug":"audio-output-path-must-be-within-the-current-worki","errorCode":null,"errorMessage":"audio_output_path must be within the current working directory.","messagePattern":"audio_output_path must be within the current working directory\\.","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"python/packages/autogen-ext/src/autogen_ext/agents/video_surfer/tools.py","lineNumber":38,"sourceCode":"    :param audio_output_path: Path to save the extracted audio file (must end with .mp3).\n    :return: Confirmation message with the path to the saved audio file.\n    \"\"\"\n    import os\n    import re\n\n    # Reject URLs to prevent SSRF via ffmpeg\n    if re.match(r\"^[a-zA-Z][a-zA-Z0-9+\\-.]*://\", video_path):\n        raise ValueError(\"video_path must be a local file path, not a URL.\")\n\n    # Enforce .mp3 extension to prevent writing arbitrary file types\n    if not audio_output_path.lower().endswith(\".mp3\"):\n        raise ValueError(\"audio_output_path must end with .mp3.\")\n\n    # Prevent path traversal — output must stay within the current working directory\n    cwd = os.path.realpath(os.getcwd())\n    output_real = os.path.realpath(audio_output_path)\n    if not output_real.startswith(cwd + os.sep) and output_real != cwd:\n        raise ValueError(\"audio_output_path must be within the current working directory.\")\n\n    (ffmpeg.input(video_path).output(audio_output_path, format=\"mp3\").run(quiet=True, overwrite_output=True))  # type: ignore\n    return f\"Audio extracted and saved to {audio_output_path}.\"\n\n\ndef transcribe_audio_with_timestamps(audio_path: str) -> str:\n    \"\"\"\n    Transcribes the audio file with timestamps using the Whisper model.\n\n    :param audio_path: Path to the audio file.\n    :return: Transcription with timestamps.\n    \"\"\"\n    model = whisper.load_model(\"base\")  # type: ignore\n    result: Dict[str, Any] = model.transcribe(audio_path, task=\"transcribe\", language=\"en\", verbose=False)  # type: ignore\n\n    segments: List[Dict[str, Any]] = result[\"segments\"]\n    transcription_with_timestamps = \"\"\n","sourceCodeStart":20,"sourceCodeEnd":56,"githubUrl":"https://github.com/microsoft/autogen/blob/027ecf0a379bcc1d09956d46d12d44a3ad9cee14/python/packages/autogen-ext/src/autogen_ext/agents/video_surfer/tools.py#L20-L56","documentation":"Thrown by extract_audio() in autogen_ext.agents.video_surfer.tools when the resolved (realpath) audio_output_path does not lie inside the process's current working directory. This is a deliberate security guard: because the path is handed to ffmpeg, allowing absolute paths or ../ segments would let an LLM-directed call write .mp3 files anywhere on disk. The check uses os.path.realpath on both sides, so symlinks pointing outside the cwd are also rejected.","triggerScenarios":"Calling extract_audio(video_path, audio_output_path) with an absolute output path like /tmp/out.mp3, a path with traversal segments like ../shared/audio.mp3, a relative path that escapes the cwd after realpath resolution, or a symlink inside the cwd that resolves to a directory outside it.","commonSituations":"Agents running from a different working directory than expected (e.g. notebook kernel cwd vs. notebook location), reusing paths returned by other tools that are absolute, or a host process whose os.getcwd() is a system directory. Also triggered when the caller chdir'd after computing the output path.","solutions":["Pass a relative output path, e.g. extract_audio('video.mp4', 'audio.mp3'), so it resolves under os.getcwd()","Build the path from the cwd explicitly: os.path.join(os.getcwd(), 'audio.mp3')","If a custom directory is required, os.chdir() into it before calling extract_audio","Verify with os.path.realpath(audio_output_path).startswith(os.path.realpath(os.getcwd()) + os.sep) before calling"],"exampleFix":"// before\nextract_audio('input/video.mp4', '/tmp/episode_audio.mp3')  # ValueError\n\n// after\nimport os\nextract_audio('input/video.mp4', os.path.join(os.getcwd(), 'episode_audio.mp3'))","handlingStrategy":"validation","validationCode":"import os\ndef safe_output_path(p: str) -> bool:\n    cwd = os.path.realpath(os.getcwd())\n    out = os.path.realpath(p)\n    return out == cwd or out.startswith(cwd + os.sep) and out.lower().endswith('.mp3')","typeGuard":"def is_cwd_relative_mp3(path: str) -> bool:\n    import os\n    real = os.path.realpath(path)\n    return real.lower().endswith('.mp3') and real.startswith(os.path.realpath(os.getcwd()) + os.sep)","tryCatchPattern":"try:\n    result = extract_audio(video, out)\nexcept ValueError as e:\n    # e.message names which rule failed (url / extension / cwd); fix the path accordingly\n    out = os.path.join(os.getcwd(), 'audio.mp3')\n    result = extract_audio(video, out)","preventionTips":["Always build output paths with os.path.join(os.getcwd(), name)","Never pass absolute temp dirs like /tmp to extract_audio","Keep the .mp3 extension; the guard rejects other suffixes first"],"tags":["security","path-traversal","ffmpeg","video-surfer","validation"],"backgroundTag":null,"analyzedSha":"027ecf0a379bcc1d09956d46d12d44a3ad9cee14","analyzedAt":"2026-08-15T03:38:00.719Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}