• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

Gallopsled / pwntools / 7171266103

11 Dec 2023 05:41PM UTC coverage: 74.638% (+0.1%) from 74.499%
7171266103

push

github

Arusekk
term: hotfix

4564 of 7217 branches covered (0.0%)

12834 of 17195 relevant lines covered (74.64%)

0.75 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

98.17
/pwnlib/ui.py
1
from __future__ import absolute_import
1✔
2
from __future__ import division
1✔
3

4
import os
1✔
5
import signal
1✔
6
import six
1✔
7
import string
1✔
8
import struct
1✔
9
import subprocess
1✔
10
import sys
1✔
11
import time
1✔
12
import types
1✔
13

14
from pwnlib import term
1✔
15
from pwnlib.log import getLogger
1✔
16
from pwnlib.term.readline import raw_input
1✔
17
from pwnlib.tubes.process import process
1✔
18

19
log = getLogger(__name__)
1✔
20

21
def testpwnproc(cmd):
1✔
22
    import fcntl
1✔
23
    import termios
1✔
24
    env = dict(os.environ)
1✔
25
    env.pop("PWNLIB_NOTERM", None)
1✔
26
    env["TERM"] = "xterm-256color"
1✔
27
    def handleusr1(sig, frame):
1✔
28
        s = p.stderr.read()
×
29
        log.error("child process failed:\n%s", s.decode())
×
30
    signal.signal(signal.SIGUSR1, handleusr1)
1✔
31
    cmd = """\
1✔
32
import os
33
import signal
34
import sys
35
_ehook = sys.excepthook
36
def ehook(*args):
37
    _ehook(*args)
38
    os.kill(os.getppid(), signal.SIGUSR1)
39
sys.excepthook = ehook
40
from pwn import *
41
""" + cmd
42
    if "coverage" in sys.modules:
1!
43
        cmd = "import coverage; coverage.process_startup()\n" + cmd
1✔
44
        env.setdefault("COVERAGE_PROCESS_START", ".coveragerc")
1✔
45
    env['COLUMNS'] = '80'
1✔
46
    env['ROWS'] = '24'
1✔
47
    p = process([sys.executable, "-c", cmd], env=env, stderr=subprocess.PIPE)
1✔
48
    # late initialization can lead to EINTR in many places
49
    fcntl.ioctl(p.stdout.fileno(), termios.TIOCSWINSZ, struct.pack('HH', 24, 80))
1✔
50
    return p
1✔
51

52
def yesno(prompt, default=None):
1✔
53
    r"""Presents the user with prompt (typically in the form of question)
54
    which the user must answer yes or no.
55

56
    Arguments:
57
      prompt (str): The prompt to show
58
      default: The default option;  `True` means "yes"
59

60
    Returns:
61
      `True` if the answer was "yes", `False` if "no"
62

63
    Examples:
64

65
    .. doctest::
66
       :skipif: branch_dev
67

68
        >>> yesno("A number:", 20)
69
        Traceback (most recent call last):
70
        ...
71
        ValueError: yesno(): default must be a boolean or None
72
        >>> saved_stdin = sys.stdin
73
        >>> try:
74
        ...     sys.stdin = io.TextIOWrapper(io.BytesIO(b"x\nyes\nno\n\n"))
75
        ...     yesno("is it good 1")
76
        ...     yesno("is it good 2", True)
77
        ...     yesno("is it good 3", False)
78
        ... finally:
79
        ...     sys.stdin = saved_stdin
80
         [?] is it good 1 [yes/no] Please answer yes or no
81
         [?] is it good 1 [yes/no] True
82
         [?] is it good 2 [Yes/no] False
83
         [?] is it good 3 [yes/No] False
84

85
    Tests:
86

87
    .. doctest::
88
       :skipif: branch_dev
89

90
        >>> p = testpwnproc("print(yesno('is it ok??'))")
91
        >>> b"is it ok" in p.recvuntil(b"??")
92
        True
93
        >>> p.sendline(b"x\nny")
94
        >>> b"True" in p.recvall()
95
        True
96
    """
97

98
    if default is not None and not isinstance(default, bool):
1✔
99
        raise ValueError('yesno(): default must be a boolean or None')
1✔
100

101
    if term.term_mode:
1✔
102
        term.output(' [?] %s [' % prompt)
1✔
103
        yesfocus, yes = term.text.bold('Yes'), 'yes'
1✔
104
        nofocus, no = term.text.bold('No'), 'no'
1✔
105
        hy = term.output(yesfocus if default is True else yes)
1✔
106
        hs = term.output('/')
1✔
107
        hn = term.output(nofocus if default is False else no)
1✔
108
        he = term.output(']\n')
1✔
109
        cur = default
1✔
110
        while True:
1✔
111
            k = term.key.get()
1✔
112
            if   k in ('y', 'Y', '<left>') and cur is not True:
1✔
113
                cur = True
1✔
114
                hy.update(yesfocus)
1✔
115
                hn.update(no)
1✔
116
            elif k in ('n', 'N', '<right>') and cur is not False:
1✔
117
                cur = False
1✔
118
                hy.update(yes)
1✔
119
                hn.update(nofocus)
1✔
120
            elif k == '<enter>':
1✔
121
                if cur is not None:
1✔
122
                    return cur
1✔
123
    else:
124
        prompt = ' [?] %s [%s/%s] ' % (prompt,
1✔
125
                                       'Yes' if default is True else 'yes',
126
                                       'No' if default is False else 'no',
127
                                       )
128
        while True:
1✔
129
            opt = raw_input(prompt).strip().lower()
1✔
130
            if not opt and default is not None:
1✔
131
                return default
1✔
132
            elif opt in (b'y', b'yes'):
1✔
133
                return True
1✔
134
            elif opt in (b'n', b'no'):
1✔
135
                return False
1✔
136
            print('Please answer yes or no')
1✔
137

138
def options(prompt, opts, default = None):
1✔
139
    r"""Presents the user with a prompt (typically in the
140
    form of a question) and a number of options.
141

142
    Arguments:
143
      prompt (str): The prompt to show
144
      opts (list): The options to show to the user
145
      default: The default option to choose
146

147
    Returns:
148
      The users choice in the form of an integer.
149

150
    Examples:
151

152
    .. doctest::
153
       :skipif: branch_dev
154

155
        >>> options("Select a color", ("red", "green", "blue"), "green")
156
        Traceback (most recent call last):
157
        ...
158
        ValueError: options(): default must be a number or None
159

160
    Tests:
161

162
    .. doctest::
163
       :skipif: branch_dev
164

165
        >>> p = testpwnproc("print(options('select a color', ('red', 'green', 'blue')))")
166
        >>> p.sendline(b"\33[C\33[A\33[A\33[B\33[1;5A\33[1;5B 0310")
167
        >>> _ = p.recvall()
168
        >>> saved_stdin = sys.stdin
169
        >>> try:
170
        ...     sys.stdin = io.TextIOWrapper(io.BytesIO(b"\n4\n\n3\n"))
171
        ...     with context.local(log_level="INFO"):
172
        ...         options("select a color A", ("red", "green", "blue"), 0)
173
        ...         options("select a color B", ("red", "green", "blue"))
174
        ... finally:
175
        ...     sys.stdin = saved_stdin
176
         [?] select a color A
177
               1) red
178
               2) green
179
               3) blue
180
             Choice [1] 0
181
         [?] select a color B
182
               1) red
183
               2) green
184
               3) blue
185
             Choice  [?] select a color B
186
               1) red
187
               2) green
188
               3) blue
189
             Choice  [?] select a color B
190
               1) red
191
               2) green
192
               3) blue
193
             Choice 2
194
    """
195

196
    if default is not None and not isinstance(default, six.integer_types):
1✔
197
        raise ValueError('options(): default must be a number or None')
1✔
198

199
    if term.term_mode:
1✔
200
        numfmt = '%' + str(len(str(len(opts)))) + 'd) '
1✔
201
        print(' [?] ' + prompt)
1✔
202
        hs = []
1✔
203
        space = '       '
1✔
204
        arrow = term.text.bold_green('    => ')
1✔
205
        cur = default
1✔
206
        for i, opt in enumerate(opts):
1✔
207
            h = term.output(arrow if i == cur else space, frozen = False)
1✔
208
            num = numfmt % (i + 1)
1✔
209
            h1 = term.output(num)
1✔
210
            h2 = term.output(opt + '\n', indent = len(num) + len(space))
1✔
211
            hs.append((h, h1, h2))
1✔
212
        ds = ''
1✔
213
        while True:
1✔
214
            prev = cur
1✔
215
            was_digit = False
1✔
216
            k = term.key.get()
1✔
217
            if   k == '<up>':
1✔
218
                if cur is None:
1✔
219
                    cur = 0
1✔
220
                else:
221
                    cur = max(0, cur - 1)
1✔
222
            elif k == '<down>':
1✔
223
                if cur is None:
1!
224
                    cur = 0
×
225
                else:
226
                    cur = min(len(opts) - 1, cur + 1)
1✔
227
            elif k == 'C-<up>':
1✔
228
                cur = 0
1✔
229
            elif k == 'C-<down>':
1✔
230
                cur = len(opts) - 1
1✔
231
            elif k in ('<enter>', '<right>'):
1✔
232
                if cur is not None:
1✔
233
                    return cur
1✔
234
            elif k in tuple(string.digits):
1✔
235
                was_digit = True
1✔
236
                d = str(k)
1✔
237
                n = int(ds + d)
1✔
238
                if 0 < n <= len(opts):
1✔
239
                    ds += d
1✔
240
                    cur = n - 1
1✔
241
                elif d != '0':
1✔
242
                    ds = d
1✔
243
                    n = int(ds)
1✔
244
                    cur = n - 1
1✔
245

246
            if prev != cur:
1✔
247
                if prev is not None:
1✔
248
                    hs[prev][0].update(space)
1✔
249
                if was_digit:
1✔
250
                    hs[cur][0].update(term.text.bold_green('%5s> ' % ds))
1✔
251
                else:
252
                    hs[cur][0].update(arrow)
1✔
253
    else:
254
        linefmt =       '       %' + str(len(str(len(opts)))) + 'd) %s'
1✔
255
        if default is not None:
1✔
256
            default += 1
1✔
257
        while True:
1✔
258
            print(' [?] ' + prompt)
1✔
259
            for i, opt in enumerate(opts):
1✔
260
                print(linefmt % (i + 1, opt))
1✔
261
            s = '     Choice '
1✔
262
            if default:
1✔
263
                s += '[%s] ' % str(default)
1✔
264
            try:
1✔
265
                x = int(raw_input(s) or default)
1✔
266
            except (ValueError, TypeError):
1✔
267
                continue
1✔
268
            if x >= 1 and x <= len(opts):
1✔
269
                return x - 1
1✔
270

271
def pause(n=None):
1✔
272
    r"""Waits for either user input or a specific number of seconds.
273

274
    Examples:
275

276
    .. doctest::
277
       :skipif: branch_dev
278

279
        >>> with context.local(log_level="INFO"):
280
        ...     pause(1)
281
        [x] Waiting
282
        [x] Waiting: 1...
283
        [+] Waiting: Done
284
        >>> pause("whatever")
285
        Traceback (most recent call last):
286
        ...
287
        ValueError: pause(): n must be a number or None
288

289
    Tests:
290

291
    .. doctest::
292
       :skipif: branch_dev
293

294
        >>> saved_stdin = sys.stdin
295
        >>> try:
296
        ...     sys.stdin = io.TextIOWrapper(io.BytesIO(b"\n"))
297
        ...     with context.local(log_level="INFO"):
298
        ...         pause()
299
        ... finally:
300
        ...     sys.stdin = saved_stdin
301
        [*] Paused (press enter to continue)
302
        >>> p = testpwnproc("pause()")
303
        >>> b"Paused" in p.recvuntil(b"press any")
304
        True
305
        >>> p.send(b"x")
306
        >>> _ = p.recvall()
307
    """
308

309
    if n is None:
1✔
310
        if term.term_mode:
1✔
311
            log.info('Paused (press any to continue)')
1✔
312
            term.getkey()
1✔
313
        else:
314
            log.info('Paused (press enter to continue)')
1✔
315
            raw_input('')
1✔
316
    elif isinstance(n, six.integer_types):
1✔
317
        with log.waitfor("Waiting") as l:
1✔
318
            for i in range(n, 0, -1):
1✔
319
                l.status('%d... ' % i)
1✔
320
                time.sleep(1)
1✔
321
            l.success()
1✔
322
    else:
323
        raise ValueError('pause(): n must be a number or None')
1✔
324

325
def more(text):
1✔
326
    r"""more(text)
327

328
    Shows text like the command line tool ``more``.
329

330
    It not in term_mode, just prints the data to the screen.
331

332
    Arguments:
333
      text(str):  The text to show.
334

335
    Returns:
336
      :const:`None`
337

338
    Tests:
339

340
    .. doctest::
341
       :skipif: branch_dev
342
       
343
        >>> more("text")
344
        text
345
        >>> p = testpwnproc("more('text\\n' * (term.height + 2))")
346
        >>> p.send(b"x")
347
        >>> data = p.recvall()
348
        >>> b"text" in data or data
349
        True
350
    """
351
    if term.term_mode:
1✔
352
        lines = text.split('\n')
1✔
353
        h = term.output(term.text.reverse('(more)'), float = True, frozen = False)
1✔
354
        step = term.height - 1
1✔
355
        for i in range(0, len(lines), step):
1✔
356
            for l in lines[i:i + step]:
1✔
357
                print(l)
1✔
358
            if i + step < len(lines):
1✔
359
                term.key.get()
1✔
360
    else:
361
        print(text)
1✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc