Skip to content

gh-140006: Harden fish prompt against shadowed builtins#150936

Open
gaborbernat wants to merge 1 commit into
python:mainfrom
gaborbernat:gh-140006-fish-activate-shadowed-builtins
Open

gh-140006: Harden fish prompt against shadowed builtins#150936
gaborbernat wants to merge 1 commit into
python:mainfrom
gaborbernat:gh-140006-fish-activate-shadowed-builtins

Conversation

@gaborbernat
Copy link
Copy Markdown
Contributor

@gaborbernat gaborbernat commented Jun 5, 2026

The activate.fish prompt override restores the previous command's exit status with echo "exit $old_status" | .. fish resolves a function ahead of a builtin, so a user function . (or function source) shadows the source builtin even inside a pipe. The override then pipes exit $old_status into that function instead of sourcing. A dot-style directory navigator that lists the directory turns this into a directory listing on every prompt, and the exit status handed to the original prompt is lost.

This routes every builtin the prompt path uses through builtin: source, echo, printf, set_color, and the functions calls in deactivate and activation. builtin forces builtin resolution, so no user function can intercept them.

Compatibility

Every command goes through a builtin present with a stable interface since fish 2.0.0 (May 2013):

The script already required fish 2.0.0 through functions --copy, so the minimum supported fish version does not change. No version check is added.

Testing

test_venv.BasicTest.test_fish_activate_shadowed_builtins creates a venv, shadows ./source/echo/printf/set_color with functions, renders the prompt, and asserts the exit status is restored and no shadowed function ran. It fails against the current script and passes with this change. Verified on fish 4.1.2 (the reported version) and 4.7.1.

A user function that shadows the `.`/`source` builtin hijacks the
activate.fish prompt. fish resolves functions ahead of builtins, so the
`echo "exit $status" | .` line that restores the exit status pipes into
the user function instead of sourcing. Dot-style directory navigators
redefine `.`, which made the prompt list the directory on every command
and dropped the exit status handed to the original prompt.

Route every builtin the prompt path uses (`source`, `echo`, `printf`,
`set_color`, `functions`) through `builtin` so no user function can
intercept them. These have all been fish builtins with a stable
interface since fish 2.0.0, the version the script already required
through `functions --copy`, so the minimum supported fish version does
not change.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant