{"record":{"id":"1adf169616ac968b","repo":"xtekky/gpt4free","slug":"file-access-outside-workspace-is-denied-file","errorCode":null,"errorMessage":"File access outside workspace is denied: '{file}'. Workspace: {workspace}","messagePattern":"File access outside workspace is denied: '(.+?)'\\. Workspace: (.+?)","errorType":"exception","errorClass":"PermissionError","httpStatus":null,"severity":"error","filePath":"g4f/mcp/pa_provider.py","lineNumber":538,"sourceCode":"    # Build a reduced copy of the real built-ins\n    _blocked = frozenset(\n        {\"exec\", \"eval\", \"compile\", \"input\", \"breakpoint\", \"__import__\"}\n    )\n    safe_builtins: Dict[str, Any] = {\n        k: getattr(_builtins, k) for k in dir(_builtins) if k not in _blocked\n    }\n\n    # Provide a workspace-scoped open()\n    def _safe_open(file, mode=\"r\", *args, **kwargs):\n        \"\"\"open() restricted to the workspace directory.\"\"\"\n        path = Path(file)\n        if not path.is_absolute():\n            path = workspace / path\n        try:\n            resolved = path.resolve()\n            ws_resolved = workspace.resolve()\n            if not str(resolved).startswith(str(ws_resolved)):\n                raise PermissionError(\n                    f\"File access outside workspace is denied: '{file}'. \"\n                    f\"Workspace: {workspace}\"\n                )\n        except (ValueError, OSError) as exc:\n            raise PermissionError(f\"Invalid file path: '{file}'\") from exc\n        return open(resolved, mode, *args, **kwargs)\n\n    safe_builtins[\"open\"] = _safe_open\n    safe_builtins[\"__import__\"] = _make_restricted_import(allowed)\n\n    # Override print / input so stdout/stderr stay local to this sandbox\n    # execution and are never written to the real sys.stdout/stderr.  This\n    # avoids the global-state side-effect that contextlib.redirect_stdout\n    # would cause when the thread is abandoned after a timeout.\n    if stdout_buf is not None:\n        _real_print = _builtins.print\n\n        def _safe_print(*args, **kwargs):","sourceCodeStart":520,"sourceCodeEnd":556,"githubUrl":"https://github.com/xtekky/gpt4free/blob/973504e1770928ed5fb82f43da528f441ad9ddc3/g4f/mcp/pa_provider.py#L520-L556","documentation":"Raised by the sandbox's replacement open() when the requested path resolves outside the workspace directory. The shim resolves both the target and the workspace root and denies any target whose resolved path string does not start with the workspace prefix, confining .pa.py file I/O to the workspace. Note the check is a plain string prefix, so a sibling directory sharing a prefix (e.g. /ws-evil next to /ws) would also pass — a known weakness of this style of check.","triggerScenarios":"open('/etc/passwd'), open('../../secrets.txt'), or any absolute/relative path whose resolved location is not under the workspace directory. Also triggered on Windows when drive letters or .. traversal escape the workspace.","commonSituations":"Provider code writing cache/token files to a hard-coded absolute path; using '..' to reach project files; scripts developed outside the sandbox that read config from $HOME.","solutions":["Use relative paths — they are joined onto the workspace root by the shim","Store any cache/credentials inside the workspace directory (e.g. open('cache.json', 'w'))","Pass file contents in as strings/parameters instead of reading external files"],"exampleFix":"# before\nopen('/home/user/tokens.json')\n\n# after\nopen('tokens.json')  # resolves inside the workspace dir","handlingStrategy":"validation","validationCode":"from pathlib import Path\n\ndef is_in_workspace(candidate, workspace: Path) -> bool:\n    resolved = (workspace / candidate).resolve() if not Path(candidate).is_absolute() else Path(candidate).resolve()\n    ws = workspace.resolve()\n    return resolved == ws or str(resolved).startswith(str(ws) + str(Path('/').sep))\n# note: use the sep-terminated prefix to avoid the sibling-dir bypass","typeGuard":null,"tryCatchPattern":"try:\n    with open(target) as f:\n        data = f.read()\nexcept PermissionError as e:\n    if \"outside workspace\" in str(e):\n        data = None  # re-point the path into the workspace","preventionTips":["Always use workspace-relative paths in .pa.py files","Keep caches, tokens and state files inside the workspace directory","Never hard-code absolute paths in sandboxed provider code"],"tags":["sandbox","filesystem","security","permissions","pa-provider"],"backgroundTag":null,"analyzedSha":"973504e1770928ed5fb82f43da528f441ad9ddc3","analyzedAt":"2026-08-14T23:45:32.408Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}