VAITP Dataset

← Back to the dataset

CVE-2026-7466

AgentFlow allows RCE of local Python files via a crafted `pipeline_path`.

  • CVSS 7.7
  • CWE-94
  • Input Validation and Sanitization
  • Local

AgentFlow contains an arbitrary code execution vulnerability that allows attackers to execute local Python pipeline files by supplying a user-controlled pipeline_path parameter to the POST /api/runs and POST /api/runs/validate endpoints. Attackers can induce requests to the local AgentFlow API to load and execute existing Python pipeline files on disk, resulting in code execution in the context of the user running AgentFlow.

CVSS base score
7.7
Published
2026-04-29
OWASP
A08 Software and Data Integrity Failures
Orthogonal defect classification
Checking
Code defect classification
Missing Check
Category
Input Validation and Sanitization
Subcategory
Local File Inclusion (LFI)
Accessibility scope
Local
Impact
Arbitrary Code Execution
Affected component
AgentFlow
Fixed by upgrading
Yes

Solution

Upgrade AgentFlow to version 0.2.2 or later.

Vulnerable code sample

from flask import Flask, request, jsonify

app = Flask(__name__)

def execute_pipeline_from_path(file_path):
    """
    Loads a Python file from the given path and executes its content.
    This is the core of the vulnerability.
    """
    with open(file_path, 'r') as f:
        pipeline_code = f.read()
    
    # The vulnerability: executing code from a user-controlled file path.
    # An attacker could supply a path to any Python file on the server.
    exec(pipeline_code, globals())

@app.route('/api/runs', methods=['POST'])
def create_run():
    """
    Vulnerable endpoint that takes a 'pipeline_path' and executes it.
    """
    data = request.get_json()
    if not data or 'pipeline_path' not in data:
        return jsonify({"error": "pipeline_path is required"}), 400

    pipeline_path = data['pipeline_path']

    try:
        # No validation or sanitization is performed on the pipeline_path.
        execute_pipeline_from_path(pipeline_path)
        return jsonify({"status": "success", "message": f"Pipeline from {pipeline_path} executed."})
    except FileNotFoundError:
        return jsonify({"error": f"File not found: {pipeline_path}"}), 404
    except Exception as e:
        return jsonify({"error": f"An error occurred: {str(e)}"}), 500

@app.route('/api/runs/validate', methods=['POST'])
def validate_run():
    """
    Another vulnerable endpoint that also executes code from 'pipeline_path'.
    """
    data = request.get_json()
    if not data or 'pipeline_path' not in data:
        return jsonify({"error": "pipeline_path is required"}), 400

    pipeline_path = data['pipeline_path']

    try:
        # The same vulnerability exists here.
        execute_pipeline_from_path(pipeline_path)
        return jsonify({"status": "valid", "message": f"Pipeline from {pipeline_path} is valid and was test-executed."})
    except FileNotFoundError:
        return jsonify({"error": f"File not found: {pipeline_path}"}), 404
    except Exception as e:
        return jsonify({"error": f"An error occurred during validation: {str(e)}"}), 500

if __name__ == '__main__':
    # To test this vulnerability:
    # 1. Save a python file, e.g., /tmp/malicious_pipeline.py with content: print("Code executed from malicious file!")
    # 2. Run this Flask application.
    # 3. Send a POST request:
    #    curl -X POST -H "Content-Type: application/json" -d '{"pipeline_path": "/tmp/malicious_pipeline.py"}' http://127.0.0.1:5000/api/runs
    app.run(debug=True)

Patched code sample

import os
from flask import Flask, request, jsonify

# --- The Fix ---
# A predefined, secure base directory where all legitimate pipelines are stored.
# os.path.abspath ensures we have a canonical base path to compare against.
SAFE_PIPELINE_DIRECTORY = os.path.abspath('./allowed_pipelines')
# --- End of Fix ---

# Create the directory if it doesn't exist for the example to run
if not os.path.exists(SAFE_PIPELINE_DIRECTORY):
    os.makedirs(SAFE_PIPELINE_DIRECTORY)
    # Create a dummy pipeline file for a valid test case
    with open(os.path.join(SAFE_PIPELINE_DIRECTORY, 'legit_pipeline.py'), 'w') as f:
        f.write('print("Executing the legitimate pipeline.")\n')

app = Flask(__name__)

@app.route('/api/runs', methods=['POST'])
def run_pipeline():
    data = request.get_json()
    if not data or 'pipeline_path' not in data:
        return jsonify({"error": "pipeline_path is required"}), 400

    pipeline_path = data['pipeline_path']

    # --- The Fix ---
    # 1. Safely join the trusted base directory with the user-provided path.
    #    This prevents the user path from being treated as an absolute path.
    requested_path = os.path.join(SAFE_PIPELINE_DIRECTORY, pipeline_path)

    # 2. Resolve the real, absolute path (e.g., handles '..', symlinks).
    absolute_path = os.path.abspath(requested_path)

    # 3. CRITICAL CHECK: Ensure the resolved absolute path is still inside
    #    the safe base directory. If not, it's a path traversal attempt.
    if not absolute_path.startswith(SAFE_PIPELINE_DIRECTORY):
        return jsonify({"error": "Path traversal attempt detected"}), 403
    # --- End of Fix ---

    if not os.path.isfile(absolute_path):
        return jsonify({"error": "Pipeline file not found"}), 404

    try:
        # In a real application, this would use a secure method to execute
        # the pipeline. For this example, we simulate execution.
        print(f"--- SIMULATING EXECUTION OF: {absolute_path} ---")
        with open(absolute_path, 'r') as f:
            code = f.read()
            # In a real scenario, this might be exec(), importlib, or a subprocess
            # call. The security lies in the path validation above.
            exec(code)
        print("--- SIMULATION COMPLETE ---")
        
        return jsonify({"status": "success", "executed_pipeline": absolute_path}), 200
    except Exception as e:
        return jsonify({"error": f"Failed to execute pipeline: {str(e)}"}), 500

if __name__ == '__main__':
    print(f"Allowed pipelines must be in: {SAFE_PIPELINE_DIRECTORY}")
    print("Example valid request:")
    print("curl -X POST -H \"Content-Type: application/json\" -d '{\"pipeline_path\": \"legit_pipeline.py\"}' http://127.0.0.1:5000/api/runs")
    print("Example invalid (path traversal) request:")
    print("curl -X POST -H \"Content-Type: application/json\" -d '{\"pipeline_path\": \"../../../../etc/passwd\"}' http://127.0.0.1:5000/api/runs")
    app.run(port=5000)

Payload

{
  "pipeline_path": "/tmp/malicious_pipeline.py"
}

Cite this entry

@misc{vaitp:cve20267466,
  title        = {{AgentFlow allows RCE of local Python files via a crafted `pipeline_path`.}},
  author       = {Bogaerts, Fr\'ed\'eric and Ivaki, Naghmeh and Fonseca, Jos\'e},
  year         = {2026},
  note         = {VAITP Python Vulnerability Dataset, entry CVE-2026-7466},
  howpublished = {\url{https://netpack.pt/vaitp/vulnerability/CVE-2026-7466/}}
}
Introducing the "VAITP dataset": a specialized repository of Python vulnerabilities and patches, meticulously compiled for the use of the security research community. As Python's prominence grows, understanding and addressing potential security vulnerabilities become crucial. Crafted by and for the cybersecurity community, this dataset offers a valuable resource for researchers, analysts, and developers to analyze and mitigate the security risks associated with Python. Through the comprehensive exploration of vulnerabilities and corresponding patches, the VAITP dataset fosters a safer and more resilient Python ecosystem, encouraging collaborative advancements in programming security.

The supreme art of war is to subdue the enemy without fighting.

Sun Tzu – “The Art of War”

:: Shaping the future through research and ingenuity ::