# ARGUS-TASKINGAI-WEB-READER-SSRF-20260625

## Summary

TaskingAI Plugin web_reader can fetch attacker-controlled internal URLs through /v1/execute

## Description

## Summary

Argus scanned a fresh real GitHub target with Arbor enabled. Arbor flagged the public plugin execution boundary for missing input validation at `plugin/app/routes/execute.py:117`, and Metis flagged a high-confidence SSRF in the `web_reader/read_web_page` plugin. Manual source review found that TaskingAI Plugin exposes a documented Docker-run `/v1/execute` API, accepts user-supplied `bundle_id`, `plugin_id`, and `input_params`, validates `read_web_page.url` only as a required string, and then performs an outbound `aiohttp` request to that URL from the plugin server.

This ticket asks Pruva to validate only the concrete running-service SSRF behavior. Do not count this as confirmed unless the published TaskingAI Plugin Docker service receives a `/v1/execute` request and an attacker-controlled local listener captures the resulting server-side HTTP request.

## Tested Source

- Repository: https://github.com/TaskingAI/TaskingAI
- Commit: f0092d6b2dd82e98e188e0b9849fdd4c7230dd98
- Public Docker path documented by the repo: `docker run -d -p 8003:8000 taskingai/taskingai-plugin:latest`
- Prior-art check: GitHub security advisories API returned no public advisories for TaskingAI/TaskingAI; GitHub issue search found no repo-local `read_web_page` SSRF report.

## Code Path

1. `plugin/README.md:38-48` documents running `taskingai/taskingai-plugin:latest` and exposing the service on host port 8003.
2. `plugin/README.md:83-95` documents direct unauthenticated `POST http://127.0.0.1:8003/v1/execute` usage with JSON body fields `bundle_id`, `plugin_id`, `input_params`, and `credentials`.
3. `plugin/app/routes/execute.py:21-52` defines `RunToolRequest` with arbitrary `input_params` and credentials.
4. `plugin/app/routes/execute.py:64-79` validates that the plugin exists and calls `plugin.validate_input(data.input_params)`.
5. `plugin/bundles/web_reader/plugins/read_web_page/plugin_schema.yml:3-8` declares `url` as a required plain string, with no URL scheme, hostname, IP range, redirect, or private-address restriction.
6. `plugin/app/routes/execute.py:117-123` strips only `None` values and invokes the selected plugin with the user-supplied `input_params`.
7. `plugin/bundles/web_reader/plugins/read_web_page/plugin.py:9-13` reads `url` from `plugin_input.input_params` and calls `aiohttp.ClientSession().get(url=url, proxy=CONFIG.PROXY)`.
8. `plugin/bundles/web_reader/plugins/read_web_page/plugin.py:20-24` returns the fetched page text in the API response.

## Reproduction Goal for Pruva

Create an end-to-end reproduction with Docker and local-only infrastructure:

1. Start an attacker-controlled HTTP listener on a Docker network. The listener should record requests and return a small HTML or text body containing a unique marker, for example `PRUVA_TASKINGAI_SSRF_MARKER`.
2. Start TaskingAI Plugin using the repository-documented image, preferably `taskingai/taskingai-plugin:latest`. If the repo compose-pinned image is required for reproducibility, test `taskingai/taskingai-plugin:v0.2.10` as well and record the exact image digest.
3. Send a request to the running plugin API:

```http
POST /v1/execute HTTP/1.1
Host: 127.0.0.1:8003
Content-Type: application/json

{
  "bundle_id": "web_reader",
  "plugin_id": "read_web_page",
  "input_params": {
    "url": "http://<listener-host>:<listener-port>/internal/proof"
  },
  "credentials": {}
}
```

4. Confirm both required signals:
   - The local listener captures a request from the TaskingAI Plugin container for `/internal/proof`.
   - The `/v1/execute` response contains the marker text returned by the listener.

Use only local Docker networking for the SSRF destination. Do not contact cloud metadata endpoints or third-party internal services.

## Expected Impact

Server-side request forgery. A caller who can reach the TaskingAI Plugin `/v1/execute` API can cause the plugin server to initiate HTTP requests to attacker-selected URLs through the `web_reader/read_web_page` plugin. In self-hosted deployments where the plugin service has access to internal networks or local administrative HTTP services, this can be used for internal network probing and interaction with unauthenticated internal HTTP endpoints. The reproduction should prove the capability with a local listener only.

## Metadata

- Product: TaskingAI Plugin
- Severity: high
- Status: open
