Skip to content

[repo-health] Medium: LocalProvider.fetch() accepts arbitrary filesystem paths — path traversal risk when /api/analyze is wired up #61

Description

@Liohtml

Summary

LocalProvider.fetch() resolves the source argument directly to Path(target) and scans any directory or ZIP file on the host filesystem. When the /api/analyze endpoint is connected to the pipeline (currently returns not_implemented), an authenticated user could pass source=/etc or source=../../secrets to read arbitrary files.

Category

Security

Severity

Medium

Location

  • File: src/medcheck/providers/local.py
  • Lines: 23–34 (fetch() method)

Details

The ZIP-traversal check in _scan_zip() is correctly implemented. The gap is in the directory path branch: Path(target).is_dir() accepts any absolute or relative path. There is no sandboxing to a safe base directory.

Current mitigations (lower severity):

  • /api/analyze currently returns {"status": "not_implemented"} — the code is never reached
  • Server defaults to 127.0.0.1 (localhost-only) and supports API key auth
  • Operators must explicitly set MEDCHECK_HOST=0.0.0.0 to expose over the network

Suggested Fix

Before connecting the analyze pipeline to the web endpoint, add a sandbox check:

import os

SAFE_BASE = Path(os.environ.get("MEDCHECK_DATA_DIR", "/data/dicom"))

def _sandbox(path: Path) -> Path:
    resolved = path.resolve()
    if not str(resolved).startswith(str(SAFE_BASE.resolve())):
        raise ValueError(f"Path outside allowed data directory: {path}")
    return resolved

And in fetch():

target_path = self._sandbox(Path(target))

Effort Estimate

15 min


Automated finding by repo-health-agent v1.0

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions