bazelbuild / bazelbuild/bazel

Remote cache symlinks with relative targets can escape exec root on download

Open
#30,352 0 comments 0 reactions 0 assignees View on GitHub
team-Remote-Exec type: bug
Dominant language
Java
Stars
25.8k
Forks
4.6k
Avg merge
2d 20h
Merged PRs (30d)
72

Description

## Summary

When materializing symlinks from a cached `ActionResult`, `createSymlinks()` in `RemoteExecutionService.java` validates that the symlink **path** stays inside `execRoot`, but performs no validation on relative symlink **targets**. A relative target like `../../../.ssh/authorized_keys` passes the path check but resolves outside the build output tree.

## Reproduction

1. Set up a shared remote cache (e.g., `--remote_cache=grpc://cache-server:8080`)
2. A malicious actor pushes an `ActionResult` containing a symlink entry:
- `path`: `bazel-out/k8-fastbuild/bin/output_link` (valid, inside execRoot)
- `target`: `../../../../.ssh/authorized_keys` (relative, escapes execRoot)
3. A developer or CI machine fetches this cached result
4. `createSymlinks()` materializes the symlink — the path check passes (path is inside execRoot), but the resolved target points to `~/.ssh/authorized_keys`
5. Subsequent build actions or tools that follow the symlink now read/write outside the sandbox

## Context

The **upload side** already validates symlink targets via `UploadManifest.checkAbsoluteSymlinkAllowed()` using `SymlinkAbsolutePathStrategy`. However, the download side has no equivalent check for relative targets that traverse upward out of the exec root.

This creates an asymmetry: a directly-built action would be caught on upload, but a poisoned cache entry (injected via direct API calls to the cache, bypassing Bazel's upload validation) would not be caught on download.

## Impact

Shared remote caches are common in production Bazel deployments (CI farms, developer machines). A compromised cache entry could:
- Create symlinks to sensitive files (SSH keys, credentials, source code outside the workspace)
- Enable data exfiltration if a subsequent action reads the symlink and uploads results
- Enable file overwrites if a subsequent action writes through the symlink

## Proposed Fix

Before calling `createSymbolicLink()`, resolve relative targets against the symlink's parent directory and verify the resolved path remains within `execRoot`. Absolute targets should follow the existing `SymlinkAbsolutePathStrategy` policy.

## Related PR

#30346

Contributor guide

Open the contributing guide

Research direction

Start in RemoteExecutionService.java at createSymlinks(), then compare the download path with UploadManifest.checkAbsoluteSymlinkAllowed() and SymlinkAbsolutePathStrategy. Add regression coverage for relative targets that traverse outside execRoot, while preserving the existing policy for absolute targets; the related PR #30346 indicates work may already be underway.

Written by the indexing model from the issue text.

Assessment

Tech stack
java
Domain
build-system, security
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Clearly specified
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.