Skip to content

Escape "."/".." path components so a lane name cannot escape lanes/#1703

Open
jax-0n-git wants to merge 1 commit into
osaurus-ai:mainfrom
jax-0n-git:fix/coordinator-paths-dotdot-sanitization
Open

Escape "."/".." path components so a lane name cannot escape lanes/#1703
jax-0n-git wants to merge 1 commit into
osaurus-ai:mainfrom
jax-0n-git:fix/coordinator-paths-dotdot-sanitization

Conversation

@jax-0n-git

Copy link
Copy Markdown
Contributor

Summary

CoordinatorPaths.fileComponent(for:) exists to neutralize path separators so a resource/lane name stays a single child component (the existing testLockFileComponentEscapesPathSeparators enforces Packages/Foo.swift -> Packages%2FFoo.swift). But it passes . (byte 46) through verbatim, so a component of only . or .. survives unescaped, and laneDirectory(named:) appends it directly:

  • laneDirectory(named: "..") -> <root>/lanes/.. -> standardizes to <root> — one level above the lanes/ container
  • laneDirectory(named: ".") -> <root>/lanes/. -> the lanes/ directory itself

Multi-segment traversal like ../../etc is already blocked (/ is escaped to %2F); only a bare single-segment ./.. slips through. There is no untrusted caller today (CoordinatorBootstrap uses a fixed lane list), so this is a defense-in-depth correctness fix for a public sanitization API, not an exploitable path in current code.

The fix percent-encodes the dots when the whole sanitized component is . or ... Dots inside longer names (Foo.swift, v1.2.3) are unaffected, so the existing separator test still passes.

Changes

  • Bug fix
  • Tests

Test Plan

swift test in Packages/OsaurusCLI (91 tests green, +3 new) + swift-format lint --strict clean. New tests: ./.. encode to %2E/%2E%2E (fail on pre-fix), dots inside names pass through unchanged, and laneDirectory(named:) stays strictly under lanes/ for both . and ... No MLX/GUI dependency.

`CoordinatorPaths.fileComponent(for:)` percent-encodes path separators so a
resource/lane name stays a single child component, but it passes `.` (byte 46)
through verbatim. A component of only "." or ".." therefore survives unescaped,
and `laneDirectory(named:)` appends it directly:

  - `laneDirectory(named: "..")` -> `<root>/lanes/..` -> standardizes to `<root>`
    (one level *above* the lanes/ container)
  - `laneDirectory(named: ".")` -> `<root>/lanes/.` -> the lanes/ dir itself

This defeats the function's own contract (the existing
`testLockFileComponentEscapesPathSeparators` exists to enforce that a name
cannot leave its container). Multi-segment traversal like `../../etc` is
already blocked because `/` is escaped to `%2F`; only a bare single-segment
"." / ".." slips through.

Percent-encode the dots when the whole sanitized component is "." or "..".
Dots inside longer names (`Foo.swift`, `v1.2.3`) are unaffected, so the
existing separator-escaping test still passes.

Adds tests for the dot-segment encoding, the unchanged in-name dots, and that
`laneDirectory(named:)` stays strictly under lanes/ for both "." and "..".

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant