Malformed `rewrite` replacement in `--downloader_config` silently terminates Bazel (exit code 37)
- Dominant language
- Java
- Stars
- 25.8k
- Forks
- 4.6k
- Avg merge
- 2d 20h
- Merged PRs (30d)
- 72
Description
### Description of the bug:
A `rewrite` directive in the `--downloader_config` file takes a matching pattern and a replacement pattern, where the replacement uses back-references (`$1`, `$2`, ...) numbered from the matching pattern's capturing groups.
`UrlRewriterConfig` validates the matching pattern (it is compiled with `Pattern.compile` at parse time), but the replacement is stored verbatim and never validated. `java.util.regex.Matcher` expands the replacement lazily, only when a URL actually matches and is rewritten during a download. So a replacement that references a capturing group the pattern does not have (e.g. `$3` when the pattern has two groups), or is otherwise malformed (e.g. a trailing `$`), parses without error and then throws an uncaught `IndexOutOfBoundsException` / `IllegalArgumentException` from `Matcher` on the first matching download.
Bazel terminates with an internal error (exit code 37) and no message pointing at the config, so there is nothing to tell the user which line is wrong.
### Which category does this issue belong to?
External Dependency
### What's the simplest, easiest way to reproduce this bug? Please provide a minimal example if possible.
- `MODULE.bazel`:
```starlark
bazel_dep(name = "rules_cc", version = "0.0.9") # any registry-backed module
```
- `rewrite.cfg` with a replacement referencing a non-existent group e.g. `$3` when the matching pattern has only two groups:
```
rewrite (bcr\.bazel\.build)/(.*) https://mirror.example.com/$1/$2/$3
```
- `.bazelrc`:
```
common --downloader_config=rewrite.cfg
```
- Run: `bazel mod deps`
### Actual behavior
Bazel exits with code 37 and prints no diagnostic:
```
Starting local Bazel server and connecting to it...
$ echo $?
37
```
The server log shows an uncaught `java.lang.IndexOutOfBoundsException: No group 3`
originating in `UrlRewriter.applyRewriteRules` (`Matcher.replaceFirst`).
### Expected behavior
Bazel reports the malformed replacement as a downloader-config parse error and xits with a normal command-line error (exit code 2), naming the offending line, e.g.:
```
ERROR: Failed to parse downloader config at rewrite.cfg:1:0: \
Invalid rewrite pattern `https://mirror.example.com/$1/$2/$3` in `rewrite`: No group 3
```
### Which operating system are you running Bazel on?
Linux
### What is the output of `bazel info release`?
release 9.1.1
### If `bazel info release` returns `development version` or `(@non-git)`, tell us how you built Bazel.
_No response_
### What's the output of `git remote get-url origin; git rev-parse HEAD` ?
```text
git@github.com:jjmaestro/bazel.git
4f6e97e9a020a1305deb50fb2da35d289f5ea9ce
```
### If this is a regression, please try to identify the Bazel commit where the bug was introduced with bazelisk --bisect.
_No response_
### Have you found anything relevant by searching the web?
_No response_
### Any other information, logs, or outputs that you want to share?
This is the same silent-termination failure mode as several earlier downloader-config bugs:
- #20832: an invalid regex in a `rewrite` pattern crashed with exit code 37.
- #28305: a `UrlRewriterParseException` for an unparseable line was wrapped in a `RuntimeException` and surfaced as exit code 37.
Contributor guide
Research direction
Start by reading UrlRewriterConfig parsing and UrlRewriter.applyRewriteRules, then reproduce the failure with the provided MODULE.bazel, rewrite.cfg, and .bazelrc using bazel mod deps. Done means malformed replacements are rejected during downloader-config parsing with a useful file-and-line diagnostic and a normal command-line error instead of exit code 37.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- java
- Domain
- build-system
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Quiet
- Clarity
- Clearly specified
- Newbie friendliness
- 68/100