Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 35 additions & 12 deletions app.py
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,33 @@
)


def _validated_monitor_log_path(user_path, allowed_paths):
"""Return canonical validated log path or raise ValueError/PermissionError."""
if not isinstance(user_path, str) or not user_path:
raise ValueError("Path is required")

if not os.path.isabs(user_path):
raise ValueError("Path must be absolute")

real_log_path = os.path.realpath(user_path)

allowed_roots = []
for p in allowed_paths or []:
if isinstance(p, str) and p:
root = os.path.realpath(p)
if os.path.isdir(root):
allowed_roots.append(root)

for root in allowed_roots:
try:
if os.path.commonpath([real_log_path, root]) == root:
return real_log_path
except ValueError:
continue

raise PermissionError("Path not in allowed monitor paths")


@app.route("/monitor", methods=["POST"])
def monitor_start():
"""
Expand All @@ -259,18 +286,14 @@
log_path = body.get("path", "")
log_format = body.get("format", "auto")

if not os.path.isabs(log_path):
return jsonify({"error": "Path must be absolute"}), 400

real_log_path = os.path.realpath(log_path)
allowed_paths = app.config.get("MONITOR_ALLOWED_PATHS", [])
allowed_roots = [os.path.realpath(p) for p in allowed_paths]

if not any(
os.path.commonpath([real_log_path, root]) == root
for root in allowed_roots
):
return jsonify({"error": "Path not in allowed monitor paths"}), 403
try:
real_log_path = _validated_monitor_log_path(
log_path, app.config.get("MONITOR_ALLOWED_PATHS", [])
)
except ValueError as exc:
return jsonify({"error": str(exc)}), 400

Check warning

Code scanning / CodeQL

Information exposure through an exception Medium

Stack trace information
flows to this location and may be exposed to an external user.
except PermissionError as exc:
return jsonify({"error": str(exc)}), 403

Check warning

Code scanning / CodeQL

Information exposure through an exception Medium

Stack trace information
flows to this location and may be exposed to an external user.

if not os.path.isfile(real_log_path):
return jsonify({"error": "File not found"}), 404
Expand Down
Loading