Skip to content

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

Merged
FFY00 merged 1 commit into
python:mainfrom
gaborbernat:gh-140006-fish-activate-shadowed-builtins
Jun 22, 2026
Merged

gh-140006: Harden fish prompt against shadowed builtins#150936
FFY00 merged 1 commit into
python:mainfrom
gaborbernat:gh-140006-fish-activate-shadowed-builtins

Conversation

@gaborbernat

@gaborbernat gaborbernat commented Jun 5, 2026

Copy link
Copy Markdown
Contributor

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.

@FFY00 FFY00 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for looking into this!

@FFY00 FFY00 merged commit 0d540af into python:main Jun 22, 2026
58 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants