-
Notifications
You must be signed in to change notification settings - Fork 976
Security issue with bash-git-prompt ? #310
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
SrslyJosh just reported on HackerNews that bash-git-prompt is vulnerable. |
The official git-prompt script addresses this issue by storing the branch name in a variable and only substituting the variable once into the final PS1 assignment. Edit: It actually still uses some indirection but achieves the same result. |
As I currently have not that much time I would need some help here, as this is quite a serious issue. |
The first naive approach would be to use replaceSymbols on all branch names and filter out anything but a-z,0-9 and slash, see #312 You could allow more characters (like A-Z) This one at least makes the original pwnage go away. Needs some code review though. |
Some people use '-' and '.' in their branch names. This proposal would create a problem for all these people. The solution should be to not interpret the string containing the branch name. |
How about escaping special characters like $ etc. if found? |
AFAICT the escaping would only last through one level of substitution. |
Here is a simple test we can use by using a simple environment variable substitution. This requires that you have the environment variable EDITOR defined. A good news is that gitstatus.py and gitstatus.sh are ok. As expected, $ is not the only problem. A branch name with back ticks is also a problem.
The prompt shows the content of the directory where is should show the branch name. I had the same problem when using * for untracked branches. The * was interpreted as a glob and replaced by the list of file names in the directory. Are these substitutions performed in the bash-git-prompt script or when $ps1 is substituted by the shell ? |
Yes I know that the correct way would be to make sure that the branch name is not part of any eval - that will be more tricky to implement though, so the quick fix is to remove $ and backtick I guess. The biggest problem is that we make an explicit eval of the STATUS variable on several occasions when updating the prompt. See the |
Ok. Just found it. So the ideal solution would be to get rid of this eval. It is unsafe to use eval with user input. We can't be sure if removing $ and ` is enough. |
Something like this might work diff --git a/gitprompt.sh b/gitprompt.sh
index 9ff5b42..735b595 100755
--- a/gitprompt.sh
+++ b/gitprompt.sh
@@ -512,7 +512,8 @@ function updatePrompt() {
local NEW_PROMPT="$EMPTY_PROMPT"
if [[ -n "$git_status_fields" ]]; then
- local STATUS="${PROMPT_LEADING_SPACE}${GIT_PROMPT_PREFIX}${GIT_PROMPT_BRANCH}${GIT_BRANCH}${ResetColor}"
+ local STATUS_PREFIX="${PROMPT_LEADING_SPACE}${GIT_PROMPT_PREFIX}${GIT_PROMPT_BRANCH}"
+ local STATUS=""
# __add_status KIND VALEXPR INSERT
# eg: __add_status 'STAGED' '-ne 0'
@@ -542,7 +543,6 @@ function updatePrompt() {
eval "STATUS=\"$STATUS$1\""
}
- __add_status '$GIT_UPSTREAM'
__chk_gitvar_status 'REMOTE' '-n'
__add_status "$GIT_PROMPT_SEPARATOR"
__chk_gitvar_status 'STAGED' '-ne 0'
@@ -553,7 +553,7 @@ function updatePrompt() {
__chk_gitvar_status 'CLEAN' '-eq 1' -
__add_status "$ResetColor$GIT_PROMPT_SUFFIX"
- NEW_PROMPT="$(gp_add_virtualenv_to_prompt)$PROMPT_START$($prompt_callback)$STATUS$PROMPT_END"
+ NEW_PROMPT="$(gp_add_virtualenv_to_prompt)$PROMPT_START$($prompt_callback)$STATUS_PREFIX\${GIT_BRANCH}${ResetColor}\${GIT_UPSTREAM}$STATUS$PROMPT_END"
else
NEW_PROMPT="$EMPTY_PROMPT"
fi |
The approach works for the current branch, but not for upstream for me. Upstream won't show. You can test it by setting |
I'm not an expert with bash programming. Why can't we leave the |
In fact the NEW_PROMPT contains the unevaluated branch name. But it is evaluated when displayed as prompt. |
@chmike It's just escaping the $ so it doesn't get expanded until the the assignment of |
Thanks. Here is a fix for the branch which is slightly more elegant. diff --git a/gitprompt.sh b/gitprompt.sh
index 9ff5b42..f2afe9d 100755
--- a/gitprompt.sh
+++ b/gitprompt.sh
@@ -512,7 +512,8 @@ function updatePrompt() {
local NEW_PROMPT="$EMPTY_PROMPT"
if [[ -n "$git_status_fields" ]]; then
- local STATUS="${PROMPT_LEADING_SPACE}${GIT_PROMPT_PREFIX}${GIT_PROMPT_BRANCH}${GIT_BRANCH}${ResetColor}"
+ local STATUS_PREFIX="${PROMPT_LEADING_SPACE}${GIT_PROMPT_PREFIX}${GIT_PROMPT_BRANCH}\${GIT_BRANCH}${ResetColor}"
+ local STATUS=""
# __add_status KIND VALEXPR INSERT
# eg: __add_status 'STAGED' '-ne 0'
@@ -553,7 +554,7 @@ function updatePrompt() {
__chk_gitvar_status 'CLEAN' '-eq 1' -
__add_status "$ResetColor$GIT_PROMPT_SUFFIX"
- NEW_PROMPT="$(gp_add_virtualenv_to_prompt)$PROMPT_START$($prompt_callback)$STATUS$PROMPT_END"
+ NEW_PROMPT="$(gp_add_virtualenv_to_prompt)$PROMPT_START$($prompt_callback)$STATUS_PREFIX$STATUS$PROMPT_END"
else
NEW_PROMPT="$EMPTY_PROMPT"
fi |
I'll try the GIT_UPSTREAM now. I would append it escaped at STATUS_PREFIX. PS: How did you managed to get a colored diff ? That is nicer then mine. |
@chmike Surround the diff with
Not sure how to handle |
I think it's because GIT_UPSTREAM is a local variable and GIT_BRANCH is exported. So when PS1 is evaluated GIT_UPSTREAM is out of scope. If you export it you get the problem with color escape codes. [fredrik@fredriks: ~/Dokument/github/pw3nage] [$(./pw3n) {\[\033[0;34m\]origin/$(./pw3n)\[\033[0;0m\]}|✚ 1] ✔ |
Ah that makes sense. I'm not sure exactly what the rules are for bash evaluation, but I think the best way to evaluate the colors without evaluating the user input is to either filter the upstream name to be safe (no $ or ` etc) and eval the |
EDIT: fixed an error with local variable. It now works as expected. diff --git a/gitprompt.sh b/gitprompt.sh
index 9ff5b42..5aee916 100755
--- a/gitprompt.sh
+++ b/gitprompt.sh
@@ -496,11 +496,10 @@ function updatePrompt() {
unset GIT_REMOTE
fi
- local GIT_UPSTREAM="${git_status_fields[2]}"
+ export GIT_UPSTREAM="${git_status_fields[2]}"
+ local GIT_FORMATED_UPSTREAM="${GIT_PROMPT_UPSTREAM//_UPSTREAM_/\$GIT_UPSTREAM}"
if [[ -z "${__GIT_PROMPT_SHOW_UPSTREAM}" || "^" == "$GIT_UPSTREAM" ]]; then
- unset GIT_UPSTREAM
- else
- GIT_UPSTREAM="${GIT_PROMPT_UPSTREAM//_UPSTREAM_/${GIT_UPSTREAM}}"
+ unset GIT_FORMATED_UPSTREAM
fi
local GIT_STAGED=${git_status_fields[3]}
@@ -512,7 +511,8 @@ function updatePrompt() {
local NEW_PROMPT="$EMPTY_PROMPT"
if [[ -n "$git_status_fields" ]]; then
- local STATUS="${PROMPT_LEADING_SPACE}${GIT_PROMPT_PREFIX}${GIT_PROMPT_BRANCH}${GIT_BRANCH}${ResetColor}"
+ local STATUS_PREFIX="${PROMPT_LEADING_SPACE}${GIT_PROMPT_PREFIX}${GIT_PROMPT_BRANCH}\${GIT_BRANCH}${ResetColor}$GIT_FORMATED_UPSTREAM"
+ local STATUS=""
# __add_status KIND VALEXPR INSERT
# eg: __add_status 'STAGED' '-ne 0'
@@ -542,7 +542,6 @@ function updatePrompt() {
eval "STATUS=\"$STATUS$1\""
}
- __add_status '$GIT_UPSTREAM'
__chk_gitvar_status 'REMOTE' '-n'
__add_status "$GIT_PROMPT_SEPARATOR"
__chk_gitvar_status 'STAGED' '-ne 0'
@@ -553,7 +552,7 @@ function updatePrompt() {
__chk_gitvar_status 'CLEAN' '-eq 1' -
__add_status "$ResetColor$GIT_PROMPT_SUFFIX"
- NEW_PROMPT="$(gp_add_virtualenv_to_prompt)$PROMPT_START$($prompt_callback)$STATUS$PROMPT_END"
+ NEW_PROMPT="$(gp_add_virtualenv_to_prompt)$PROMPT_START$($prompt_callback)$STATUS_PREFIX$STATUS$PROMPT_END"
else
NEW_PROMPT="$EMPTY_PROMPT"
fi |
Good catch to use the unescaped variable in the substitution! Seems to work fine for me, I just did a typo-fix:
|
Sorry, I Edited my answer. It didn't work when displaying a branch without upstream. |
Your fix works if there is a local repo without upstream - bash won't have a problem with an unset variable - it will just not render anything. Couldn't you just continue to unset the GIT_UPSTREAM like before? diff --git a/gitprompt.sh b/gitprompt.sh
index 9ff5b42..03c109a 100755
--- a/gitprompt.sh
+++ b/gitprompt.sh
@@ -496,11 +496,11 @@ function updatePrompt() {
unset GIT_REMOTE
fi
- local GIT_UPSTREAM="${git_status_fields[2]}"
+ export GIT_UPSTREAM="${git_status_fields[2]}"
+ local GIT_FORMATTED_UPSTREAM="${GIT_PROMPT_UPSTREAM//_UPSTREAM_/\$GIT_UPSTREAM}"
if [[ -z "${__GIT_PROMPT_SHOW_UPSTREAM}" || "^" == "$GIT_UPSTREAM" ]]; then
unset GIT_UPSTREAM
- else
- GIT_UPSTREAM="${GIT_PROMPT_UPSTREAM//_UPSTREAM_/${GIT_UPSTREAM}}"
+ unset GIT_FORMATTED_UPSTREAM
fi
local GIT_STAGED=${git_status_fields[3]}
@@ -512,7 +512,8 @@ function updatePrompt() {
local NEW_PROMPT="$EMPTY_PROMPT"
if [[ -n "$git_status_fields" ]]; then
- local STATUS="${PROMPT_LEADING_SPACE}${GIT_PROMPT_PREFIX}${GIT_PROMPT_BRANCH}${GIT_BRANCH}${ResetColor}"
+ local STATUS_PREFIX="${PROMPT_LEADING_SPACE}${GIT_PROMPT_PREFIX}${GIT_PROMPT_BRANCH}\${GIT_BRANCH}${ResetColor}${GIT_FORMATTED_UPSTREAM}"
+ local STATUS=""
# __add_status KIND VALEXPR INSERT
# eg: __add_status 'STAGED' '-ne 0'
@@ -542,7 +543,6 @@ function updatePrompt() {
eval "STATUS=\"$STATUS$1\""
}
- __add_status '$GIT_UPSTREAM'
__chk_gitvar_status 'REMOTE' '-n'
__add_status "$GIT_PROMPT_SEPARATOR"
__chk_gitvar_status 'STAGED' '-ne 0'
@@ -553,7 +553,7 @@ function updatePrompt() {
__chk_gitvar_status 'CLEAN' '-eq 1' -
__add_status "$ResetColor$GIT_PROMPT_SUFFIX"
- NEW_PROMPT="$(gp_add_virtualenv_to_prompt)$PROMPT_START$($prompt_callback)$STATUS$PROMPT_END"
+ NEW_PROMPT="$(gp_add_virtualenv_to_prompt)$PROMPT_START$($prompt_callback)$STATUS_PREFIX$STATUS$PROMPT_END"
else
NEW_PROMPT="$EMPTY_PROMPT"
fi |
I made a PR with these changes, would like for some more people to test it so that nothing that we haven't thought of breaks. |
May I suggest a small refactoring ? local TMP_GIT_UPSTREAM="${git_status_fields[2]}"
if [[ -z "${__GIT_PROMPT_SHOW_UPSTREAM}" || "^" == "$TMP_GIT_UPSTREAM" ]]; then
unset GIT_UPSTREAM
else
export GIT_UPSTREAM="${TMP_GIT_UPSTREAM}"
local GIT_FORMATTED_UPSTREAM="${GIT_PROMPT_UPSTREAM//_UPSTREAM_/\$GIT_UPSTREAM}"
fi |
I updated the PR @chmike, thanks for the feedback. |
Thanks to all for the hints and the fix. |
I have seen this report of possible hack with some shell git prompts. I didn't test if it works with bash-git-prompt.
The trick is to name a git branch as '$(./nastyScript)'. When the shell displays the branch name, the shell will execute nastyScript which should be in the local directory.
Is bash-git-prompt exposed to this risk ?
The text was updated successfully, but these errors were encountered: