{"record":{"id":"5fe5c25bccd99181","repo":"langchain-ai/deepagents","slug":"path-traversal-not-allowed-in-glob-pattern-patter","errorCode":null,"errorMessage":"Path traversal not allowed in glob pattern {pattern!r}","messagePattern":"Path traversal not allowed in glob pattern (.+?)","errorType":"validation","errorClass":"InvalidGlobPatternError","httpStatus":null,"severity":"error","filePath":"libs/deepagents/deepagents/backends/utils.py","lineNumber":146,"sourceCode":"    Args:\n        pattern: Glob include pattern.\n\n    Returns:\n        Predicate accepting a search-root-relative POSIX path; returns True when\n        the path is included by `pattern`.\n\n    Raises:\n        InvalidGlobPatternError: If the pattern contains a `..` segment, or if\n            `wcmatch` refuses it (e.g. brace expansion past its limit). Note\n            most malformed patterns (`*.{py`, `[a-`) do not raise -- they\n            compile and simply match nothing.\n    \"\"\"\n    # Reject traversal here rather than per-backend: every backend routes\n    # through this function, so a single check keeps `../*.py` from being an\n    # exception in one backend and a silent empty result in another.\n    if \"..\" in pattern.replace(\"\\\\\", \"/\").split(\"/\"):\n        msg = f\"Path traversal not allowed in glob pattern {pattern!r}\"\n        raise InvalidGlobPatternError(msg)\n\n    flags = wcglob.BRACE | wcglob.GLOBSTAR\n    # A leading `/` anchors to the search root: strip it so it matches against\n    # the (slash-less) relative path, but decide anchoring from the original\n    # pattern so `/*.py` stays root-anchored instead of collapsing to a\n    # basename-at-any-depth match.\n    anchored = \"/\" in pattern\n    try:\n        compiled = wcglob.compile(pattern.lstrip(\"/\"), flags=flags)\n    except Exception as exc:\n        # `wcmatch` only raises private types (`wcmatch._wcparse.PatternLimitException`),\n        # so catch broadly and re-raise a public type: every backend can then catch\n        # one public type instead of importing from a private module. Log first --\n        # the breadth also swallows genuine bugs (a non-`str` pattern, a wcmatch\n        # version bump), which would otherwise reach the user as \"invalid pattern\"\n        # for a pattern that is perfectly valid.\n        logger.warning(\"wcmatch refused glob pattern %r (%s): %s\", pattern, type(exc).__name__, exc)\n        msg = f\"Invalid glob pattern {pattern!r}: {exc}\"","sourceCodeStart":128,"sourceCodeEnd":164,"githubUrl":"https://github.com/langchain-ai/deepagents/blob/a1af029e6e73cb17c36bff823d227747b28e91e1/libs/deepagents/deepagents/backends/utils.py#L128-L164","documentation":"`compile_grep_include_glob` compiles include/exclude glob patterns used by glob and grep across all backends. Patterns containing a literal `..` path component are rejected up-front with `InvalidGlobPatternError` so traversal cannot behave inconsistently between backends (accepted in one, silently empty in another).","triggerScenarios":"Calling `glob('**/../*.py')` or passing an include/exclude glob like `'../src/*.py'` to grep; building glob strings by naive concatenation of a relative parent path with a wildcard.","commonSituations":"Letting an LLM-generated tool call supply the glob; constructing patterns from user-supplied subdirectories; older code that used filesystem-style relative globs now routed through the virtual path layer.","solutions":["Remove the `..` component: anchor the pattern to the search root instead (e.g. `**/*.py`)","Pass the intended root as the search path/`path=` argument rather than climbing with `..` in the pattern","Sanitize or reject user/LLM-supplied globs containing `..` before invoking the tool","Catch `InvalidGlobPatternError` to return a clear tool error instead of crashing"],"exampleFix":"// before\nbackend.glob('../workspace/*.py')\n// after\nbackend.glob('*.py', path='/workspace/')","handlingStrategy":"validation","validationCode":"def safe_glob(pattern: str) -> str:\n    if '..' in pattern.replace('\\\\', '/').split('/'):\n        raise ValueError(f'path traversal in glob: {pattern!r}')\n    return pattern\nbackend.glob(safe_glob('**/*.py'), path='/workspace')","typeGuard":"def is_safe_glob(pattern: object) -> bool:\n    return isinstance(pattern, str) and '..' not in pattern.replace('\\\\', '/').split('/')","tryCatchPattern":"from deepagents.backends.utils import InvalidGlobPatternError\ntry:\n    files = backend.glob(pattern)\nexcept InvalidGlobPatternError:\n    files = backend.glob('**/*')  # safe fallback pattern","preventionTips":["Never concatenate parent-relative paths into glob patterns; use the `path=` parameter instead","Sanitize LLM/user-supplied globs for `..` components","Anchor patterns at the virtual root (`/workspace/*.py` or `*.py` with path=)","Return a clear tool error (InvalidGlobPatternError) instead of letting agents retry traversal"],"tags":["path-traversal","glob","security"],"backgroundTag":"path-traversal-rejected","analyzedSha":"a1af029e6e73cb17c36bff823d227747b28e91e1","analyzedAt":"2026-08-29T11:43:24.718Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}