Skip to content

Commit

Permalink
adding tests and automated workflow for pytest
Browse files Browse the repository at this point in the history
  • Loading branch information
clearbluejar committed Sep 9, 2023
1 parent 0f093bc commit bd7fb68
Show file tree
Hide file tree
Showing 7 changed files with 256 additions and 13 deletions.
4 changes: 2 additions & 2 deletions .devcontainer/post-create.sh
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ if uname -a | grep -q 'aarch64'; then
$GHIDRA_INSTALL_DIR/support/buildNatives
fi

# install local workspace
pip install -e .
# install local workspace and test requirements
pip install -e ".[testing]"

# Setup Ghidra Dev for Reference
# git clone https://github.com/NationalSecurityAgency/ghidra.git ~/ghidra-master
Expand Down
45 changes: 45 additions & 0 deletions .github/workflows/lint-python-package.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# This workflow will install Python dependencies, run tests and lint with a variety of Python versions
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python

name: Lint Python Package

on:
push:
branches: [ "pytest" ]
# pull_request:
# branches: [ "main" ]
workflow_dispatch:

jobs:
build:

runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
python-version: ["3.11"]

steps:
- uses: actions/checkout@v3
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v3
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
run: |
python -m pip install --upgrade pip
python -m pip install autopep8 flake8 pytest
if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
- name: Install package
run: |
python -m pip install -e .
- name: Lint with flake8
run: |
# stop the build if there are Python syntax errors or undefined names
flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics
# exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide
flake8 . --count --exit-zero --max-complexity=10 --max-line-length=130 --statistics --ignore F405,F401,F403
- name: Lint with autopep8
run: |
# stop the build if there are Python syntax errors or undefined names
autopep8 -r . -d
34 changes: 34 additions & 0 deletions .github/workflows/pytest-devcontainer.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# This workflow will install Python dependencies, run tests and lint with a variety of Python versions
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python

name: Pytest Python Package In Devcontainer

on:
push:
branches: [ "main", "pytest" ]
pull_request:
branches: [ "main" ]
workflow_dispatch:

jobs:
test:

runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
image: ["latest", "10.1.5ghidra3.11python-bookworm"]

steps:
- uses: actions/checkout@v3
- name: Test with pytest on devcontainer
uses: devcontainers/[email protected]
with:
imageName: ghcr.io/clearbluejar/ghidra-python:${{matrix.image}}
cacheFrom: ghcr.io/clearbluejar/ghidra-python:${{matrix.image}}
push: never
runCmd: |
pip install --upgrade pip
pip install -e .
pip install -r tests/pytest-requirements.txt
pytest -rA
3 changes: 0 additions & 3 deletions requirements.txt

This file was deleted.

116 changes: 116 additions & 0 deletions tests/test_diff.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
from pathlib import Path

from ghidriff.ghidra_diff_engine import GhidraDiffEngine

from ghidriff import get_parser, get_engine_classes, VersionTrackingDiff

import requests
import json
import os

def get_chrome_headers() -> dict:

headers = {
"accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7",
"accept-language": "en-US,en;q=0.9",
"cache-control": "no-cache",
"pragma": "no-cache",
"sec-ch-ua": '"Chromium";v="116", "Not)A;Brand";v="24", "Google Chrome";v="116"',
"sec-ch-ua-mobile": "?0",
"sec-ch-ua-platform": '"macOS"',
"sec-fetch-dest": "document",
"sec-fetch-mode": "navigate",
"sec-fetch-site": "none",
"sec-fetch-user": "?1",
"upgrade-insecure-requests": "1"
}

return headers



def test_diff_afd_cve_2023_21768(shared_datadir: Path):
"""
Tests end to end diff of CVE
"""

test_name = 'cve-2023-21768'
output_path = shared_datadir / test_name
output_path.mkdir(exist_ok=True, parents=True)


# setup bins
old_bin_path = shared_datadir / 'afd.sys.x64.10.0.22621.1028'
old_url = 'https://msdl.microsoft.com/download/symbols/afd.sys/0C5C6994A8000/afd.sys'
new_bin_path = shared_datadir / 'afd.sys.x64.10.0.22621.1415'
new_url = 'https://msdl.microsoft.com/download/symbols/afd.sys/50989142A9000/afd.sys'

# download binaries
# TODO this download can fail, store in repo?
headers = get_chrome_headers()
old_bin_path.write_bytes(requests.get(old_url,headers=headers).content)
new_bin_path.write_bytes(requests.get(new_url,headers=headers).content)

assert old_bin_path.exists()
assert new_bin_path.exists()

parser = get_parser()

GhidraDiffEngine.add_ghidra_args_to_parser(parser)

args = parser.parse_args([str(old_bin_path.absolute()),str(new_bin_path.absolute())])

engine_log_path = output_path / parser.get_default('log_path')

binary_paths = args.old + [bin for sublist in args.new for bin in sublist]

binary_paths = [Path(path) for path in binary_paths]

if any([not path.exists() for path in binary_paths]):
missing_bins = [f'{path.name}' for path in binary_paths if not path.exists()]
raise FileNotFoundError(f"Missing Bins: {' '.join(missing_bins)}")

project_name = f'{args.project_name}-{binary_paths[0].name}-{binary_paths[-1].name}'


DiffEngine: GhidraDiffEngine = VersionTrackingDiff

d: GhidraDiffEngine = DiffEngine(args=args,
verbose=True,
threaded=args.threaded,
max_ram_percent=args.max_ram_percent,
print_jvm_flags=args.print_flags,
jvm_args=args.jvm_args,
force_analysis=args.force_analysis,
force_diff=args.force_diff,
verbose_analysis=args.va,
no_symbols=args.no_symbols,
engine_log_path=engine_log_path,
engine_log_level=args.log_level,
engine_file_log_level=args.file_log_level
)

d.setup_project(binary_paths, args.project_location, project_name, args.symbols_path)

d.analyze_project()

pdiff = d.diff_bins(old_bin_path, new_bin_path)
pdiff_json = json.dumps(pdiff)

d.validate_diff_json(pdiff_json)

diff_name = f"{old_bin_path.name}-{new_bin_path.name}_diff"

d.dump_pdiff_to_path(diff_name,
pdiff,
output_path,
side_by_side=args.side_by_side,
max_section_funcs=args.max_section_funcs,
md_title=args.md_title)

assert len(pdiff['functions']['modified']) == 10
assert len(pdiff['functions']['added']) == 28
assert len(pdiff['functions']['deleted']) == 0

func_name = "AfdNotifyRemoveIoCompletion"
assert any([func_name in func['old']['name'] or func_name in func['new']['name'] for func in pdiff['functions']['modified'] ]) is True
Empty file removed tests/test_main.py
Empty file.
67 changes: 59 additions & 8 deletions tests/test_startup.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,63 @@
from pathlib import Path
import ghidriff
import os
import pytest
import pyhidra

def test_pyhidra_start():
pyhidra.start(verbose=True)

def test_bogus_ghidra_install_dir(monkeypatch):
def test_ghidra_install_dir():
import os
install_dir = os.getenv("GHIDRA_INSTALL_DIR")
assert install_dir == "/ghidra"

monkeypatch.setenv("GHIDRA_INSTALL_DIR", "/somebogusplace")
print(os.getenv('GHIDRA_INSTALL_DIR'))
pyhidra.start()
#from ghidriff import GhidraDiffEngine
# from pytest import MonkeyPatch

# @pytest.fixture
# def setup_bogus_env(mon):
# from mock import patch

# @patch('pyhidra.GHIDRA_INSTALL_DIR', "/someboguspath"):
# def test_bogus_ghidra_install_dira():
# with pytest.raises(FileNotFoundError):
# import pyhidra as err_pyhidra
# err_pyhidra.start(verbose=True)


# def test_ghidra_install_dir():
# # import sys
# # sys.modules.pop('pyhidra')
# with MonkeyPatch.context() as mp:
# mp.delenv("GHIDRA_INSTALL_DIR")

# import pyhidra
# # print(os.getenv("GHIDRA_INSTALL_DIR"))
# with pytest.raises(SystemExit) as pytest_wrapped_e:
# launcher = pyhidra.start(verbose=True)
# assert pytest_wrapped_e.type == SystemExit
# #assert pytest_wrapped_e.value.code == 42
# import os
# print(os.getenv("GHIDRA_INSTALL_DIR"))
# # from importlib import reload
# # reload(pyhidra)
# import sys
# del sys.modules['pyhidra']



# def test_bogus_ghidra_install_dir():
# #monkeypatch.setenv("GHIDRA_INSTALL_DIR", '/someboguspath')
# import os
# print(os.getenv("GHIDRA_INSTALL_DIR"))
# with MonkeyPatch.context() as mp:
# with pytest.raises(FileNotFoundError):
# import pyhidra
# #mp.setattr(pyhidra.constants,'GHIDRA_INSTALL_DIR', '/someboguspath')
# pyhidra.start(verbose=True)





# launcher = pyhidra.start(verbose=True)
# print(launcher.check_ghidra_version())

#det test_file_not_exist():

0 comments on commit bd7fb68

Please sign in to comment.