Skip to content

Commit 877fa52

Browse files
authored
Merge pull request #91 from Wenzel/e2e_testing
E2e testing
2 parents efece27 + 9b7dd70 commit 877fa52

File tree

11 files changed

+155
-138
lines changed

11 files changed

+155
-138
lines changed

.github/workflows/ci.yml

+8-9
Original file line numberDiff line numberDiff line change
@@ -99,10 +99,15 @@ jobs:
9999

100100
test:
101101
needs: build
102-
runs-on: ubuntu-latest
102+
strategy:
103+
matrix:
104+
os: [ubuntu-latest, windows-latest]
105+
runs-on: ${{ matrix.os }}
103106

104107
steps:
105-
- uses: actions/checkout@v1
108+
- uses: actions/checkout@v2
109+
with:
110+
submodules: true
106111

107112
- name: Set up Python 3.7 🐍
108113
uses: actions/setup-python@v1
@@ -113,13 +118,7 @@ jobs:
113118
run: python -m pip install nox==2020.8.22
114119

115120
- name: Run tests
116-
run: nox -s test
117-
118-
- name: Install package 📦
119-
run: python -m pip install .
120-
121-
- name: Run smoke test
122-
run: checksec /usr/lib
121+
run: nox -s test_e2e -- -s
123122

124123
release:
125124
needs: test

.gitmodules

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
[submodule "tests/binaries"]
2+
path = tests/binaries
3+
url = git@github.com:Wenzel/checksec.py-test-binaries.git

noxfile.py

+24-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import nox
22

3-
nox.options.sessions = ["fmt", "lint", "test"]
3+
nox.options.sessions = ["fmt", "lint", "test_e2e"]
44

55

66
@nox.session
@@ -31,7 +31,28 @@ def run(session):
3131

3232

3333
@nox.session
34-
def test(session):
34+
def test_unit(session):
35+
# run unit tests
36+
args = session.posargs
3537
session.install("-r", "requirements.txt")
3638
session.install("pytest==6.0.2", "coverage==5.3")
37-
session.run("coverage", "run", "-m", "pytest", "-v")
39+
session.run("coverage", "run", "--concurrency=multiprocessing", "-m", "pytest", "-v", "-k", "unit", *args)
40+
session.run("coverage", "combine")
41+
session.run("coverage", "report")
42+
43+
44+
@nox.session
45+
def test_e2e(session):
46+
args = session.posargs
47+
session.install(".")
48+
session.install("pytest==6.0.2", "coverage==5.3")
49+
session.run("coverage", "run", "--concurrency=multiprocessing", "-m", "pytest", "-v", "-k", "e2e", *args)
50+
session.run("coverage", "combine")
51+
session.run("coverage", "report")
52+
53+
54+
@nox.session
55+
def coverage(session):
56+
args = session.posargs if session.posargs else ["report"]
57+
session.install("coverage==5.3")
58+
session.run("coverage", *args)

setup.cfg

+7
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,10 @@ max-line-length=120
33
exclude = .git, .nox, venv, build, dist
44
# black compatibility
55
extend-ignore = E203
6+
7+
[coverage:run]
8+
# measure branch coverage in addition to statement coverage
9+
branch = True
10+
# restrict measurement to avoid .nox/*
11+
source =
12+
checksec

setup.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
import setuptools
22

3-
with open("README.md", "r") as fh:
3+
with open("README.md", "r", encoding="utf-8") as fh:
44
long_description = fh.read()
55

66
with open("requirements.txt") as f:
77
requirements = f.read().splitlines()
88

99
setuptools.setup(
1010
name="checksec.py",
11-
version="0.5.0",
11+
version="0.5.1",
1212
author="Mathieu Tarral",
1313
author_email="mathieu.tarral@protonmail.com",
1414
description="Checksec tool implemented in Python",

tests/binaries

Submodule binaries added at 1b842c5

tests/conftest.py

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import json
2+
import subprocess
3+
from pathlib import Path
4+
from typing import Dict
5+
6+
7+
def run_checksec(bin_path: Path, libc_path: Path = None) -> Dict:
8+
"""Runs checksec from command line, returns json output as dict"""
9+
cmd = ["checksec", str(bin_path), "-j"]
10+
if libc_path:
11+
cmd.extend(["-s", str(libc_path)])
12+
output = subprocess.check_output(cmd)
13+
return json.loads(output.decode())

tests/e2e/test_e2e_elf.py

+66
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
"""E2E tests for ELF"""
2+
3+
from pathlib import Path
4+
5+
import pytest
6+
7+
from checksec.elf import PIEType, RelroType
8+
from tests.conftest import run_checksec
9+
10+
ELF_BINARIES = Path(__file__).parent.parent / "binaries" / "elf"
11+
12+
13+
@pytest.mark.parametrize("is_enabled", [False, True])
14+
@pytest.mark.parametrize("prop", ["nx", "canary", "rpath", "runpath", "symbols", "fortify_source"])
15+
def test_bool_prop(prop: str, is_enabled: bool):
16+
"""Test that boolean prop is disabled/enabled"""
17+
libc_path = ELF_BINARIES / "libc-2.27.so"
18+
bin_path = ELF_BINARIES / f"{prop}_{'enabled' if is_enabled else 'disabled'}"
19+
chk_data = run_checksec(bin_path, libc_path)
20+
assert chk_data[str(bin_path)][prop] == is_enabled
21+
22+
23+
@pytest.mark.parametrize("relro_type", list(RelroType))
24+
def test_relro(relro_type: RelroType):
25+
"""Test that relro type is No/Partial/Full"""
26+
bin_path = ELF_BINARIES / f"relro_{relro_type.name.lower()}"
27+
chk_data = run_checksec(bin_path)
28+
assert chk_data[str(bin_path)]["relro"] == relro_type.name
29+
30+
31+
@pytest.mark.parametrize("pie_type", list(PIEType))
32+
def test_pie(pie_type):
33+
"""Test that PIE is No/Partial/Full"""
34+
bin_path = ELF_BINARIES / f"pie_{pie_type.name.lower()}"
35+
chk_data = run_checksec(bin_path)
36+
assert chk_data[str(bin_path)]["pie"] == pie_type.name
37+
38+
39+
def test_fortified():
40+
"""Test the fortified functions"""
41+
libc_path = ELF_BINARIES / "libc-2.27.so"
42+
bin_path = ELF_BINARIES / "fortify_funcs"
43+
chk_data = run_checksec(bin_path, libc_path)
44+
fortified_funcs = ["__fprintf_chk@@GLIBC_2.3.4", "__printf_chk@@GLIBC_2.3.4"]
45+
assert chk_data[str(bin_path)]["fortified"] == len(fortified_funcs)
46+
47+
48+
def test_fortifiable():
49+
"""Test the fortifiable functions"""
50+
libc_path = ELF_BINARIES / "libc-2.27.so"
51+
bin_path = ELF_BINARIES / "fortify_funcs"
52+
chk_data = run_checksec(bin_path, libc_path)
53+
fortified_funcs = ["__fprintf_chk@@GLIBC_2.3.4", "__printf_chk@@GLIBC_2.3.4"]
54+
non_fortified_funcs = ["fgets"]
55+
assert chk_data[str(bin_path)]["fortify-able"] == len(fortified_funcs) + len(non_fortified_funcs)
56+
57+
58+
def test_fortify_score():
59+
"""Test the fortify score"""
60+
libc_path = ELF_BINARIES / "libc-2.27.so"
61+
bin_path = ELF_BINARIES / "fortify_funcs"
62+
chk_data = run_checksec(bin_path, libc_path)
63+
fortified_funcs = ["__fprintf_chk@@GLIBC_2.3.4", "__printf_chk@@GLIBC_2.3.4"]
64+
non_fortified_funcs = ["fgets"]
65+
total = len(fortified_funcs) + len(non_fortified_funcs)
66+
assert chk_data[str(bin_path)]["fortify_score"] == round((2 * 100) / total, 0)

tests/e2e/test_e2e_pe.py

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
"""E2E tests for PE"""
2+
3+
from pathlib import Path
4+
5+
import pytest
6+
7+
from tests.conftest import run_checksec
8+
9+
PE_BINARIES = Path(__file__).parent.parent / "binaries" / "pe"
10+
11+
12+
@pytest.mark.parametrize("is_enabled", [False, True])
13+
@pytest.mark.parametrize(
14+
"prop",
15+
[
16+
"nx",
17+
"canary",
18+
"dynamic_base",
19+
"aslr",
20+
"high_entropy_va",
21+
"safe_seh",
22+
"force_integrity",
23+
"guard_cf",
24+
"isolation",
25+
],
26+
)
27+
def test_bool_prop(prop: str, is_enabled: bool):
28+
"""Test that boolean prop is disabled/enabled"""
29+
bin_path = PE_BINARIES / f"{prop}_{'enabled' if is_enabled else 'disabled'}.exe"
30+
chk_data = run_checksec(bin_path)
31+
assert chk_data[str(bin_path)][prop] == is_enabled

tests/main.c

-26
This file was deleted.

tests/test_elf.py

-98
This file was deleted.

0 commit comments

Comments
 (0)