relink: resolve symlinks before the in-context check (_inside / build_context_index) #44

Closed
opened 2026-11-02 16:06:15 +00:00 by blake · 1 comment
Collaborator

Problem

_inside() in pkg/scripts/scripts/relink_context.py (PR #42) compares os.path.commonpath of normcased paths and never calls realpath. build_context_index uses os.walk, which lists symlinked files. A symlink inside the job context that points outside it therefore passes _inside and gets indexed and relinked, and Blender then reads its target.

Reachability today: none through the job archive

The runner extracts the context with workspace.ExtractTar (internal/runner/runner.go downloadContext, internal/runner/workspace/archive.go). It only creates TypeDir and TypeReg entries, so symlinks and hardlinks are silently dropped. This is defense in depth: it becomes reachable if extraction ever starts keeping links, or if anything else drops a link into the job dir.

Evidence

I ran a local scratch test with root/tex/link.png -> /etc/passwd. resolve_in_context(r"E:\a/b\..\link.png", idx) returned tex/link.png, _inside returned True, and the realpath was /etc/passwd.

Acceptance criteria

  • _inside (or the relink step) compares os.path.realpath of both the path and the root. build_context_index skips entries where os.path.islink is true, or whose realpath is outside the root.
  • A unit test with a symlink that points outside the context shows it isn't indexed or relinked.
  • Optional: ExtractTar should reject link entries outright rather than dropping them silently, matching ExtractTarStripPrefix.

Found in the security review of PR #42.

## Problem `_inside()` in pkg/scripts/scripts/relink_context.py (PR #42) compares `os.path.commonpath` of `normcase`d paths and never calls `realpath`. `build_context_index` uses `os.walk`, which lists symlinked files. A symlink inside the job context that points outside it therefore passes `_inside` and gets indexed and relinked, and Blender then reads its target. ## Reachability today: none through the job archive The runner extracts the context with `workspace.ExtractTar` (internal/runner/runner.go `downloadContext`, internal/runner/workspace/archive.go). It only creates `TypeDir` and `TypeReg` entries, so symlinks and hardlinks are silently dropped. This is defense in depth: it becomes reachable if extraction ever starts keeping links, or if anything else drops a link into the job dir. ## Evidence I ran a local scratch test with `root/tex/link.png -> /etc/passwd`. `resolve_in_context(r"E:\a/b\..\link.png", idx)` returned `tex/link.png`, `_inside` returned True, and the realpath was `/etc/passwd`. ## Acceptance criteria - `_inside` (or the relink step) compares `os.path.realpath` of both the path and the root. `build_context_index` skips entries where `os.path.islink` is true, or whose realpath is outside the root. - A unit test with a symlink that points outside the context shows it isn't indexed or relinked. - Optional: `ExtractTar` should reject link entries outright rather than dropping them silently, matching `ExtractTarStripPrefix`. Found in the security review of PR #42.
Owner

Routing this to Ash since it's a code change in relink_context.py (realpath check plus a symlink unit test). It's Priority/Low and defense in depth, so Mira picks when it gets worked. The PR is security-sensitive, so Blake reviews it.

Routing this to Ash since it's a code change in relink_context.py (realpath check plus a symlink unit test). It's Priority/Low and defense in depth, so Mira picks when it gets worked. The PR is security-sensitive, so Blake reviews it.
linus closed this issue 2026-11-03 16:18:03 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
s1d3sw1ped/jiggablend#44
No description provided.