ATH-MaaS/Pixelle-Video · warning · HTTPException

Access denied: only {', '.join(p.rstrip('/') for p in allowe

Error message

Access denied: only {', '.join(p.rstrip('/') for p in allowed_prefixes)} directories are accessible

What it means

The handler enforces a path-traversal whitelist: the resolved relative path must start with one of the allowed prefixes (e.g. output/, uploads/). If the file resolves outside those directories it returns 403 with the list of allowed directories. This blocks access to arbitrary server files.

Source

Thrown at api/routers/files.py:92

        
        abs_path = Path.cwd() / full_path
        
        if not abs_path.exists():
            raise HTTPException(status_code=404, detail=f"File not found: {file_path}")
        
        if not abs_path.is_file():
            raise HTTPException(status_code=400, detail=f"Path is not a file: {file_path}")
        
        # Security: only allow access to specified directories
        try:
            rel_path = abs_path.relative_to(Path.cwd())
            rel_path_str = str(rel_path)
            
            # Check if path starts with any allowed prefix
            is_allowed = any(rel_path_str.startswith(prefix.rstrip('/')) for prefix in allowed_prefixes)
            
            if not is_allowed:
                raise HTTPException(
                    status_code=403, 
                    detail=f"Access denied: only {', '.join(p.rstrip('/') for p in allowed_prefixes)} directories are accessible"
                )
        except ValueError:
            raise HTTPException(status_code=403, detail="Access denied")
        
        # Determine media type
        suffix = abs_path.suffix.lower()
        media_types = {
            '.mp4': 'video/mp4',
            '.mp3': 'audio/mpeg',
            '.wav': 'audio/wav',
            '.png': 'image/png',
            '.jpg': 'image/jpeg',
            '.jpeg': 'image/jpeg',
            '.gif': 'image/gif',
            '.html': 'text/html',
            '.json': 'application/json',

View on GitHub (pinned to 848b054e4f)

Solutions

  1. Request only files under the allowed directories (the detail lists them).
  2. If a new storage directory is legitimate, add it to allowed_prefixes in the handler config.
  3. Normalize/resolve the requested path client-side to stay within the allowed root.
  4. Check for symlinks in your output directories that resolve outside the project root.
  5. Never attempt traversal (../) — the server resolves and rejects it by design.

Example fix

# before
GET /api/files/../../etc/passwd
# after
GET /api/files/generated/frame_0001.mp4  # stays under output/
Defensive patterns

Strategy: validation

Validate before calling

const normalized = path.posix.normalize(filePath).replace(/^\.\./, '');
if (normalized.startsWith('..') || path.isAbsolute(normalized)) throw new Error('Path must be relative and within allowed directories');
const allowedPrefixes = ['output/', 'uploads/'];
if (!allowedPrefixes.some(p => normalized.startsWith(p))) throw new Error(`Only ${allowedPrefixes.join(', ')} paths are servable`);

Prevention

When it happens

Trigger: GET to the files endpoint where the resolved path escapes the allowed roots — absolute paths outside CWD, '../' traversal sequences, symlinks pointing outside allowed directories, or requesting files from a directory not in allowed_prefixes.

Common situations: Trying to read config/secrets via path traversal (correctly blocked); legitimate files stored in a new directory that was never added to allowed_prefixes; symlinked output directories resolving outside CWD in dev setups.

Understand the failure class

Background: Path traversal blocked: "path escapes the workspace" and "outside site root" errors when a path will not stay inside its allowed directory — this error's family across 26 libraries.

Related errors


AI-assisted analysis of ATH-MaaS/Pixelle-Video@848b054e4f (2026-08-30). Data as JSON: /api/errors/7de3cd17be4b52ea. Report an issue: GitHub.