Skip to content

Commit 6a86578

Browse files
authored
Merge branch 'dev' into windows_process_recv
2 parents 757d027 + fac8f1e commit 6a86578

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+279
-63
lines changed

.github/workflows/ci.yml

+15
Original file line numberDiff line numberDiff line change
@@ -284,10 +284,25 @@ jobs:
284284
pip install --upgrade pip
285285
pip install --upgrade --editable .
286286
287+
- name: Install documentation dependencies
288+
run: pip install -r docs/requirements.txt
289+
287290
- name: Sanity checks
288291
run: |
289292
python -bb -c 'from pwn import *'
290293
python -bb examples/text.py
294+
295+
- name: Coverage doctests
296+
run: |
297+
python -bb -m coverage run -m sphinx -b doctest docs/source docs/build/doctest
298+
299+
# FIXME: Paths are broken when uploading coverage on ubuntu
300+
# coverage.exceptions.NoSource: No source for code: '/home/runner/work/pwntools/pwntools/D:\a\pwntools\pwntools\pwn\__init__.py'.
301+
# - uses: actions/upload-artifact@v4
302+
# with:
303+
# name: coverage-windows
304+
# path: .coverage*
305+
# include-hidden-files: true
291306

292307
upload-coverage:
293308
runs-on: ubuntu-latest

CHANGELOG.md

+4
Original file line numberDiff line numberDiff line change
@@ -74,8 +74,12 @@ The table below shows which release corresponds to each branch, and what date th
7474

7575
## 5.0.0 (`dev`)
7676

77+
- [#2507][2507] Add `+LINUX` and `+WINDOWS` doctest options and start proper testing on Windows
78+
- [#2522][2522] Support starting a kitty debugging window with the 'kitten' command
7779
- [#2524][2524] Raise EOFError during `process.recv` when stdout closes on Windows
7880

81+
[2507]: https://github.com/Gallopsled/pwntools/pull/2507
82+
[2522]: https://github.com/Gallopsled/pwntools/pull/2522
7983
[2524]: https://github.com/Gallopsled/pwntools/pull/2524
8084

8185
## 4.15.0 (`beta`)

docs/requirements.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,6 @@ psutil
1919
requests>=2.5.1
2020
ropgadget>=5.3
2121
sphinx==1.8.6; python_version<'3'
22-
sphinx>=7.0.0; python_version>='3'
22+
sphinx>=8.1.3, <9; python_version>='3'
2323
sphinx_rtd_theme
2424
sphinxcontrib-autoprogram<=0.1.5

docs/source/adb.rst

+3
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@
44
from pwn import *
55
adb = pwnlib.adb
66

7+
import doctest
8+
doctest_additional_flags = doctest.OPTIONFLAGS_BY_NAME['LINUX']
9+
710
:mod:`pwnlib.adb` --- Android Debug Bridge
811
=====================================================
912

docs/source/asm.rst

+4
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@
44
import subprocess
55
from pwn import *
66

7+
# TODO: Remove global POSIX flag
8+
import doctest
9+
doctest_additional_flags = doctest.OPTIONFLAGS_BY_NAME['POSIX']
10+
711
:mod:`pwnlib.asm` --- Assembler functions
812
=========================================
913

docs/source/conf.py

+76-8
Original file line numberDiff line numberDiff line change
@@ -398,6 +398,15 @@ def dont_skip_any_doctests(app, what, name, obj, skip, options):
398398

399399
class _DummyClass(object): pass
400400

401+
# doctest optionflags for platform-specific tests
402+
# they are skipped on other platforms
403+
WINDOWS = doctest.register_optionflag('WINDOWS')
404+
LINUX = doctest.register_optionflag('LINUX')
405+
POSIX = doctest.register_optionflag('POSIX')
406+
407+
# doctest optionflag for tests that haven't been looked at yet
408+
TODO = doctest.register_optionflag('TODO')
409+
401410
class Py2OutputChecker(_DummyClass, doctest.OutputChecker):
402411
def check_output(self, want, got, optionflags):
403412
sup = super(Py2OutputChecker, self).check_output
@@ -425,27 +434,86 @@ def check_output(self, want, got, optionflags):
425434
return False
426435
return True
427436

437+
import sphinx.ext.doctest
438+
439+
class PlatformDocTestRunner(sphinx.ext.doctest.SphinxDocTestRunner):
440+
def run(self, test, compileflags=None, out=None, clear_globs=True):
441+
original_optionflags = self.optionflags | test.globs.get('doctest_additional_flags', 0)
442+
def filter_platform(example):
443+
optionflags = original_optionflags
444+
if example.options:
445+
for (optionflag, val) in example.options.items():
446+
if val:
447+
optionflags |= optionflag
448+
else:
449+
optionflags &= ~optionflag
450+
451+
if (optionflags & WINDOWS) == WINDOWS and sys.platform != 'win32':
452+
return False
453+
if (optionflags & LINUX) == LINUX and sys.platform != 'linux':
454+
return False
455+
if (optionflags & POSIX) == POSIX and os.name != 'posix':
456+
return False
457+
return True
458+
459+
test.examples[:] = [example for example in test.examples if filter_platform(example)]
460+
461+
return super(PlatformDocTestRunner, self).run(test, compileflags, out, clear_globs)
462+
463+
class PlatformDocTestBuilder(sphinx.ext.doctest.DocTestBuilder):
464+
_test_runner = None
465+
466+
@property
467+
def test_runner(self):
468+
return self._test_runner
469+
470+
@test_runner.setter
471+
def test_runner(self, value):
472+
self._test_runner = PlatformDocTestRunner(value._checker, value._verbose, value.optionflags)
473+
428474
def py2_doctest_init(self, checker=None, verbose=None, optionflags=0):
429475
if checker is None:
430476
checker = Py2OutputChecker()
431477
doctest.DocTestRunner.__init__(self, checker, verbose, optionflags)
432478

433479
if 'doctest' in sys.argv:
434-
def setup(app):
435-
pass # app.connect('autodoc-skip-member', dont_skip_any_doctests)
436480

437481
if sys.version_info[:1] < (3,):
438-
import sphinx.ext.doctest
439482
sphinx.ext.doctest.SphinxDocTestRunner.__init__ = py2_doctest_init
440483
else:
484+
def setup(app):
485+
app.add_builder(PlatformDocTestBuilder, override=True)
486+
# app.connect('autodoc-skip-member', dont_skip_any_doctests)
441487
# monkey patching paramiko due to https://github.com/paramiko/paramiko/pull/1661
442488
import paramiko.client
443489
import binascii
444490
paramiko.client.hexlify = lambda x: binascii.hexlify(x).decode()
445491
paramiko.util.safe_string = lambda x: '' # function result never *actually used*
446492
class EndlessLoop(Exception): pass
447-
def alrm_handler(sig, frame):
448-
signal.alarm(180) # three minutes
449-
raise EndlessLoop()
450-
signal.signal(signal.SIGALRM, alrm_handler)
451-
signal.alarm(600) # ten minutes
493+
if hasattr(signal, 'alarm'):
494+
def alrm_handler(sig, frame):
495+
signal.alarm(180) # three minutes
496+
raise EndlessLoop()
497+
signal.signal(signal.SIGALRM, alrm_handler)
498+
signal.alarm(600) # ten minutes
499+
else:
500+
def sigabrt_handler(signum, frame):
501+
raise EndlessLoop()
502+
# thread.interrupt_main received the signum parameter in Python 3.10
503+
if sys.version_info >= (3, 10):
504+
signal.signal(signal.SIGABRT, sigabrt_handler)
505+
def alrm_handler():
506+
try:
507+
import thread
508+
except ImportError:
509+
import _thread as thread
510+
# pre Python 3.10 this raises a KeyboardInterrupt in the main thread.
511+
# it might not show a traceback in that case, but it will stop the endless loop.
512+
thread.interrupt_main(signal.SIGABRT)
513+
timer = threading.Timer(interval=180, function=alrm_handler) # three minutes
514+
timer.daemon = True
515+
timer.start()
516+
import threading
517+
timer = threading.Timer(interval=600, function=alrm_handler) # ten minutes
518+
timer.daemon = True
519+
timer.start()

docs/source/elf/corefile.rst

+3
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@
1818
# Set the environment here so it's not in the middle of our tests.
1919
os.environ.setdefault('SHELL', '/bin/sh')
2020

21+
import doctest
22+
doctest_additional_flags = doctest.OPTIONFLAGS_BY_NAME['POSIX']
23+
2124

2225
:mod:`pwnlib.elf.corefile` --- Core Files
2326
===========================================================

docs/source/elf/elf.rst

+4
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,10 @@
55
from pwnlib.elf.maps import CAT_PROC_MAPS_EXIT
66
import shutil
77

8+
# TODO: Remove global POSIX flag
9+
import doctest
10+
doctest_additional_flags = doctest.OPTIONFLAGS_BY_NAME['POSIX']
11+
812
:mod:`pwnlib.elf.elf` --- ELF Files
913
===========================================================
1014

docs/source/encoders.rst

+4
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
.. testsetup:: *
22

33
from pwn import *
4+
5+
# TODO: Remove global POSIX flag
6+
import doctest
7+
doctest_additional_flags = doctest.OPTIONFLAGS_BY_NAME['POSIX']
48

59
:mod:`pwnlib.encoders` --- Encoding Shellcode
610
===============================================

docs/source/filesystem.rst

+4
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@
66
from pwnlib.tubes.ssh import ssh
77
from pwnlib.filesystem import *
88

9+
# TODO: Remove global POSIX flag
10+
import doctest
11+
doctest_additional_flags = doctest.OPTIONFLAGS_BY_NAME['POSIX']
12+
913
:mod:`pwnlib.filesystem` --- Manipulating Files Locally and Over SSH
1014
====================================================================
1115

docs/source/gdb.rst

+4
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@
44
context.arch = 'amd64'
55
context.terminal = [os.path.join(os.path.dirname(pwnlib.__file__), 'gdb_faketerminal.py')]
66

7+
# TODO: Test on cygwin too
8+
import doctest
9+
doctest_additional_flags = doctest.OPTIONFLAGS_BY_NAME['POSIX']
10+
711
:mod:`pwnlib.gdb` --- Working with GDB
812
======================================
913

docs/source/intro.rst

+3
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22

33
from pwn import *
44

5+
import doctest
6+
doctest_additional_flags = doctest.OPTIONFLAGS_BY_NAME['POSIX']
7+
58
Getting Started
69
========================
710

docs/source/libcdb.rst

+4
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,10 @@
33
from pwn import *
44
from pwnlib.libcdb import *
55

6+
# TODO: Remove global POSIX flag
7+
import doctest
8+
doctest_additional_flags = doctest.OPTIONFLAGS_BY_NAME['POSIX']
9+
610
:mod:`pwnlib.libcdb` --- Libc Database
711
===========================================
812

docs/source/qemu.rst

+4
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22

33
from pwn import *
44

5+
# TODO: Remove global POSIX flag
6+
import doctest
7+
doctest_additional_flags = doctest.OPTIONFLAGS_BY_NAME['POSIX']
8+
59

610
:mod:`pwnlib.qemu` --- QEMU Utilities
711
==========================================

docs/source/rop/rop.rst

+4
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,10 @@
1919

2020
context.clear()
2121

22+
# TODO: Remove global LINUX flag
23+
import doctest
24+
doctest_additional_flags = doctest.OPTIONFLAGS_BY_NAME['LINUX']
25+
2226

2327
:mod:`pwnlib.rop.rop` --- Return Oriented Programming
2428
==========================================================

docs/source/rop/srop.rst

+3
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@
77
from pwnlib.elf import ELF
88
from pwnlib.tubes.process import process
99

10+
import doctest
11+
doctest_additional_flags = doctest.OPTIONFLAGS_BY_NAME['LINUX']
12+
1013
:mod:`pwnlib.rop.srop` --- Sigreturn Oriented Programming
1114
==========================================================
1215

docs/source/runner.rst

+4
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,10 @@
33
from pwnlib.runner import *
44
from pwnlib.asm import asm
55

6+
# TODO: Remove global POSIX flag
7+
import doctest
8+
doctest_additional_flags = doctest.OPTIONFLAGS_BY_NAME['POSIX']
9+
610
:mod:`pwnlib.runner` --- Running Shellcode
711
===========================================
812

docs/source/shellcraft.rst

+4
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22

33
from pwnlib import shellcraft
44

5+
# TODO: Remove global POSIX flag
6+
import doctest
7+
doctest_additional_flags = doctest.OPTIONFLAGS_BY_NAME['POSIX']
8+
59
:mod:`pwnlib.shellcraft` --- Shellcode generation
610
=================================================
711

docs/source/shellcraft/aarch64.rst

+3
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@
33
from pwn import *
44
context.clear(arch='aarch64')
55

6+
import doctest
7+
doctest_additional_flags = doctest.OPTIONFLAGS_BY_NAME['LINUX']
8+
69
:mod:`pwnlib.shellcraft.aarch64` --- Shellcode for AArch64
710
===========================================================
811

docs/source/shellcraft/amd64.rst

+4
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,10 @@
33
from pwn import *
44
context.clear(arch='amd64')
55

6+
# TODO: POSIX/WINDOWS shellcode test
7+
import doctest
8+
doctest_additional_flags = doctest.OPTIONFLAGS_BY_NAME['LINUX']
9+
610
:mod:`pwnlib.shellcraft.amd64` --- Shellcode for AMD64
711
===========================================================
812

docs/source/shellcraft/arm.rst

+3
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@
33
from pwn import *
44
context.clear(arch='arm')
55

6+
import doctest
7+
doctest_additional_flags = doctest.OPTIONFLAGS_BY_NAME['LINUX']
8+
69
:mod:`pwnlib.shellcraft.arm` --- Shellcode for ARM
710
===========================================================
811

docs/source/shellcraft/i386.rst

+3
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@
33
from pwn import *
44
context.clear(arch='i386')
55

6+
import doctest
7+
doctest_additional_flags = doctest.OPTIONFLAGS_BY_NAME['POSIX']
8+
69
:mod:`pwnlib.shellcraft.i386` --- Shellcode for Intel 80386
710
===========================================================
811

docs/source/shellcraft/mips.rst

+3
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@
1212

1313
context.clear(arch='mips')
1414

15+
import doctest
16+
doctest_additional_flags = doctest.OPTIONFLAGS_BY_NAME['LINUX']
17+
1518
:mod:`pwnlib.shellcraft.mips` --- Shellcode for MIPS
1619
===========================================================
1720

docs/source/shellcraft/riscv64.rst

+3
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@
33
from pwn import *
44
context.clear(arch='riscv64')
55

6+
import doctest
7+
doctest_additional_flags = doctest.OPTIONFLAGS_BY_NAME['LINUX']
8+
69
:mod:`pwnlib.shellcraft.riscv64` --- Shellcode for RISCV64
710
==========================================================
811

docs/source/shellcraft/thumb.rst

+3
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@
33
from pwn import *
44
context.clear(arch='thumb')
55

6+
import doctest
7+
doctest_additional_flags = doctest.OPTIONFLAGS_BY_NAME['LINUX']
8+
69
:mod:`pwnlib.shellcraft.thumb` --- Shellcode for Thumb Mode
710
===========================================================
811

docs/source/tubes/processes.rst

+4
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22

33
from pwn import *
44

5+
# TODO: Remove global POSIX flag
6+
import doctest
7+
doctest_additional_flags = doctest.OPTIONFLAGS_BY_NAME['POSIX']
8+
59
:mod:`pwnlib.tubes.process` --- Processes
610
===========================================================
711

0 commit comments

Comments
 (0)