Skip to content

Commit 39ea2fa

Browse files
committed
fix(update): reject dangling symlinks instead of lexical fallback
EvalSymlinks failure used filepath.Clean on the link text, which does not preserve already-traversed links. A dangling member is now an error.
1 parent d558882 commit 39ea2fa

2 files changed

Lines changed: 18 additions & 13 deletions

File tree

‎internal/update/extract.go‎

Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -208,19 +208,7 @@ func verifyNoSymlinkEscape(destDirClean string, target string) error {
208208
if info.Mode()&os.ModeSymlink != 0 {
209209
resolved, err := filepath.EvalSymlinks(current)
210210
if err != nil {
211-
if os.IsNotExist(err) {
212-
// Dangling symlink is allowed if its relative destination stays within destDir.
213-
linkTarget, readErr := os.Readlink(current)
214-
if readErr != nil {
215-
return readErr
216-
}
217-
if filepath.IsAbs(linkTarget) {
218-
return fmt.Errorf("archive symlink %s has absolute target: %s", current, linkTarget)
219-
}
220-
resolved = filepath.Clean(filepath.Join(filepath.Dir(current), linkTarget))
221-
} else {
222-
return err
223-
}
211+
return fmt.Errorf("archive symlink %s is dangling or unresolvable: %w", current, err)
224212
}
225213
resolved = filepath.Clean(resolved)
226214
if resolved != destDirResolved && !strings.HasPrefix(resolved, destDirResolved+string(os.PathSeparator)) &&

‎internal/update/extract_test.go‎

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -381,3 +381,20 @@ func TestExtractTarGzRejectsChainedSymlinkEscapingFile(t *testing.T) {
381381
t.Fatalf("escaped file exists outside destDir: %v", err)
382382
}
383383
}
384+
385+
func TestVerifyNoSymlinkEscapeRejectsDanglingLink(t *testing.T) {
386+
if !symlinksSupported(t) {
387+
t.Skip("symlinks not supported")
388+
}
389+
destDir := t.TempDir()
390+
if err := os.Symlink("missing-target", filepath.Join(destDir, "d")); err != nil {
391+
t.Fatal(err)
392+
}
393+
if err := os.Symlink("..", filepath.Join(destDir, "d", "s")); err == nil {
394+
t.Log("created d/s through dangling d")
395+
}
396+
target := filepath.Join(destDir, "d", "s", "x")
397+
if err := verifyNoSymlinkEscape(destDir, target); err == nil {
398+
t.Fatal("expected dangling or unresolvable symlink to be rejected")
399+
}
400+
}

0 commit comments

Comments
 (0)