Reject a repo file path that could escape the snapshot directory once
joined onto it. rfilename comes verbatim from the remote HF API's
siblings[].rfilename (listRepoFiles) — for a repo the caller doesn't
control (attacker-owned/attacker-referenced), that string is untrusted
input reaching a path-join + symlink write (downloadModel's
join(snapDir, f.rfilename) + symlinkSync). node:path's join()
normalizes .. segments, so an rfilename like
../../../../home/x/.ssh/authorized_keys would otherwise resolve
OUTSIDE the intended snapshots dir and write a symlink at an arbitrary
filesystem path. Reachable over HTTP via POST /api/hub/download
(apps/mlx-bun/src/server/hub-routes.ts), which passes any caller-supplied repo straight
through with no allowlist — this check is the actual gate, not that
route. A leading / (absolute path) is equally rejected: join() would
otherwise treat it as relative to snapDir and still normalize safely,
but there's no legitimate reason a repo file entry needs one, and
rejecting it removes any doubt.
Reject a repo file path that could escape the snapshot directory once joined onto it.
rfilenamecomes verbatim from the remote HF API'ssiblings[].rfilename(listRepoFiles) — for a repo the caller doesn't control (attacker-owned/attacker-referenced), that string is untrusted input reaching a path-join + symlink write (downloadModel'sjoin(snapDir, f.rfilename)+symlinkSync). node:path'sjoin()normalizes..segments, so an rfilename like../../../../home/x/.ssh/authorized_keyswould otherwise resolve OUTSIDE the intended snapshots dir and write a symlink at an arbitrary filesystem path. Reachable over HTTP via POST /api/hub/download (apps/mlx-bun/src/server/hub-routes.ts), which passes any caller-suppliedrepostraight through with no allowlist — this check is the actual gate, not that route. A leading/(absolute path) is equally rejected: join() would otherwise treat it as relative to snapDir and still normalize safely, but there's no legitimate reason a repo file entry needs one, and rejecting it removes any doubt.