{"record":{"id":"a7e35c910047e4e9","repo":"ComposioHQ/composio","slug":"refusing-to-build-a-path-from-a-label-containing","errorCode":null,"errorMessage":"Refusing to build a path from a {label} containing path separators or a drive letter: {value!r}","messagePattern":"Refusing to build a path from a (.+?) containing path separators or a drive letter: (.+?)","errorType":"validation","errorClass":"UnsafePathComponentError","httpStatus":null,"severity":"critical","filePath":"python/composio/utils/safe_path.py","lineNumber":110,"sourceCode":"    component, else raise.\n\n    Fails closed. Rejects traversal (``..``), separators of either platform,\n    absolute paths, drive letters, NUL bytes, reserved device names, and\n    anything outside :data:`SAFE_COMPONENT_REGEX`.\n\n    :raises UnsafePathComponentError: when ``value`` is unsafe.\n    \"\"\"\n    if not isinstance(value, str) or not value:\n        raise UnsafePathComponentError(\n            f\"Refusing to build a path from an empty or non-string {label}: {value!r}\"\n        )\n\n    # `PureWindowsPath` treats both `/` and `\\` as separators, so a single check\n    # catches `../x` and `..\\x` regardless of the host platform. A slug crafted\n    # for a Windows target must not slip through on a POSIX build machine.\n    as_windows_path = PureWindowsPath(value)\n    if len(as_windows_path.parts) != 1 or as_windows_path.anchor:\n        raise UnsafePathComponentError(\n            f\"Refusing to build a path from a {label} containing path separators \"\n            f\"or a drive letter: {value!r}\"\n        )\n\n    if len(value) > MAX_COMPONENT_LENGTH:\n        raise UnsafePathComponentError(\n            f\"Refusing to build a path from a {label} longer than \"\n            f\"{MAX_COMPONENT_LENGTH} characters: {value[:32]!r}... \"\n            f\"({len(value)} characters)\"\n        )\n\n    # `.` and `..` are excluded by the regex (no `.` in the character class),\n    # as are NUL bytes and every separator. The explicit checks above exist to\n    # produce a precise error message rather than a generic pattern mismatch.\n    #\n    # `fullmatch`, not `match`: in a `match`, `$` also matches just before a\n    # single trailing newline, so `\"GMAIL\\n\"` would satisfy `^[A-Za-z0-9_-]+$`\n    # and reach the filesystem with a control character in the name.","sourceCodeStart":92,"sourceCodeEnd":128,"githubUrl":"https://github.com/ComposioHQ/composio/blob/64b1b85502b1beeb2379e6c9e8bf1104504fa637/python/composio/utils/safe_path.py#L92-L128","documentation":"assert_safe_path_component rejects a component containing path separators (/ or \\) or a Windows drive letter (C:). PureWindowsPath is used so both separator styles are caught on every platform, preventing ../x and ..\\x traversal.","triggerScenarios":"secure_join(root, '../etc/passwd'), secure_join(root, 'C:\\\\boot.ini'), or any component with an anchor/multiple parts — i.e. traversal payloads or components that were meant to be joined with the root instead.","commonSituations":"Passing relative file paths where a single directory component is expected; untrusted API fields containing traversal strings; accidentally passing a full path as a slug.","solutions":["Pass only the final directory name; build multi-level paths as separate safe components to secure_join","If a real subpath is required, split it yourself and validate each segment","Sanitize untrusted input before it reaches path building"],"exampleFix":"# before\nsecure_join(root, \"a/b/c\")\n# after\nsecure_join(root, \"a\", \"b\", \"c\")","handlingStrategy":"validation","validationCode":"from pathlib import PureWindowsPath\ndef is_single_component(v):\n    p = PureWindowsPath(v)\n    return isinstance(v, str) and len(p.parts) == 1 and not p.anchor","typeGuard":"def is_traversal_free(v) -> bool:\n    return isinstance(v, str) and '/' not in v and '\\\\' not in v and ':' not in v and v not in ('.','..')","tryCatchPattern":"from composio.exceptions import UnsafePathComponentError\ntry:\n    p = secure_join(root, name)\nexcept UnsafePathComponentError:\n    name = name.replace('/','_').replace('\\\\','_')\n    p = secure_join(root, name)","preventionTips":["Pass each directory level as its own component to secure_join","Treat any separator in a slug as malicious input, not a path"],"tags":["path-traversal","security","python"],"backgroundTag":"path-traversal-detected","analyzedSha":"64b1b85502b1beeb2379e6c9e8bf1104504fa637","analyzedAt":"2026-08-28T15:39:33.623Z","schemaVersion":2},"datasetVersion":"2026-08-28T16:17:29.566Z"}