REPRO-2026-00197: Verified Repro With Script Download
REPRO-2026-00197: TaskingAI web reader plugin SSRF via /v1/execute
REPRO-2026-00197 is verified against taskingai · github. Affected versions: commit f0092d6b2dd82e98e188e0b9849fdd4c7230dd98. Vulnerability class: SSRF. This high reproduction includes runnable sandbox proof, artifacts, and a plain-text agent view under REPRO-2026-00197.
What Is REPRO-2026-00197?
REPRO-2026-00197 reproduces a high-severity server-side request forgery (CWE-918) in the TaskingAI Plugin server's web_reader/read_web_page plugin, reachable through the documented /v1/execute API.
REPRO-2026-00197 Severity
REPRO-2026-00197 is rated high severity.
High — serious impact or readily exploitable. Prioritize remediation.
Affected taskingai Versions
taskingai · github versions commit f0092d6b2dd82e98e188e0b9849fdd4c7230dd98 are affected.
How to Reproduce REPRO-2026-00197
pruva-verify REPRO-2026-00197 curl -O https://pruva.dev/api/v1/reproductions/REPRO-2026-00197/artifacts/bundle/repro/reproduction_steps.sh && chmod +x reproduction_steps.sh && ./reproduction_steps.sh Proof of Reproduction for REPRO-2026-00197
- reached the target end-to-end
- on the real production code path
- high confidence
- the upstream fix blocks the same trigger
input_params.url in POST /v1/execute with bundle_id=web_reader and plugin_id=read_web_page
- POST /v1/execute
- read_web_page plugin
- aiohttp.ClientSession().get(url=attacker_url)
How the agent worked
Root Cause and Exploit Chain for REPRO-2026-00197
The TaskingAI Plugin server exposes an unauthenticated /v1/execute API endpoint that allows callers to invoke bundled plugins with arbitrary user-supplied input parameters. The web_reader/read_web_page plugin accepts a url parameter with no validation of scheme, hostname, IP range, or private network restrictions. When invoked through the API, the plugin server performs an outbound HTTP GET request to the attacker-supplied URL using aiohttp.ClientSession().get(url=url, proxy=CONFIG.PROXY) and returns the fetched content in the API response. This enables server-side request forgery (SSRF) against internal services accessible from the plugin container's network.
- Package/component affected: TaskingAI Plugin server (
taskingai/taskingai-plugin:latestDocker image, version approximately v0.3.5-1) - Affected versions: At least commit
f0092d6b2dd82e98e188e0b9849fdd4c7230dd98and the currentlatestDocker image - Risk level: High
- Consequences: An attacker who can reach the plugin service can force it to make HTTP requests to internal addresses, potentially exfiltrating metadata, accessing internal APIs, or scanning the container network.
Root Cause
The root cause is missing input validation on the url parameter in the read_web_page plugin and the /v1/execute route that passes arbitrary input_params directly to the plugin implementation.
Code path:
plugin/app/routes/execute.py:117-123strips onlyNonevalues and invokes the selected plugin with user-suppliedinput_params.plugin/bundles/web_reader/plugins/read_web_page/plugin.py:9-13readsurlfromplugin_input.input_paramsand passes it directly toaiohttp.ClientSession().get(url=url, proxy=CONFIG.PROXY)without any allow-list, deny-list, or private-IP validation.- The plugin schema at
plugin/bundles/web_reader/plugins/read_web_page/plugin_schema.yml:3-8declaresurlas a required plain string with no URL restrictions.
No fix commit is known at this time; the ticket references a vulnerable commit but does not name a patched version.
Reproduction Steps
The reproduction script is bundle/repro/reproduction_steps.sh. It performs the following steps:
- Creates a dedicated Docker bridge network (
taskingai-ssrf-net). - Starts a Python HTTP listener container on the network that logs every incoming request and returns a unique marker string (
PRUVA_TASKINGAI_SSRF_MARKER). - Starts the official
taskingai/taskingai-plugin:latestDocker container on the same network with required environment variables (MODE=PROD,OBJECT_STORAGE_TYPE=local,HOST_URL, etc.). - Waits for the plugin service to become healthy (checks
/v1/executefrom a client container on the same network). - Sends a
POST /v1/executerequest to the plugin withbundle_id=web_reader,plugin_id=read_web_page, andinput_params.urlpointing to the internal listener (http://taskingai-listener:9000/internal/proof). - Verifies both required signals:
- The listener logs show
REQUEST_RECEIVED: /internal/proof. - The plugin API response contains
"result":"PRUVA_TASKINGAI_SSRF_MARKER".
- The listener logs show
Expected evidence of reproduction
bundle/logs/listener.logshould contain the listener access log showing the request.bundle/logs/execute_response.jsonshould contain the JSON response from the plugin with the marker string insidedata.data.result.
Evidence
- Log file locations:
bundle/logs/execute_response.json— the/v1/executeresponse from the plugin serverbundle/logs/listener.log— the listener access log showing the SSRF requestbundle/logs/plugin_startup.log— plugin startup log confirming the service loaded all bundles successfully
- Key excerpts:
- Response:
{"status":"success","data":{"status":200,"data":{"result":"PRUVA_TASKINGAI_SSRF_MARKER"}}} - Listener log:
REQUEST_RECEIVED: /internal/prooffollowed by"GET /internal/proof HTTP/1.1" 200 -
- Response:
- Environment details: Docker-based reproduction using the official
taskingai/taskingai-plugin:latestimage (digestsha256:d38f821e1585be4b56e827b81fdfcc1f42958cf063e8fe6e1f95defb8b1a1159) on a Linux host with Docker bridge networking.
Recommendations / Next Steps
- Input validation: Add strict URL validation in the
read_web_pageplugin (or in the/v1/executeroute) before performing the HTTP request. Reject URLs with private IP ranges (e.g.,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16,127.0.0.0/8,169.254.0.0/16) and loopback hostnames (localhost). - URL scheme allow-list: Restrict allowed schemes to
httpandhttpsonly, and blockfile://,ftp://, etc. - Redirect handling: If redirects are followed, validate the destination of each redirect hop to prevent redirect-based SSRF bypasses.
- Network isolation: Run the plugin service in a restricted network segment with egress controls to limit internal exposure.
- Testing: Add unit and integration tests that verify the plugin rejects internal URLs and returns an appropriate error response.
Additional Notes
- Idempotency: The reproduction script is fully idempotent. It creates and cleans up its own Docker network and containers, and it runs successfully every time (confirmed with 2 consecutive successful runs).
- Edge cases: The vulnerability depends only on the
urlparameter reaching theaiohttp.get()call. Any URL string that the container can resolve is exploitable, including DNS names that resolve to internal addresses. - Limitations: The reproduction was performed using Docker networking. In a production deployment, the attacker would need network reachability to the plugin service; the SSRF destination would be any address reachable from the plugin server's network context.
REPRO-2026-00197 Reproduction Transcript
The agent's step-by-step process — every tool call, every handoff, the moment the exploit fired.
Full session Replay every step — scrub the timeline or play it back.
Unknown error
docker ps 2>&1 || echo "Docker not available"{"cwd":"/data/pruva/runs/931492d5-87a9-4c81-a5f4-9d0da4747943","duration_ms":45,"exit_code":0,"stderr":"","stdout":"CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES\n3608f7e5f28e athena-pruva-worker-sandbox:latest \"pruva-worker --back…\" 3 minutes ago Up 3 minutes (healthy) athena-pruva-worker-sandbox\n55236f6a5df6 521ba66fe894 \"/app/pruva-api --po…\" 2 hours ago Up 2 hours 0.0.0.0:4000->4000/tcp, [::]:4000->4000/tcp athena-pruva-api\nfbdb55817c22 athena-gated \"python3 /app/gated_…\" 46 hours ago Up 46 hours 0.0.0.0:8092->8092/tcp, [::]:8092->8092/tcp athena-gated\n8d082d97e33c platform-argus \"argus\" 4 days ago Up 4 days 0.0.0.0:8090->8090/tcp, [::]:8090->8090/tcp athena-argus\n503fb20147f4 postgres:16-alpine \"docker-entrypoint.s…\" 4 days ago Up 4 days (healthy) 0.0.0.0:5434->5432/tcp, [::]:5434->5432/tcp athena-a… [truncated]docker pull taskingai/taskingai-plugin:latest 2>&1 | tail -544e1fad13eb2: Pull complete 73bd558ccdcc: Pull complete Digest: sha256:d38f821e1585be4b56e827b81fdfcc1f42958cf063e8fe6e1f95defb8b1a1159 Status: Downloaded newer image for taskingai/taskingai-plugin:latest docker.io/taskingai/taskingai-plugin:latest
docker run -d --rm -p 8003:8000 --name taskingai-plugin-test taskingai/taskingai-plugin:latest && sleep 5 && curl -s http://127.0.0.1:8003/ || echo "No root response"; curl -s http://127.0.0.1:8003/v1/execute || echo "No execute response"; docker logs taskingai-plugin-test 2>&1 | tail -20; docker stop taskingai-plugin-test 2>/dev/null; docker rm taskingai-plugin-test 2>/dev/nullaf74315f3c77eed4a04c13649b837c011f0a8c82bf2eeb4ab11707ff1e642e47 No root response No execute response Error response from daemon: No such container: taskingai-plugin-test
#!/bin/bash
set -euo pipefail
ROOT="${PRUVA_ROOT:-$(cd "$(dirname "$0")/.." && pwd)}"
LOGS="$ROOT/logs"
REPRO_DIR="$ROOT/repro"
mkdir -p "$LOGS"
mkdir -p "$REPRO_DIR"
# Create a Docker network for isolated communication between containers
NETWORK_NAME="taskingai-ssrf-net"
docker network create "$NETWORK_NAME" 2>/dev/null || true
# Start a simple Python HTTP listener in a container
# It will log requests and return a marker
LISTENER_CONTAINER="taskingai-listener"
PLUGIN_CONTAINER="taskingai-plugin"
docker rm -f "$LISTENER_CONTAINER" "$PLUGIN_CONTAINER" 2>/dev/null || true
chmod +x /data/pruva/runs/931492d5-87a9-4c81-a5f4-9d0da4747943/bundle/repro/reproduction_steps.sh && /data/pruva/runs/931492d5-87a9-4c81-a5f4-9d0da4747943/bundle/repro/reproduction_steps.shSCRIPT QUALITY GUARD BLOCKED `run_shell`: the mid-run judge marked `bundle/repro/reproduction_steps.sh` weak, but the current script has runtime markers and writes `bundle/repro/runtime_manifest.json`. The next successful tool call must be run_shell executing `bundle/repro/reproduction_steps.sh`. Run the script now and use the concrete logs to decide the next revision.
Unknown error
Artifacts and Evidence for REPRO-2026-00197
Scripts, logs, diffs, and output captured during the reproduction.
How to Fix REPRO-2026-00197
FAQ: REPRO-2026-00197
How does the TaskingAI web_reader SSRF work?
Which TaskingAI versions are affected?
How severe is this TaskingAI SSRF?
How can I reproduce this TaskingAI SSRF?
References for REPRO-2026-00197
Authoritative sources for REPRO-2026-00197 — official vulnerability databases and the upstream advisory. Pruva's reproduction verifies the issue firsthand; these are the primary records to corroborate it.