mirror of
https://github.com/trustedsec/hate_crack.git
synced 2026-06-30 10:07:04 -07:00
Merge pull request #61 from trustedsec/ci-github-actions
Add CI workflow and E2E install tests
This commit is contained in:
@@ -0,0 +1,37 @@
|
||||
name: tests
|
||||
|
||||
on:
|
||||
push:
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
pytest:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: "3.13"
|
||||
|
||||
- name: Install system dependencies
|
||||
run: sudo apt-get update && sudo apt-get install -y p7zip-full transmission-cli
|
||||
|
||||
- name: Install uv
|
||||
run: python -m pip install --upgrade pip uv==0.9.28
|
||||
|
||||
- name: Install project dependencies
|
||||
run: |
|
||||
uv venv .venv
|
||||
uv pip install --python .venv/bin/python pytest
|
||||
uv pip install --python .venv/bin/python .
|
||||
|
||||
- name: Run tests
|
||||
env:
|
||||
HATE_CRACK_RUN_E2E: "0"
|
||||
HATE_CRACK_RUN_DOCKER_TESTS: "0"
|
||||
HATE_CRACK_RUN_LIVE_TESTS: "0"
|
||||
HATE_CRACK_SKIP_INIT: "1"
|
||||
run: .venv/bin/python -m pytest -v
|
||||
+15
-2
@@ -2,11 +2,24 @@ FROM python:3.13-slim
|
||||
|
||||
WORKDIR /workspace
|
||||
|
||||
RUN python -m pip install -q uv
|
||||
RUN apt-get update \
|
||||
&& apt-get install -y --no-install-recommends \
|
||||
build-essential \
|
||||
ca-certificates \
|
||||
curl \
|
||||
gzip \
|
||||
hashcat \
|
||||
ocl-icd-libopencl1 \
|
||||
pocl-opencl-icd \
|
||||
p7zip-full \
|
||||
transmission-cli \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
RUN python -m pip install -q uv==0.9.28
|
||||
|
||||
COPY . /workspace
|
||||
|
||||
RUN uv tool install .
|
||||
RUN make install
|
||||
|
||||
ENV PATH="/root/.local/bin:${PATH}"
|
||||
ENV HATE_CRACK_SKIP_INIT=1
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
.PHONY: all install clean hashcat-utils test
|
||||
|
||||
all: hashcat-utils
|
||||
|
||||
hashcat-utils:
|
||||
$(MAKE) -C hashcat-utils
|
||||
|
||||
install: hashcat-utils
|
||||
uv tool install .
|
||||
|
||||
clean:
|
||||
-$(MAKE) -C hashcat-utils clean
|
||||
rm -rf .pytest_cache .ruff_cache build dist *.egg-info
|
||||
find . -name "__pycache__" -type d -prune -exec rm -rf {} +
|
||||
|
||||
test:
|
||||
uv run pytest -v
|
||||
@@ -16,6 +16,26 @@ cd hashcat/
|
||||
make
|
||||
make install
|
||||
```
|
||||
|
||||
### External Dependencies
|
||||
These are required for certain download/extraction flows:
|
||||
|
||||
- `7z`/`7za` (p7zip) — used to extract `.7z` archives.
|
||||
- `transmission-cli` — used to download Weakpass torrents.
|
||||
|
||||
Install commands:
|
||||
|
||||
Ubuntu/Kali:
|
||||
```
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y p7zip-full transmission-cli
|
||||
```
|
||||
|
||||
macOS (Homebrew):
|
||||
```
|
||||
brew install p7zip transmission-cli
|
||||
```
|
||||
|
||||
### Download hate_crack
|
||||
```git clone --recurse-submodules https://github.com/trustedsec/hate_crack.git```
|
||||
* Customize binary and wordlist paths in "config.json"
|
||||
@@ -84,6 +104,31 @@ You can also use Python directly:
|
||||
python hate_crack.py
|
||||
```
|
||||
|
||||
### Makefile helpers
|
||||
Build hashcat-utils and install the tool:
|
||||
|
||||
```
|
||||
make install
|
||||
```
|
||||
|
||||
Build only hashcat-utils:
|
||||
|
||||
```
|
||||
make
|
||||
```
|
||||
|
||||
Clean build/test artifacts:
|
||||
|
||||
```
|
||||
make clean
|
||||
```
|
||||
|
||||
Run the test suite:
|
||||
|
||||
```
|
||||
make test
|
||||
```
|
||||
|
||||
Common options:
|
||||
- `--download-hashview`: Download hashes from Hashview before cracking.
|
||||
- `--weakpass`: Download wordlists from Weakpass.
|
||||
@@ -135,6 +180,8 @@ uv run pytest -v
|
||||
uv run pytest tests/test_hashview.py -v
|
||||
```
|
||||
|
||||
You can also run the full suite with `make test`.
|
||||
|
||||
### Live Hashview Tests
|
||||
|
||||
The live Hashview upload test is skipped by default. To run it, set the
|
||||
@@ -158,6 +205,9 @@ Docker-based end-to-end install/run (cached via `Dockerfile.test`):
|
||||
HATE_CRACK_RUN_DOCKER_TESTS=1 uv run pytest tests/test_docker_script_install.py -v
|
||||
```
|
||||
|
||||
The Docker E2E test also downloads a small subset of rockyou and runs a basic
|
||||
hashcat crack to validate external tool integration.
|
||||
|
||||
### Test Structure
|
||||
|
||||
- **tests/test_hashview.py**: Comprehensive test suite for HashviewAPI class with mocked API responses, including:
|
||||
@@ -170,7 +220,7 @@ All tests use mocked API calls, so they can run without connectivity to a Hashvi
|
||||
|
||||
### Continuous Integration
|
||||
|
||||
Tests automatically run on GitHub Actions for every push and pull request. The workflow tests against multiple Python versions (3.9, 3.10, 3.11, 3.12) to ensure compatibility.
|
||||
Tests automatically run on GitHub Actions for every push and pull request (Ubuntu, Python 3.13).
|
||||
|
||||
-------------------------------------------------------------------
|
||||
|
||||
|
||||
@@ -2063,6 +2063,7 @@ def main():
|
||||
|
||||
if args.hashview:
|
||||
if not hashview_api_key:
|
||||
print("Available Customers:")
|
||||
print("\nError: Hashview API key not configured.")
|
||||
print("Please set 'hashview_api_key' in config.json")
|
||||
sys.exit(1)
|
||||
|
||||
@@ -1,25 +1,31 @@
|
||||
import os
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
import uuid
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
|
||||
@pytest.mark.skipif(
|
||||
os.environ.get("HATE_CRACK_RUN_DOCKER_TESTS") != "1",
|
||||
reason="Set HATE_CRACK_RUN_DOCKER_TESTS=1 to run Docker-based tests.",
|
||||
)
|
||||
def test_docker_script_install_and_run():
|
||||
def _require_docker():
|
||||
if os.environ.get("HATE_CRACK_RUN_DOCKER_TESTS") != "1":
|
||||
pytest.skip("Set HATE_CRACK_RUN_DOCKER_TESTS=1 to run Docker-based tests.")
|
||||
if shutil.which("docker") is None:
|
||||
pytest.skip("docker not available")
|
||||
|
||||
repo_root = Path(__file__).resolve().parents[1]
|
||||
image_tag = "hate-crack-e2e"
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
def docker_image():
|
||||
_require_docker()
|
||||
repo_root = Path(__file__).resolve().parents[1]
|
||||
# Use a unique tag per test run to avoid conflicts
|
||||
image_tag = f"hate-crack-e2e-{uuid.uuid4().hex[:8]}"
|
||||
|
||||
dockerfile_path = str(repo_root / "Dockerfile.test")
|
||||
try:
|
||||
build = subprocess.run(
|
||||
["docker", "build", "-f", "Dockerfile.test", "-t", image_tag, str(repo_root)],
|
||||
["docker", "build", "-f", dockerfile_path, "-t", image_tag, str(repo_root)],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
timeout=600,
|
||||
@@ -31,18 +37,63 @@ def test_docker_script_install_and_run():
|
||||
"Docker build failed. "
|
||||
f"stdout={build.stdout} stderr={build.stderr}"
|
||||
)
|
||||
|
||||
|
||||
yield image_tag
|
||||
|
||||
# Cleanup: remove the Docker image after tests complete
|
||||
try:
|
||||
run = subprocess.run(
|
||||
["docker", "run", "--rm", image_tag],
|
||||
result = subprocess.run(
|
||||
["docker", "image", "rm", image_tag],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
timeout=120,
|
||||
timeout=60,
|
||||
)
|
||||
if result.returncode != 0:
|
||||
print(
|
||||
f"Warning: Failed to remove Docker image {image_tag}. "
|
||||
f"stderr={result.stderr}",
|
||||
file=sys.stderr
|
||||
)
|
||||
except Exception as e:
|
||||
# Don't fail the test if cleanup fails, but log the issue
|
||||
print(f"Warning: Exception while removing Docker image {image_tag}: {e}", file=sys.stderr)
|
||||
|
||||
|
||||
def _run_container(image_tag, command, timeout=180):
|
||||
try:
|
||||
run = subprocess.run(
|
||||
["docker", "run", "--rm", image_tag, "bash", "-lc", command],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
timeout=timeout,
|
||||
)
|
||||
except subprocess.TimeoutExpired as exc:
|
||||
pytest.fail(f"Docker run timed out after {exc.timeout}s")
|
||||
return run
|
||||
|
||||
|
||||
def test_docker_script_install_and_run(docker_image):
|
||||
run = _run_container(
|
||||
docker_image,
|
||||
"/root/.local/bin/hate_crack --help >/tmp/hc_help.txt && ./hate_crack.py --help >/tmp/hc_script_help.txt",
|
||||
timeout=120,
|
||||
)
|
||||
assert run.returncode == 0, (
|
||||
"Docker script install/run failed. "
|
||||
f"stdout={run.stdout} stderr={run.stderr}"
|
||||
)
|
||||
|
||||
|
||||
def test_docker_hashcat_cracks_simple_password(docker_image):
|
||||
command = (
|
||||
"set -euo pipefail; "
|
||||
"printf 'password\\nletmein\\n123456\\n' > /tmp/wordlist.txt; "
|
||||
"echo 5f4dcc3b5aa765d61d8327deb882cf99 > /tmp/hash.txt; "
|
||||
"hashcat -m 0 -a 0 --potfile-disable -o /tmp/out.txt /tmp/hash.txt /tmp/wordlist.txt --quiet; "
|
||||
"grep -q ':password' /tmp/out.txt"
|
||||
)
|
||||
run = _run_container(docker_image, command, timeout=180)
|
||||
assert run.returncode == 0, (
|
||||
"Docker hashcat crack failed. "
|
||||
f"stdout={run.stdout} stderr={run.stderr}"
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user