Detect infinite recursion in uv run. (#11386)

<!--
Thank you for contributing to uv! To help us out with reviewing, please
consider the following:

- Does this pull request include a summary of the change? (See below.)
- Does this pull request include a descriptive title?
- Does this pull request include references to any relevant issues?
-->

## Summary

Handle potential infinite recursion if `uv run` recursively invokes `uv
run`. This can happen if the shebang line of a script includes `uv run`,
but does not pass `--script`.

Handled by adding a new environment variable `UV_RUN_RECURSION_DEPTH`,
which contains a counter of the number of times that uv run has been
recursively invoked. If unset, it defaults to zero, and each time uv run
starts a subprocess we increment the counter, erroring if the value is
greater than a configurable (but not currently exposed or documented)
threshold.

Closes https://github.com/astral-sh/uv/issues/11220.

## Test Plan

I've added a snapshot test to `uv/crates/uv/tests/it/run` that tests the
end-to-end recursion detection flow. I've currently made it a unix-only
test because I'm not sure offhand how uv run will interact with shebang
lines on windows.

---------

Co-authored-by: Zanie Blue <contact@zanie.dev>
This commit is contained in:
Scott Sanderson 2025-02-12 13:58:43 -05:00 committed by GitHub
parent 81966c43dc
commit 7154800e0c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 111 additions and 0 deletions

View file

@ -2932,6 +2932,15 @@ pub struct RunArgs {
/// By default, environment modifications are omitted, but enabled under `--verbose`.
#[arg(long, env = EnvVars::UV_SHOW_RESOLUTION, value_parser = clap::builder::BoolishValueParser::new(), hide = true)]
pub show_resolution: bool,
/// Number of times that `uv run` will allow recursive invocations.
///
/// The current recursion depth is tracked by environment variable. If environment variables are
/// cleared, uv will fail to detect the recursion depth.
///
/// If uv reaches the maximum recursion depth, it will exit with an error.
#[arg(long, hide = true, env = EnvVars::UV_RUN_MAX_RECURSION_DEPTH)]
pub max_recursion_depth: Option<u32>,
}
#[derive(Args)]