summaryrefslogtreecommitdiffstats
path: root/test/t/unit/test_unit_quote_compgen.py
blob: faf23fed9ffec13c4b1dfd4b33f4cedaa5d466d2 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
import os

import pytest

from conftest import assert_bash_exec, assert_complete, bash_env_saved


@pytest.mark.bashcomp(cmd=None, temp_cwd=True)
class TestUnitQuoteCompgen:
    @pytest.fixture(scope="class")
    def functions(self, bash):
        assert_bash_exec(
            bash,
            '_comp__test_quote_compgen() { local REPLY; _comp_quote_compgen "$1"; printf %s "$REPLY"; }',
        )

    @pytest.mark.parametrize(
        "funcname", "_comp__test_quote_compgen quote_readline".split()
    )
    def test_exec(self, bash, functions, funcname):
        assert_bash_exec(bash, "%s '' >/dev/null" % funcname)

    @pytest.mark.parametrize(
        "funcname", "_comp__test_quote_compgen quote_readline".split()
    )
    def test_env_non_pollution(self, bash, functions, funcname):
        """Test environment non-pollution, detected at teardown."""
        assert_bash_exec(
            bash, "foo() { %s meh >/dev/null; }; foo; unset -f foo" % funcname
        )

    @pytest.mark.parametrize(
        "funcname", "_comp__test_quote_compgen quote_readline".split()
    )
    def test_1(self, bash, functions, funcname):
        output = assert_bash_exec(
            bash, "%s '';echo" % funcname, want_output=True
        )
        assert output.strip() == "''"

    @pytest.mark.parametrize(
        "funcname", "_comp__test_quote_compgen quote_readline".split()
    )
    def test_2(self, bash, functions, funcname):
        output = assert_bash_exec(
            bash, "%s foo;echo" % funcname, want_output=True
        )
        assert output.strip() == "foo"

    @pytest.mark.parametrize(
        "funcname", "_comp__test_quote_compgen quote_readline".split()
    )
    def test_3(self, bash, functions, funcname):
        output = assert_bash_exec(
            bash, '%s foo\\"bar;echo' % funcname, want_output=True
        )
        assert output.strip() == 'foo\\"bar'

    @pytest.mark.parametrize(
        "funcname", "_comp__test_quote_compgen quote_readline".split()
    )
    def test_4(self, bash, functions, funcname):
        output = assert_bash_exec(
            bash, "%s '$(echo x >&2)';echo" % funcname, want_output=True
        )
        assert output.strip() == "\\$\\(echo\\ x\\ \\>\\&2\\)"

    def test_github_issue_189_1(self, bash, functions):
        """Test error messages on a certain command line

        Reported at https://github.com/scop/bash-completion/issues/189

        Syntax error messages should not be shown by completion on the
        following line:

          $ ls -- '${[TAB]
          $ rm -- '${[TAB]

        """
        assert_bash_exec(bash, "_comp__test_quote_compgen $'\\'${' >/dev/null")

    def test_github_issue_492_1(self, bash, functions):
        """Test unintended code execution on a certain command line

        Reported at https://github.com/scop/bash-completion/pull/492

        Arbitrary commands could be unintendedly executed by
        _comp_quote_compgen.  In the following example, the command "touch
        1.txt" would be unintendedly created before the fix.  The file "1.txt"
        should not be created by completion on the following line:

          $ echo '$(touch file.txt)[TAB]

        """
        assert_bash_exec(
            bash, "_comp__test_quote_compgen $'\\'$(touch 1.txt)' >/dev/null"
        )
        assert not os.path.exists("./1.txt")

    def test_github_issue_492_2(self, bash, functions):
        """Test the file clear by unintended redirection on a certain command line

        Reported at https://github.com/scop/bash-completion/pull/492

        The file "1.0" should not be created by completion on the following
        line:

          $ awk '$1 > 1.0[TAB]

        """
        assert_bash_exec(
            bash, "_comp__test_quote_compgen $'\\'$1 > 1.0' >/dev/null"
        )
        assert not os.path.exists("./1.0")

    def test_github_issue_492_3(self, bash, functions):
        """Test code execution through unintended pathname expansions

        When there is a file named "quote=$(COMMAND)" (for
        _comp_compgen_filedir) or "REPLY=$(COMMAND)" (for _comp_quote_compgen),
        the completion of the word '$* results in the execution of COMMAND.

          $ echo '$*[TAB]

        """
        os.mkdir("./REPLY=$(echo injected >&2)")
        assert_bash_exec(bash, "_comp__test_quote_compgen $'\\'$*' >/dev/null")

    def test_github_issue_492_4(self, bash, functions):
        """Test error messages through unintended pathname expansions

        When "shopt -s failglob" is set by the user, the completion of the word
        containing glob character and special characters (e.g. TAB) results in
        the failure of pathname expansions.

          $ shopt -s failglob
          $ echo a\\	b*[TAB]

        """
        with bash_env_saved(bash) as bash_env:
            bash_env.shopt("failglob", True)
            assert_bash_exec(
                bash, "_comp__test_quote_compgen $'a\\\\\\tb*' >/dev/null"
            )

    def test_github_issue_526_1(self, bash):
        r"""Regression tests for unprocessed escape sequences after quotes

        Ref [1] https://github.com/scop/bash-completion/pull/492#discussion_r637213822
        Ref [2] https://github.com/scop/bash-completion/pull/526

        The escape sequences in the local variable of "value" in
        "_comp_quote_compgen" needs to be unescaped by passing it to printf as
        the format string.  This causes a problem in the following case [where
        the spaces after "alpha\" is a TAB character inserted in the command
        string by "C-v TAB"]:

          $ echo alpha\   b[TAB]

        """
        os.mkdir("./alpha\tbeta")
        assert (
            assert_complete(
                # Remark on "rendered_cmd": Bash aligns the last character 'b'
                # in the rendered cmd to an "8 x n" boundary using spaces.
                # Here, the command string is assumed to start from column 2
                # because the width of PS1 (conftest.PS1 = '/@') is 2,
                bash,
                "echo alpha\\\026\tb",
                rendered_cmd="echo alpha\\   b",
            )
            == "eta/"
        )