Skip to content

Commit e9bf32c

Browse files
authored
Merge pull request #890 from akinomyoga/eval-man
fix(man,info): avoid unexpected code execution and add failglob fixes
2 parents 1cf6fa6 + 9f60090 commit e9bf32c

File tree

6 files changed

+53
-45
lines changed

6 files changed

+53
-45
lines changed

bash_completion

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -580,7 +580,8 @@ _comp__get_cword_at_cursor()
580580
((index < 0)) && index=0
581581
fi
582582

583-
local "$2" "$3" "$4" && _comp_upvars -a${#words[@]} "$2" ${words+"${words[@]}"} \
583+
local IFS=$' \t\n'
584+
local "$2" "$3" "$4" && _comp_upvars -a${#words[@]} "$2" ${words[@]+"${words[@]}"} \
584585
-v "$3" "$cword" -v "$4" "${cur:0:index}"
585586
}
586587

@@ -655,19 +656,19 @@ _comp_get_words()
655656

656657
[[ $vcur ]] && {
657658
upvars+=("$vcur")
658-
upargs+=(-v $vcur "$cur")
659+
upargs+=(-v "$vcur" "$cur")
659660
}
660661
[[ $vcword ]] && {
661662
upvars+=("$vcword")
662-
upargs+=(-v $vcword "$cword")
663+
upargs+=(-v "$vcword" "$cword")
663664
}
664665
[[ $vprev && $cword -ge 1 ]] && {
665666
upvars+=("$vprev")
666-
upargs+=(-v $vprev "${words[cword - 1]}")
667+
upargs+=(-v "$vprev" "${words[cword - 1]}")
667668
}
668669
[[ $vwords ]] && {
669670
upvars+=("$vwords")
670-
upargs+=(-a${#words[@]} $vwords ${words+"${words[@]}"})
671+
upargs+=(-a"${#words[@]}" "$vwords" ${words+"${words[@]}"})
671672
}
672673

673674
((${#upvars[@]})) && local "${upvars[@]}" && _comp_upvars "${upargs[@]}"

completions/info

Lines changed: 16 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -49,26 +49,23 @@ _info()
4949
infopath=$INFOPATH
5050
fi
5151

52-
infopath=$infopath:
53-
if [[ $cur ]]; then
54-
infopath="${infopath//://$cur* }"
55-
else
56-
infopath="${infopath//:// }"
57-
fi
58-
59-
# redirect stderr for when path doesn't exist
60-
COMPREPLY=($(eval command ls "$infopath" 2>/dev/null))
61-
# weed out directory path names and paths to info pages
62-
COMPREPLY=(${COMPREPLY[@]##*/?(:)})
63-
# weed out info dir file
64-
for i in ${!COMPREPLY[*]}; do
65-
[[ ${COMPREPLY[i]} == dir ]] && unset -v 'COMPREPLY[i]'
66-
done
67-
# strip suffix from info pages
68-
COMPREPLY=(${COMPREPLY[@]%.@(gz|bz2|xz|lzma)})
69-
((${#COMPREPLY[@]})) &&
70-
COMPREPLY=($(compgen -W '"${COMPREPLY[@]%.*}"' -- "${cur//\\\\/}"))
52+
_comp_split -F : infopath "$infopath"
53+
if ((${#infopath[@]})); then
54+
infopath=("${infopath[@]/%//$cur*}")
55+
local IFS=
56+
_comp_expand_glob COMPREPLY '${infopath[@]}'
57+
_comp_unlocal IFS
7158

59+
if ((${#COMPREPLY[@]})); then
60+
# weed out directory path names and paths to info pages (empty
61+
# elements will be removed by the later `compgen -X ''`)
62+
COMPREPLY=("${COMPREPLY[@]##*/?(:)}")
63+
# strip suffix from info pages
64+
COMPREPLY=("${COMPREPLY[@]%.@(gz|bz2|xz|lzma)}")
65+
# weed out info dir file with -X 'dir'
66+
_comp_split -l COMPREPLY "$(compgen -W '"${COMPREPLY[@]%.*}"' -X '@(|dir)' -- "${cur//\\\\/}")"
67+
fi
68+
fi
7269
} &&
7370
complete -F _info info pinfo
7471

completions/man

Lines changed: 14 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -77,27 +77,21 @@ _man()
7777
# shellcheck disable=SC2053
7878
[[ $prev == $mansect ]] && sect=$prev || sect='*'
7979

80-
manpath=$manpath:
81-
if [[ $cur ]]; then
82-
manpath="${manpath//://*man$sect/$cur* } ${manpath//://*cat$sect/$cur* }"
83-
else
84-
manpath="${manpath//://*man$sect/ } ${manpath//://*cat$sect/ }"
85-
fi
86-
87-
local IFS=$' \t\n' reset=$(shopt -p failglob)
88-
shopt -u failglob
89-
# redirect stderr for when path doesn't exist
90-
COMPREPLY=($(eval command ls "$manpath" 2>/dev/null))
91-
$reset
80+
_comp_split -F : manpath "$manpath"
81+
if ((${#manpath[@]})); then
82+
manpath=("${manpath[@]/%//*man$sect/$cur*}" "${manpath[@]/%//*cat$sect/$cur*}")
83+
local IFS=
84+
_comp_expand_glob COMPREPLY '${manpath[@]}'
85+
_comp_unlocal IFS
9286

93-
if ((${#COMPREPLY[@]} != 0)); then
94-
# weed out directory path names and paths to man pages
95-
COMPREPLY=(${COMPREPLY[@]##*/?(:)})
96-
# strip suffix from man pages
97-
((${#COMPREPLY[@]})) &&
98-
COMPREPLY=(${COMPREPLY[@]%$comprsuffix})
99-
((${#COMPREPLY[@]})) &&
100-
COMPREPLY=($(compgen -W '"${COMPREPLY[@]%.*}"' -- "${cur//\\\\/}"))
87+
if ((${#COMPREPLY[@]} != 0)); then
88+
# weed out directory path names and paths to man pages (empty
89+
# elements will be removed by the later `compgen -X ''`)
90+
COMPREPLY=("${COMPREPLY[@]##*/?(:)}")
91+
# strip suffix from man pages
92+
COMPREPLY=("${COMPREPLY[@]%$comprsuffix}")
93+
_comp_split -l COMPREPLY "$(compgen -W '"${COMPREPLY[@]%.*}"' -X '' -- "${cur//\\\\/}")"
94+
fi
10195
fi
10296

10397
# shellcheck disable=SC2053

completions/ri

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
_ri_get_methods()
55
{
6-
local regex
6+
local regex IFS=$' \t\n'
77

88
if [[ $ri_version == integrated ]]; then
99
if [[ ! $separator ]]; then

test/t/test_info.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,11 @@ def test_1(self, completion):
1010
@pytest.mark.complete("info -", require_cmd=True)
1111
def test_2(self, completion):
1212
assert completion
13+
14+
@pytest.mark.complete(
15+
"info nonexistent-nam",
16+
env=dict(INFOPATH="'$(echo malicious code >/dev/tty)'"),
17+
)
18+
def test_infopath_code_injection(self, completion):
19+
# no completion, no space appended
20+
assert not completion

test/t/test_man.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,3 +148,11 @@ def test_delimited_deduplication(self, completion):
148148
)
149149
def test_zstd_arbitrary_sectsuffix(self, completion):
150150
assert completion == "e"
151+
152+
@pytest.mark.complete(
153+
"man bash-completion-testcas",
154+
env=dict(MANPATH="'$(echo malicious code >/dev/tty)'"),
155+
)
156+
def test_manpath_code_injection(self, completion):
157+
# no completion, no space appended
158+
assert not completion

0 commit comments

Comments
 (0)