bazelbuild / bazelbuild/bazel

Missing path validation on OutputFile/OutputSymlink paths when parsing cached ActionResult (defense-in-depth gap)

Open
#30,714 0 comments 0 reactions 1 assignee Claimed by @meisterT View on GitHub
P2 team-Remote-Exec type: bug
Dominant language
Java
Stars
25.8k
Forks
4.6k
Avg merge
2d 20h
Merged PRs (30d)
72

Description

In `RemoteExecutionService.parseActionResultMetadata()`, `validatePathComponent()` is applied to `Directory`/`Tree` nodes but **not** to top-level `OutputFile.path` / `OutputSymlink.path`. These are passed directly to `RemotePathResolver.outputPathToLocalPath()`, which resolves them against the exec root via `execRoot.getRelative(...)`.

**Current state:** This is not exploitable today, because Bazel's output-matching layer rejects cache entries whose outputs don't match the declared outputs and falls back to local execution. However, the parser should not trust path fields from cache data this is a defense-in-depth gap. If the output-matching layer is ever weakened or bypassed, the unvalidated path becomes an arbitrary-write primitive.

**Repro:** A poisoned disk-cache `ActionResult` with `output_files[0].path = "../../../../.../pwned.txt"` is parsed without validation (verified via byte-level patch of the AC file). Bazel currently rejects the entry at the output-matching stage.

**Disclosure note:** This was reported to the Google VRP, which determined it does not meet their security-escalation threshold and explicitly cleared it for public disclosure.

**Suggested fix:** Apply the same containment check used for `Directory` nodes (reject `..`, absolute paths, `/` in components, or enforce `startsWith(execRoot)` after resolution) to `OutputFile.path`, `OutputSymlink.path/target`, and `OutputDirectory.path`.

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.