VDB
KO
HIGH 7.5

GHSA-j7qx-p75m-wp7g

PraisonAI dynamic-context artifact tools read arbitrary host files outside artifact storage

Details

# PraisonAI dynamic-context artifact tools read arbitrary host files outside artifact storage

## Summary

PraisonAI's Dynamic Context Discovery feature exposes artifact helper tools through `ctx.get_tools()`:

```python ctx = setup_dynamic_context()

agent = Agent( instructions="You are a data analyst.", tools=ctx.get_tools(), hooks=[ctx.get_middleware()], ) ```

The official documentation describes these helpers as a way for the agent to explore large tool-output artifacts that were queued by the middleware:

- large tool outputs are saved as artifacts; - the agent receives compact artifact references; and - the agent uses `artifact_tail` and `artifact_grep` to explore that data.

The implemented artifact tools do not enforce that the supplied `artifact_path` is an artifact created by the configured store or that it lives under the configured artifact base directory. Instead, `artifact_head`, `artifact_tail`, `artifact_grep`, and `artifact_chunk` wrap the caller-supplied path directly into an `ArtifactRef` and then read it from the host filesystem.

As a result, any prompt/user/tool-caller that can influence those tool arguments can read files readable by the PraisonAI process, such as project `.env` files, cloud credentials, SSH keys, source files, or other local data.

## Affected Product

- Repository: `MervinPraison/PraisonAI` - Ecosystem: `pip` - Package: `praisonai` - Component: Dynamic Context Discovery artifact tools - Current source path: `src/praisonai/praisonai/context/queue.py` - Artifact store path: `src/praisonai/praisonai/context/artifact_store.py` - Latest PyPI version validated: `4.6.58` - Current `origin/main` validated: `1ad58ca02975ff1398efeda694ea2ab78f20cf3e` - Current `origin/main` tag validated: `v4.6.58`

Suggested affected range:

```text pip:praisonai >= 3.8.1, <= 4.6.58 ```

Representative local sweep:

- `3.8.1`: vulnerable - `4.0.0`: vulnerable - `4.5.113`: vulnerable - `4.6.33`: vulnerable - `4.6.34`: vulnerable - `4.6.40`: vulnerable - `4.6.50`: vulnerable - `4.6.58`: vulnerable

## Root Cause

`create_artifact_tools()` creates an artifact store bound to `base_dir`, but the read tools do not use `base_dir` for containment.

For example, `artifact_head()` accepts `artifact_path` and immediately creates an `ArtifactRef` with that path:

```python def artifact_head(artifact_path: str, lines: int = 50) -> str: ref = ArtifactRef(path=artifact_path, summary="", size_bytes=0) try: return artifact_store.head(ref, lines=lines) except FileNotFoundError: return f"Error: Artifact not found: {artifact_path}" ```

`artifact_tail()`, `artifact_grep()`, and `artifact_chunk()` have the same pattern. They trust the caller-supplied path rather than resolving it through an artifact identifier, store lookup, manifest, or base-directory containment check.

The store methods then read that path directly:

```python def head(self, ref: ArtifactRef, lines: int = 50) -> str: file_path = Path(ref.path) if not file_path.exists(): raise FileNotFoundError(f"Artifact not found: {ref.path}")

result_lines = [] with open(file_path, "r", encoding="utf-8", errors="replace") as f: ... ```

There is no check equivalent to:

```python resolved = Path(ref.path).resolve() base = self.base_dir.resolve() resolved.relative_to(base) ```

There is also no check that the file has a valid `.meta` sidecar or appears in `artifact_list()`.

## Local PoV

Run against the latest PyPI package:

```bash uv run --with 'praisonai==4.6.58' \ python poc/pov_prai_cand_026_artifact_tools_arbitrary_file_read.py --json ```

The PoV:

1. Creates a temporary artifact base directory. 2. Creates a separate `outside-secret.txt` file outside that base directory. 3. Stores one legitimate artifact through `FileSystemArtifactStore.store()`. 4. Calls `artifact_head()` on the legitimate artifact as a positive control. 5. Calls `artifact_head()`, `artifact_grep()`, and `artifact_chunk()` on the outside file path. 6. Confirms `artifact_list()` does not list the outside file.

Observed output summary from `evidence/pov-pypi-4.6.58.json`:

```json { "package": "praisonai", "package_version": "4.6.58", "controls": { "outside_file_not_listed": true, "outside_file_outside_base_dir": true, "valid_artifact_read_works": true }, "outside_head": "PRAI-CAND-026-OUTSIDE-ARTIFACT-SECRET", "outside_grep": "Found 1 matches:\\n\\n--- Line 1 ---\\n> PRAI-CAND-026-OUTSIDE-ARTIFACT-SECRET\\n second line", "outside_chunk": "PRAI-CAND-026-OUTSIDE-ARTIFACT-SECRET", "outside_file_listed_by_artifact_list": false, "vulnerable": true } ```

The PoV was rerun successfully after a fresh `origin/main` fetch; see `evidence/pov-pypi-4.6.58-rerun.json`.

The PoV is local-only. It does not start a server, contact a third-party target, or use real credentials.

## Why This Is Not Intended Behavior

This report does not claim that every file-reading tool is automatically a vulnerability. The issue is narrower: tools documented and named as artifact helpers accept arbitrary host file paths.

The controls show the intended boundary:

- a valid artifact stored under `base_dir` is readable; - an outside file is not returned by `artifact_list()`; - the outside file is outside `base_dir`; and - the read helpers still disclose the outside file when handed its absolute path.

PraisonAI's own context-security documentation recommends relative paths and reviewing ignore rules to avoid sensitive-file exposure. Those controls are bypassed when artifact tools can be pointed directly at any readable host path.

## Impact

If a PraisonAI application exposes an agent with `ctx.get_tools()` to untrusted or lower-trust prompts, the lower-trust caller can request artifact tools against arbitrary local paths. This can disclose sensitive host files readable by the PraisonAI process, including:

- project `.env` files; - cloud or service credentials; - SSH keys; - local application configuration; - source files and private data; and - terminal/history artifacts from other runs if the path is known or guessed.

The impact is confidentiality-only in the tested surface. Integrity and availability are not claimed for this report.

## Duplicate Posture

I checked visible PraisonAI advisories and local prior PraisonAI submissions. This is distinct from nearby file-read/file-write issues:

- `GHSA-9cr9-25q5-8prj` / `CVE-2026-47394` covers MCP CLI `workflow.show`, `workflow.validate`, and `deploy.validate` path handling. This report covers Dynamic Context Discovery artifact tools in `context/queue.py`. - `GHSA-hvhp-v2gc-268q` / `CVE-2026-47397` covers `write_file` arbitrary file write when `workspace=None`. This report is a read-only disclosure issue in artifact helper tools. - Public recipe registry path traversal advisories cover recipe publish/pull storage and extraction. This report does not involve the recipe registry. - Local prior submissions in this harness do not cover `artifact_head`, `artifact_tail`, `artifact_grep`, `artifact_chunk`, or `FileSystemArtifactStore` path containment.

## Severity

Suggested severity: High.

Suggested CVSS v3.1:

Rationale:

- `AV`: applies when an application exposes a PraisonAI agent over a network chat/API surface, which is a documented PraisonAI deployment pattern. - `AC`: no race, special environment, or complex path manipulation is required; an absolute readable path is sufficient. - `PR`: an unauthenticated or public-facing agent endpoint can be exploited without an account. Deployments that require authenticated chat/API access may score this as `PR:L`. - `UI`: the attacker directly supplies the prompt/tool argument to the exposed agent surface. - `C`: arbitrary readable host files can contain secrets or private data. - `I/A`: this report demonstrates read-only disclosure.

## Remediation

Do not let artifact tools open arbitrary paths. Prefer stable artifact IDs over raw filesystem paths in tool arguments.

Recommended fixes:

1. Change tool schemas to accept `artifact_id` plus optional `run_id` and `agent_id`, then resolve those through the artifact store's metadata/index. 2. If path arguments must remain for compatibility, resolve the path with `Path(path).resolve()` and reject it unless it is under `artifact_store.base_dir.resolve()`. 3. Require a valid artifact metadata sidecar for read helpers. Files not created by `FileSystemArtifactStore.store()` should not be readable through artifact tools. 4. Apply the same containment check to `load()`, `head()`, `tail()`, `grep()`, `chunk()`, and `delete()`. 5. Avoid returning absolute host paths in prompt-visible artifact references when an opaque artifact ID would suffice.

Minimal containment helper:

```python def _resolve_artifact_path(self, path: str) -> Path: resolved = Path(path).expanduser().resolve() base = self.base_dir.resolve() try: resolved.relative_to(base) except ValueError as exc: raise PermissionError("Artifact path is outside artifact storage") from exc return resolved ```

This helper should be paired with metadata-sidecar validation so arbitrary non-artifact files placed under the base directory are not automatically treated as valid artifacts.

Are you affected?

Enter the version of the package you're using.

Affected packages

PyPI / praisonai
Introduced in: 3.8.1 Fixed in: 4.6.59
Fix pip install --upgrade 'praisonai>=4.6.59'

References