fix(scope): validate remote lane scope/name to prevent path-traversal write#10414
fix(scope): validate remote lane scope/name to prevent path-traversal write#10414davidfirst wants to merge 8 commits into
Conversation
… write A remote lane's scope/name come from a Lane object served by a remote scope (untrusted). They were used unsanitized as path segments in composeRemoteLanePath (path.join), so a malicious/compromised remote could set scope/name to '../../...' and make 'bit switch'/'bit lane import'/'bit fetch --lanes' write a file outside the scope's refs directory via fs.outputFile. Reject any lane scope/name that isn't safe as a single path segment (contains a separator, '..', NUL, or is absolute) at the untrusted boundary (syncWithLaneObject) and at the path choke point (composeRemoteLanePath). Same class as the isValidPath fix that closed the version-file write path; this completes coverage for the lane sink.
There was a problem hiding this comment.
Pull request overview
This PR hardens remote-lane ref persistence against path-traversal by validating untrusted Lane-originated scope/name values before they’re used as filesystem path segments, preventing a malicious remote (or MITM on HTTP) from writing outside the refs directory.
Changes:
- Add
assertValidRemoteLaneSegment()and enforce it at both the untrusted boundary (syncWithLaneObject) and the sink (composeRemoteLanePath). - Throw a
BitErrorwhen a remote lane scope/name contains path separators,.., NUL, or is absolute. - Add unit coverage for traversal/absolute/backslash rejection and for a valid “does not throw” case.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
| components/legacy/scope/lanes/remote-lanes.ts | Adds runtime validation for remote lane scope/name before composing/writing remote-lane refs paths. |
| components/legacy/scope/lanes/remote-lanes.spec.ts | Adds tests to ensure traversal/absolute/backslash inputs are rejected and valid identifiers pass. |
Replace the hand-rolled traversal predicate with the shared isValidPath helper (@teambit/legacy.utils), matching Scope.getPendingDirPath. It is strictly stronger (also rejects '.' segments, INVALID_CHARS, over-length, leading './') and removes a duplicated bespoke variant of the same check. Also dedupe the spec's repeated try/catch boilerplate into a grabError helper.
…temp dirs Address PR review: type assertValidRemoteLaneSegment's value as unknown (it defends JSON-deserialized remote input that isn't guaranteed to be a string) and stringify safely in the error. Empty/'.'/non-string are already rejected via isValidPath. Spec now uses a unique mkdtemp dir + fresh instance per test with afterEach cleanup.
…error Use JSON.stringify(value) instead of String(value) so an invalid segment containing control chars (e.g. newlines) can't produce multi-line/confusing CLI output. Matches Scope.getPendingDirPath. Addresses PR review.
|
/agentic_review |
Code Review by Qodo
1. Unbounded error message input
|
The guard is also reached via composeRemoteLanePath on read flows (loadRemoteLane), so 'refusing to write' was misleading there. Reword to 'refusing to use it as a remote-lane ref path'. Message-only; addresses automated review feedback.
|
Code review by qodo was updated up to the latest commit 59c96ca |
|
Re: Qodo's "Misleading guard error text" finding — addressed in 59c96ca. The guard is indeed reached from |
|
Code review by qodo was updated up to the latest commit e1bc2f0 |
A remote lane's
scope/namecome from aLaneobject served by a remote scope — untrusted input. They were used unsanitized as path segments incomposeRemoteLanePath(path.join(basePath, scope, name)), so a malicious/compromised remote (or a MITM on anhttp://remote) could set them to../../…and makebit switch <remote>/<lane>/bit lane import/bit fetch --laneswrite a file outside the scope's refs directory viafs.outputFile.The fix rejects any lane scope/name that isn't safe as a single path segment (contains
/,\,.., NUL, or is absolute), enforced both at the untrusted boundary (syncWithLaneObject) and at the path choke point (composeRemoteLanePath).Same class as the
isValidPathfix (#10354) that closed the version-file write path; this completes coverage for the lane sink. Addsremote-lanes.spec.tscovering scope/name traversal, absolute, and backslash rejection plus a valid-name negative case.