Skip to content

security: use os.Root API for file path logic#1279

Merged
cbrnrd merged 3 commits intomainfrom
security/fix-symlink-readwrite
Mar 27, 2026
Merged

security: use os.Root API for file path logic#1279
cbrnrd merged 3 commits intomainfrom
security/fix-symlink-readwrite

Conversation

@cbrnrd
Copy link
Copy Markdown
Contributor

@cbrnrd cbrnrd commented Mar 26, 2026

Problem

Mount path validation in src batch uses strings.HasPrefix on non-canonical paths and os.Stat which follows symlinks. This allowed two bypass techniques:

  • Path prefix confusion — a path like /tmp/specdir-123-outside/secret.txt passes a HasPrefix check against /tmp/specdir-123
  • Symlink traversal — a symlink inside the spec directory pointing outside it passes both os.Stat and the prefix check

Solution

Replace all os.Stat/os.Open/os.ReadDir + strings.HasPrefix path validation with Go's os.OpenRoot API, which enforces directory confinement at the OS level — blocking symlink escapes, .. traversal, and prefix confusion.

Test plan

Added new tests which pass, also verified locally.

@keegancsmith keegancsmith requested a review from a team March 26, 2026 19:02
@@ -129,31 +129,43 @@ func (svc *Service) UploadBatchSpecWorkspaceFiles(ctx context.Context, workingDi
}

func getFilePaths(workingDir, filePath string) ([]string, error) {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

any idea why this implementation didn't just use filepath.Walk instead of implementing its own recursion?

Copy link
Copy Markdown
Contributor Author

@cbrnrd cbrnrd Mar 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

According to deepsearch, it's to preserve the relative path structure from a particular working directory

}

func getAbsoluteMountPath(batchSpecDir string, mountPath string) (string, error) {
root, err := os.OpenRoot(batchSpecDir)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would appreciate a comment here for future devs to tell them why this should be used.

@cbrnrd cbrnrd enabled auto-merge (squash) March 27, 2026 18:13
@cbrnrd cbrnrd merged commit 5e4aec2 into main Mar 27, 2026
9 checks passed
@cbrnrd cbrnrd deleted the security/fix-symlink-readwrite branch March 27, 2026 18:19
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.

3 participants