{"record":{"id":"6b88422d074d86f6","repo":"HKUDS/DeepTutor","slug":"this-folder-is-outside-the-locations-allowed-for-l","errorCode":null,"errorMessage":"This folder is outside the locations allowed for linking.","messagePattern":"This folder is outside the locations allowed for linking\\.","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"deeptutor/services/rag/linked_kb.py","lineNumber":109,"sourceCode":"\ndef assert_path_allowed(folder_path: str) -> Path:\n    \"\"\"Resolve ``folder_path`` and enforce the optional root allowlist.\n\n    Returns the resolved absolute path. Raises ``ValueError`` if the folder is\n    missing/not a directory, or escapes the configured allowlist (symlinks are\n    resolved first so they can't tunnel out). With no allowlist set, any\n    existing directory is permitted — the self-hosted default.\n    \"\"\"\n    folder = Path(folder_path).expanduser()\n    if not folder.exists():\n        raise ValueError(f\"Folder does not exist: {folder}\")\n    if not folder.is_dir():\n        raise ValueError(f\"Not a directory: {folder}\")\n    resolved = folder.resolve()\n\n    roots = allowed_link_roots()\n    if roots and not any(_is_within(resolved, root) for root in roots):\n        raise ValueError(\"This folder is outside the locations allowed for linking.\")\n    return resolved\n\n\ndef _is_within(path: Path, root: Path) -> bool:\n    try:\n        return path == root or root in path.parents\n    except OSError:\n        return False\n\n\ndef probe_linked_folder(folder_path: str, provider: str) -> ProbeResult:\n    \"\"\"Inspect ``folder_path`` for a ready ``provider`` index.\n\n    Always returns a :class:`ProbeResult` (never raises): ``error`` set means\n    the folder cannot be linked; ``warnings`` are non-fatal cautions the user\n    confirms. The caller is responsible for any path-jail / access checks.\n    \"\"\"\n    provider = normalize_provider_name(provider)","sourceCodeStart":91,"sourceCodeEnd":127,"githubUrl":"https://github.com/HKUDS/DeepTutor/blob/3e82f130422a813cdd73c10b21a44e9325f5821a/deeptutor/services/rag/linked_kb.py#L91-L127","documentation":"Raised when an allowlist of linkable roots is configured (allowed_link_roots()) and the resolved folder (symlinks resolved) does not fall under any allowed root. With no allowlist configured, any existing directory is permitted — the self-hosted default.","triggerScenarios":"DEEPTUTOR_LINK_ROOTS (or equivalent settings key) is set, and the user links a folder outside those roots; a symlink inside an allowed root points outside it (resolved before checking, so it's rejected).","commonSituations":"Hardened/multi-user deployments that restrict linking to specific directories; symlinks from an allowed directory into / or another user's home; misunderstanding that symlinks are resolved, so tricks to escape the allowlist fail by design.","solutions":["Move or symlink the folder from inside an allowed root (a real path under the root).","Add the folder's parent to the configured link roots setting and retry.","If you are the self-hosted admin and want permissive behavior, clear the allowlist setting.","Verify with Path(folder).resolve() which real path is being evaluated."],"exampleFix":"# before: /srv/data outside allowlist ['/home/user']\nawait connect_linked_folder_route(session, kb, \"/srv/data/vault\")\n# after: add to settings\nallowed_link_roots: [\"/home/user\", \"/srv/data\"]","handlingStrategy":"validation","validationCode":"from pathlib import Path\nresolved = Path(folder_path).expanduser().resolve()\nroots = allowed_link_roots()\nif roots and not any(resolved == r or r in resolved.parents for r in roots):\n    raise InputError(\"outside allowed link roots\")","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Surface the configured allowlist in the UI so users pick within it.","Remember symlinks are resolved — don't try to sneak paths through them."],"tags":["security","allowlist","filesystem","linked-folders"],"backgroundTag":"path-allowlist-denied","analyzedSha":"3e82f130422a813cdd73c10b21a44e9325f5821a","analyzedAt":"2026-08-27T06:57:25.364Z","schemaVersion":2},"datasetVersion":"2026-08-27T08:17:20.692Z"}