Skip to content

Refuse a Drive-Letter Component Anywhere in a Tree Path, Not Only at Its Start #2244

Description

@ptr727

Found by the local review pass on #2243 (#1452). It is not live today, and it predates that change in scripts/carry.py.

escapes_repo_root in spec/validate.py matches a drive letter only at the start of a value (^[A-Za-z]:). A drive-shaped component later in the path, such as real/C:x, passes it.

Both scripts/carry.py's relative_root and spec/validate.py's new symlinked_component walk a tree path one component at a time with current /= part. On Windows, joining a drive-relative component throws away everything joined before it, so PureWindowsPath("D:/root") / "C:x" is C:x. The symlink check then reads a path relative to drive C's current directory rather than one under the root. If that path is a symlink, symlinked_component's relative_to(root) raises an uncaught ValueError and the validator crashes rather than reporting an error.

Reaching it needs a Windows host, a : inside a component (NTFS reads it as an alternate data stream), and a symlink at that spot. The one tree spec/files.json declares trips none of these.

Suggested shape

Refuse a drive-letter spelling in any component rather than only at the start, in escapes_repo_root and in relative_root alike, so both tools refuse the value before either walk joins it.

Activity

  1. added
    gateA rule with no mechanical check, or a check that misses a shape
    on Oct 1, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    gateA rule with no mechanical check, or a check that misses a shape

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions