refhound 0.1.0b1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- refhound-0.1.0b1/.github/workflows/ci.yml +63 -0
- refhound-0.1.0b1/.github/workflows/packages.yml +176 -0
- refhound-0.1.0b1/.github/workflows/release.yml +133 -0
- refhound-0.1.0b1/.gitignore +30 -0
- refhound-0.1.0b1/.readthedocs.yaml +16 -0
- refhound-0.1.0b1/CHANGELOG.md +112 -0
- refhound-0.1.0b1/CONTRIBUTING.md +59 -0
- refhound-0.1.0b1/IMPLEMENTATION_REPORT.md +131 -0
- refhound-0.1.0b1/LICENSE +21 -0
- refhound-0.1.0b1/PKG-INFO +164 -0
- refhound-0.1.0b1/README.md +121 -0
- refhound-0.1.0b1/SECURITY.md +61 -0
- refhound-0.1.0b1/benchmarks/README.md +10 -0
- refhound-0.1.0b1/benchmarks/run.py +84 -0
- refhound-0.1.0b1/debian/changelog +8 -0
- refhound-0.1.0b1/debian/control +37 -0
- refhound-0.1.0b1/debian/copyright +25 -0
- refhound-0.1.0b1/debian/rules +9 -0
- refhound-0.1.0b1/debian/source/format +1 -0
- refhound-0.1.0b1/debian/tests/control +3 -0
- refhound-0.1.0b1/debian/tests/smoke +7 -0
- refhound-0.1.0b1/docs/api.rst +108 -0
- refhound-0.1.0b1/docs/compatibility.rst +61 -0
- refhound-0.1.0b1/docs/concepts.rst +32 -0
- refhound-0.1.0b1/docs/conf.py +46 -0
- refhound-0.1.0b1/docs/configuration.rst +19 -0
- refhound-0.1.0b1/docs/design.rst +89 -0
- refhound-0.1.0b1/docs/detectors.rst +74 -0
- refhound-0.1.0b1/docs/development.rst +40 -0
- refhound-0.1.0b1/docs/index.rst +138 -0
- refhound-0.1.0b1/docs/installation.rst +84 -0
- refhound-0.1.0b1/docs/limitations.rst +40 -0
- refhound-0.1.0b1/docs/methodology.rst +13 -0
- refhound-0.1.0b1/docs/performance.rst +12 -0
- refhound-0.1.0b1/docs/releasing.rst +99 -0
- refhound-0.1.0b1/docs/reports.rst +24 -0
- refhound-0.1.0b1/docs/requirements.txt +3 -0
- refhound-0.1.0b1/docs/scanning.rst +29 -0
- refhound-0.1.0b1/docs/security.rst +65 -0
- refhound-0.1.0b1/docs/troubleshooting.rst +9 -0
- refhound-0.1.0b1/docs/usage.rst +363 -0
- refhound-0.1.0b1/packaging/README.md +76 -0
- refhound-0.1.0b1/packaging/arch/PKGBUILD +52 -0
- refhound-0.1.0b1/packaging/rpm/refhound.spec +62 -0
- refhound-0.1.0b1/pyproject.toml +95 -0
- refhound-0.1.0b1/src/refhound/__init__.py +5 -0
- refhound-0.1.0b1/src/refhound/__main__.py +6 -0
- refhound-0.1.0b1/src/refhound/_version.py +7 -0
- refhound-0.1.0b1/src/refhound/analysis/__init__.py +1 -0
- refhound-0.1.0b1/src/refhound/analysis/chronology.py +108 -0
- refhound-0.1.0b1/src/refhound/analysis/commit_anomalies.py +130 -0
- refhound-0.1.0b1/src/refhound/analysis/correlations.py +150 -0
- refhound-0.1.0b1/src/refhound/analysis/data.py +120 -0
- refhound-0.1.0b1/src/refhound/analysis/deletion_analysis.py +157 -0
- refhound-0.1.0b1/src/refhound/analysis/force_push_analysis.py +107 -0
- refhound-0.1.0b1/src/refhound/analysis/identity_analysis.py +101 -0
- refhound-0.1.0b1/src/refhound/analysis/scoring.py +96 -0
- refhound-0.1.0b1/src/refhound/api.py +90 -0
- refhound-0.1.0b1/src/refhound/baseline.py +78 -0
- refhound-0.1.0b1/src/refhound/cache.py +73 -0
- refhound-0.1.0b1/src/refhound/cli.py +1436 -0
- refhound-0.1.0b1/src/refhound/cli_cache.py +84 -0
- refhound-0.1.0b1/src/refhound/config.py +199 -0
- refhound-0.1.0b1/src/refhound/detectors/__init__.py +1 -0
- refhound-0.1.0b1/src/refhound/detectors/base.py +125 -0
- refhound-0.1.0b1/src/refhound/detectors/cloud.py +100 -0
- refhound-0.1.0b1/src/refhound/detectors/database.py +66 -0
- refhound-0.1.0b1/src/refhound/detectors/entropy.py +80 -0
- refhound-0.1.0b1/src/refhound/detectors/generic.py +42 -0
- refhound-0.1.0b1/src/refhound/detectors/github.py +33 -0
- refhound-0.1.0b1/src/refhound/detectors/gitlab.py +45 -0
- refhound-0.1.0b1/src/refhound/detectors/jwt.py +68 -0
- refhound-0.1.0b1/src/refhound/detectors/passwords.py +98 -0
- refhound-0.1.0b1/src/refhound/detectors/private_keys.py +41 -0
- refhound-0.1.0b1/src/refhound/detectors/registry.py +54 -0
- refhound-0.1.0b1/src/refhound/errors.py +63 -0
- refhound-0.1.0b1/src/refhound/git/__init__.py +1 -0
- refhound-0.1.0b1/src/refhound/git/command.py +289 -0
- refhound-0.1.0b1/src/refhound/git/commits.py +196 -0
- refhound-0.1.0b1/src/refhound/git/diff.py +87 -0
- refhound-0.1.0b1/src/refhound/git/fsck.py +70 -0
- refhound-0.1.0b1/src/refhound/git/graph.py +110 -0
- refhound-0.1.0b1/src/refhound/git/lfs.py +47 -0
- refhound-0.1.0b1/src/refhound/git/notes.py +37 -0
- refhound-0.1.0b1/src/refhound/git/objects.py +161 -0
- refhound-0.1.0b1/src/refhound/git/reflog.py +90 -0
- refhound-0.1.0b1/src/refhound/git/refs.py +84 -0
- refhound-0.1.0b1/src/refhound/git/repository.py +188 -0
- refhound-0.1.0b1/src/refhound/git/submodules.py +117 -0
- refhound-0.1.0b1/src/refhound/git/tags.py +52 -0
- refhound-0.1.0b1/src/refhound/models/__init__.py +1 -0
- refhound-0.1.0b1/src/refhound/models/anomaly.py +67 -0
- refhound-0.1.0b1/src/refhound/models/commit.py +62 -0
- refhound-0.1.0b1/src/refhound/models/diagnostic.py +20 -0
- refhound-0.1.0b1/src/refhound/models/finding.py +119 -0
- refhound-0.1.0b1/src/refhound/models/object.py +96 -0
- refhound-0.1.0b1/src/refhound/models/repository.py +87 -0
- refhound-0.1.0b1/src/refhound/models/secret.py +45 -0
- refhound-0.1.0b1/src/refhound/models/statistics.py +79 -0
- refhound-0.1.0b1/src/refhound/providers/__init__.py +1 -0
- refhound-0.1.0b1/src/refhound/providers/base.py +71 -0
- refhound-0.1.0b1/src/refhound/providers/generic.py +20 -0
- refhound-0.1.0b1/src/refhound/providers/github.py +56 -0
- refhound-0.1.0b1/src/refhound/providers/gitlab.py +55 -0
- refhound-0.1.0b1/src/refhound/reporting/__init__.py +1 -0
- refhound-0.1.0b1/src/refhound/reporting/console.py +106 -0
- refhound-0.1.0b1/src/refhound/reporting/json.py +166 -0
- refhound-0.1.0b1/src/refhound/reporting/markdown.py +209 -0
- refhound-0.1.0b1/src/refhound/reporting/sarif.py +114 -0
- refhound-0.1.0b1/src/refhound/reporting/statistics.py +46 -0
- refhound-0.1.0b1/src/refhound/repository_key.py +40 -0
- refhound-0.1.0b1/src/refhound/scanners/__init__.py +1 -0
- refhound-0.1.0b1/src/refhound/scanners/base.py +79 -0
- refhound-0.1.0b1/src/refhound/scanners/ci.py +57 -0
- refhound-0.1.0b1/src/refhound/scanners/engine.py +514 -0
- refhound-0.1.0b1/src/refhound/scanners/files.py +48 -0
- refhound-0.1.0b1/src/refhound/scanners/history.py +46 -0
- refhound-0.1.0b1/src/refhound/scanners/identity.py +46 -0
- refhound-0.1.0b1/src/refhound/scanners/refs.py +34 -0
- refhound-0.1.0b1/src/refhound/scanners/secrets.py +202 -0
- refhound-0.1.0b1/src/refhound/scanners/timeline.py +66 -0
- refhound-0.1.0b1/src/refhound/scanners/unreachable.py +120 -0
- refhound-0.1.0b1/src/refhound/snapshots.py +155 -0
- refhound-0.1.0b1/src/refhound/storage/__init__.py +1 -0
- refhound-0.1.0b1/src/refhound/storage/database.py +322 -0
- refhound-0.1.0b1/src/refhound/storage/migrations/__init__.py +1 -0
- refhound-0.1.0b1/src/refhound/storage/schema.py +120 -0
- refhound-0.1.0b1/src/refhound/util/__init__.py +1 -0
- refhound-0.1.0b1/src/refhound/util/dates.py +97 -0
- refhound-0.1.0b1/src/refhound/util/hashing.py +71 -0
- refhound-0.1.0b1/src/refhound/util/output.py +34 -0
- refhound-0.1.0b1/src/refhound/util/paths.py +207 -0
- refhound-0.1.0b1/src/refhound/util/sanitize.py +60 -0
- refhound-0.1.0b1/src/refhound/util/sorting.py +26 -0
- refhound-0.1.0b1/tests/conftest.py +109 -0
- refhound-0.1.0b1/tests/corpus/secrets/negative/ordinary.txt +4 -0
- refhound-0.1.0b1/tests/corpus/secrets/positive/synthetic.txt +11 -0
- refhound-0.1.0b1/tests/integration/test_api.py +70 -0
- refhound-0.1.0b1/tests/integration/test_incomplete_cache.py +85 -0
- refhound-0.1.0b1/tests/integration/test_object_formats.py +41 -0
- refhound-0.1.0b1/tests/integration/test_offline_guard.py +58 -0
- refhound-0.1.0b1/tests/integration/test_repository_shapes.py +97 -0
- refhound-0.1.0b1/tests/integration/test_scan.py +409 -0
- refhound-0.1.0b1/tests/schemas/.gitkeep +1 -0
- refhound-0.1.0b1/tests/schemas/sarif-2.1.0.json +2882 -0
- refhound-0.1.0b1/tests/security/test_security_contracts.py +83 -0
- refhound-0.1.0b1/tests/unit/test_cache.py +39 -0
- refhound-0.1.0b1/tests/unit/test_cli_contract.py +73 -0
- refhound-0.1.0b1/tests/unit/test_cli_filters.py +63 -0
- refhound-0.1.0b1/tests/unit/test_cli_validation.py +33 -0
- refhound-0.1.0b1/tests/unit/test_config.py +65 -0
- refhound-0.1.0b1/tests/unit/test_detectors.py +139 -0
- refhound-0.1.0b1/tests/unit/test_git.py +135 -0
- refhound-0.1.0b1/tests/unit/test_git_features.py +28 -0
- refhound-0.1.0b1/tests/unit/test_profiles.py +33 -0
- refhound-0.1.0b1/tests/unit/test_reporting_contracts.py +78 -0
- refhound-0.1.0b1/tests/unit/test_storage_security.py +108 -0
- refhound-0.1.0b1/tests/unit/test_util.py +94 -0
- refhound-0.1.0b1/uv.lock +1224 -0
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
pull_request:
|
|
6
|
+
|
|
7
|
+
jobs:
|
|
8
|
+
quality:
|
|
9
|
+
runs-on: ubuntu-latest
|
|
10
|
+
steps:
|
|
11
|
+
- uses: actions/checkout@v4
|
|
12
|
+
- uses: astral-sh/setup-uv@v6
|
|
13
|
+
with:
|
|
14
|
+
python-version: "3.12"
|
|
15
|
+
- run: uv sync --all-extras
|
|
16
|
+
- run: uv run ruff check .
|
|
17
|
+
- run: uv run ruff format --check .
|
|
18
|
+
- run: uv run mypy src/refhound
|
|
19
|
+
- run: uv run pytest --cov=refhound --cov-report=xml --cov-fail-under=70
|
|
20
|
+
- uses: actions/upload-artifact@v4
|
|
21
|
+
with:
|
|
22
|
+
name: coverage
|
|
23
|
+
path: coverage.xml
|
|
24
|
+
|
|
25
|
+
tests:
|
|
26
|
+
strategy:
|
|
27
|
+
fail-fast: false
|
|
28
|
+
matrix:
|
|
29
|
+
os: [ubuntu-latest, macos-latest, windows-latest]
|
|
30
|
+
python: ["3.12", "3.13"]
|
|
31
|
+
runs-on: ${{ matrix.os }}
|
|
32
|
+
steps:
|
|
33
|
+
- uses: actions/checkout@v4
|
|
34
|
+
- uses: astral-sh/setup-uv@v6
|
|
35
|
+
with:
|
|
36
|
+
python-version: ${{ matrix.python }}
|
|
37
|
+
- run: uv sync --all-extras
|
|
38
|
+
- run: uv run pytest
|
|
39
|
+
|
|
40
|
+
security-contract:
|
|
41
|
+
runs-on: ubuntu-latest
|
|
42
|
+
steps:
|
|
43
|
+
- uses: actions/checkout@v4
|
|
44
|
+
- uses: astral-sh/setup-uv@v6
|
|
45
|
+
with:
|
|
46
|
+
python-version: "3.12"
|
|
47
|
+
- run: uv sync --all-extras
|
|
48
|
+
- run: uv run pytest tests/security
|
|
49
|
+
|
|
50
|
+
docs-and-package:
|
|
51
|
+
runs-on: ubuntu-latest
|
|
52
|
+
steps:
|
|
53
|
+
- uses: actions/checkout@v4
|
|
54
|
+
- uses: astral-sh/setup-uv@v6
|
|
55
|
+
with:
|
|
56
|
+
python-version: "3.12"
|
|
57
|
+
- run: uv sync --all-extras
|
|
58
|
+
- run: uv run sphinx-build -W docs docs/_build
|
|
59
|
+
- run: uv build
|
|
60
|
+
- run: uv run --isolated --with dist/*.whl refhound --version
|
|
61
|
+
- run: uv run --isolated --with dist/*.whl refhound --help
|
|
62
|
+
- run: uv run --isolated --with dist/*.whl refhound scan --help
|
|
63
|
+
- run: uv run --isolated --with dist/*.whl refhound doctor --help
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
name: Packages
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
workflow_call:
|
|
5
|
+
workflow_dispatch:
|
|
6
|
+
pull_request:
|
|
7
|
+
paths:
|
|
8
|
+
- "debian/**"
|
|
9
|
+
- "packaging/**"
|
|
10
|
+
- "pyproject.toml"
|
|
11
|
+
- "src/**"
|
|
12
|
+
- ".github/workflows/packages.yml"
|
|
13
|
+
|
|
14
|
+
permissions:
|
|
15
|
+
contents: read
|
|
16
|
+
|
|
17
|
+
jobs:
|
|
18
|
+
arch:
|
|
19
|
+
name: Arch Linux package
|
|
20
|
+
runs-on: ubuntu-latest
|
|
21
|
+
container: archlinux:latest
|
|
22
|
+
steps:
|
|
23
|
+
- uses: actions/checkout@v4
|
|
24
|
+
|
|
25
|
+
- name: Build and install package
|
|
26
|
+
run: |
|
|
27
|
+
set -euo pipefail
|
|
28
|
+
pacman -Syu --noconfirm base-devel git python python-build python-hatchling \
|
|
29
|
+
python-installer python-wheel python-typer python-rich python-pydantic \
|
|
30
|
+
python-sqlalchemy python-platformdirs python-httpx python-yaml pacman-contrib
|
|
31
|
+
|
|
32
|
+
version=$(python3 -c "import ast,pathlib; m=ast.parse(pathlib.Path('src/refhound/_version.py').read_text()); print(next(ast.literal_eval(n.value) for n in m.body if isinstance(n, ast.Assign) and any(getattr(t, 'id', '') == '__version__' for t in n.targets)))")
|
|
33
|
+
|
|
34
|
+
# checkout falls back to a source tarball when the image has no git,
|
|
35
|
+
# so build the release tarball from the working tree instead of git.
|
|
36
|
+
# COPYFILE_DISABLE keeps macOS tar from adding AppleDouble ._ members.
|
|
37
|
+
export COPYFILE_DISABLE=1
|
|
38
|
+
stage=$(mktemp -d)
|
|
39
|
+
tree="$stage/RefHound-$version"
|
|
40
|
+
mkdir -p "$tree"
|
|
41
|
+
cp -a "$GITHUB_WORKSPACE/." "$tree/"
|
|
42
|
+
rm -rf "$tree/.git" "$tree/artifacts" "$tree/.venv" "$tree/dist" \
|
|
43
|
+
"$tree/build" "$tree/docs/_build" "$tree/.pytest_cache" "$tree/.ruff_cache" \
|
|
44
|
+
"$tree/.mypy_cache" "$tree/.pybuild" "$tree/debian/.debhelper" \
|
|
45
|
+
"$tree/debian/refhound"
|
|
46
|
+
find "$tree" -name __pycache__ -type d -prune -exec rm -rf {} +
|
|
47
|
+
find "$tree" -name '*.egg-info' -type d -prune -exec rm -rf {} +
|
|
48
|
+
# macOS resource forks, for local runs of this recipe.
|
|
49
|
+
find "$tree" -name '._*' -delete
|
|
50
|
+
mkdir -p artifacts/source
|
|
51
|
+
tar -czf "artifacts/source/refhound-$version.tar.gz" -C "$stage" "RefHound-$version"
|
|
52
|
+
rm -rf "$stage"
|
|
53
|
+
|
|
54
|
+
useradd -m builder
|
|
55
|
+
chown -R builder:builder "$GITHUB_WORKSPACE"
|
|
56
|
+
chmod -R a+rX artifacts
|
|
57
|
+
|
|
58
|
+
su builder -s /bin/bash -c "
|
|
59
|
+
set -euo pipefail
|
|
60
|
+
cd '$GITHUB_WORKSPACE/packaging/arch'
|
|
61
|
+
REFHOUND_LOCAL_TARBALL='$GITHUB_WORKSPACE/artifacts/source/refhound-$version.tar.gz' \
|
|
62
|
+
makepkg -f --noconfirm
|
|
63
|
+
"
|
|
64
|
+
|
|
65
|
+
mkdir -p artifacts/arch
|
|
66
|
+
cp packaging/arch/refhound-*.pkg.tar.zst artifacts/arch/
|
|
67
|
+
pacman -U --noconfirm artifacts/arch/refhound-*.pkg.tar.zst
|
|
68
|
+
refhound --version
|
|
69
|
+
refhound scan --help >/dev/null
|
|
70
|
+
|
|
71
|
+
- uses: actions/upload-artifact@v4
|
|
72
|
+
with:
|
|
73
|
+
name: package-arch
|
|
74
|
+
path: artifacts/arch/*.pkg.tar.zst
|
|
75
|
+
|
|
76
|
+
debian:
|
|
77
|
+
name: Debian package
|
|
78
|
+
runs-on: ubuntu-latest
|
|
79
|
+
container: debian:trixie-slim
|
|
80
|
+
steps:
|
|
81
|
+
- uses: actions/checkout@v4
|
|
82
|
+
|
|
83
|
+
- name: Build and install package
|
|
84
|
+
run: |
|
|
85
|
+
set -euo pipefail
|
|
86
|
+
apt-get update
|
|
87
|
+
apt-get install -y --no-install-recommends \
|
|
88
|
+
build-essential ca-certificates debhelper dh-python git lintian \
|
|
89
|
+
pybuild-plugin-pyproject python3-all python3-hatchling python3-typer \
|
|
90
|
+
python3-rich python3-pydantic python3-sqlalchemy python3-platformdirs \
|
|
91
|
+
python3-httpx python3-yaml
|
|
92
|
+
|
|
93
|
+
dpkg-buildpackage -us -uc -b
|
|
94
|
+
mkdir -p artifacts/debian
|
|
95
|
+
cp ../refhound_*.deb artifacts/debian/
|
|
96
|
+
|
|
97
|
+
apt-get install -y ./artifacts/debian/refhound_*.deb
|
|
98
|
+
refhound --version
|
|
99
|
+
refhound scan --help >/dev/null
|
|
100
|
+
lintian --fail-on error artifacts/debian/refhound_*.deb
|
|
101
|
+
|
|
102
|
+
- uses: actions/upload-artifact@v4
|
|
103
|
+
with:
|
|
104
|
+
name: package-debian
|
|
105
|
+
path: artifacts/debian/*.deb
|
|
106
|
+
|
|
107
|
+
rpm:
|
|
108
|
+
name: RPM package (${{ matrix.image }})
|
|
109
|
+
runs-on: ubuntu-latest
|
|
110
|
+
container: ${{ matrix.image }}
|
|
111
|
+
strategy:
|
|
112
|
+
fail-fast: false
|
|
113
|
+
matrix:
|
|
114
|
+
include:
|
|
115
|
+
- image: fedora:latest
|
|
116
|
+
artifact: rpm-fedora
|
|
117
|
+
- image: opensuse/tumbleweed:latest
|
|
118
|
+
artifact: rpm-opensuse
|
|
119
|
+
steps:
|
|
120
|
+
- uses: actions/checkout@v4
|
|
121
|
+
|
|
122
|
+
- name: Install build tools
|
|
123
|
+
run: |
|
|
124
|
+
set -euo pipefail
|
|
125
|
+
if command -v dnf >/dev/null; then
|
|
126
|
+
dnf install -y git tar gzip rpm-build python3-devel pyproject-rpm-macros \
|
|
127
|
+
python3-hatchling python3-typer python3-rich python3-pydantic \
|
|
128
|
+
python3-sqlalchemy python3-platformdirs python3-httpx python3-pyyaml
|
|
129
|
+
else
|
|
130
|
+
zypper --non-interactive install git tar gzip rpm-build python3-devel \
|
|
131
|
+
python-rpm-macros python3-pip python3-hatchling python3-typer \
|
|
132
|
+
python3-rich python3-pydantic python3-SQLAlchemy python3-platformdirs \
|
|
133
|
+
python3-httpx python3-PyYAML
|
|
134
|
+
fi
|
|
135
|
+
|
|
136
|
+
- name: Build and install package
|
|
137
|
+
run: |
|
|
138
|
+
set -euo pipefail
|
|
139
|
+
version=$(python3 -c "import ast,pathlib; m=ast.parse(pathlib.Path('src/refhound/_version.py').read_text()); print(next(ast.literal_eval(n.value) for n in m.body if isinstance(n, ast.Assign) and any(getattr(t, 'id', '') == '__version__' for t in n.targets)))")
|
|
140
|
+
|
|
141
|
+
# checkout falls back to a source tarball when the image has no git,
|
|
142
|
+
# so build the release tarball from the working tree instead of git.
|
|
143
|
+
# COPYFILE_DISABLE keeps macOS tar from adding AppleDouble ._ members.
|
|
144
|
+
export COPYFILE_DISABLE=1
|
|
145
|
+
stage=$(mktemp -d)
|
|
146
|
+
tree="$stage/RefHound-$version"
|
|
147
|
+
mkdir -p "$tree"
|
|
148
|
+
cp -a "$GITHUB_WORKSPACE/." "$tree/"
|
|
149
|
+
rm -rf "$tree/.git" "$tree/artifacts" "$tree/.venv" "$tree/dist" \
|
|
150
|
+
"$tree/build" "$tree/docs/_build" "$tree/.pytest_cache" "$tree/.ruff_cache" \
|
|
151
|
+
"$tree/.mypy_cache" "$tree/.pybuild" "$tree/debian/.debhelper" \
|
|
152
|
+
"$tree/debian/refhound"
|
|
153
|
+
find "$tree" -name __pycache__ -type d -prune -exec rm -rf {} +
|
|
154
|
+
find "$tree" -name '*.egg-info' -type d -prune -exec rm -rf {} +
|
|
155
|
+
# macOS resource forks, for local runs of this recipe.
|
|
156
|
+
find "$tree" -name '._*' -delete
|
|
157
|
+
mkdir -p "$HOME/rpmbuild"/{BUILD,BUILDROOT,RPMS,SOURCES,SPECS,SRPMS}
|
|
158
|
+
tar -czf "$HOME/rpmbuild/SOURCES/refhound-$version.tar.gz" -C "$stage" "RefHound-$version"
|
|
159
|
+
rm -rf "$stage"
|
|
160
|
+
rpmbuild --define "_topdir $HOME/rpmbuild" -ba --nodeps packaging/rpm/refhound.spec
|
|
161
|
+
|
|
162
|
+
mkdir -p artifacts/${{ matrix.artifact }}
|
|
163
|
+
cp "$HOME"/rpmbuild/RPMS/noarch/*.rpm artifacts/${{ matrix.artifact }}/
|
|
164
|
+
|
|
165
|
+
if command -v dnf >/dev/null; then
|
|
166
|
+
dnf install -y artifacts/${{ matrix.artifact }}/*.rpm
|
|
167
|
+
else
|
|
168
|
+
zypper --non-interactive install --allow-unsigned-rpm artifacts/${{ matrix.artifact }}/*.rpm
|
|
169
|
+
fi
|
|
170
|
+
refhound --version
|
|
171
|
+
refhound scan --help >/dev/null
|
|
172
|
+
|
|
173
|
+
- uses: actions/upload-artifact@v4
|
|
174
|
+
with:
|
|
175
|
+
name: package-${{ matrix.artifact }}
|
|
176
|
+
path: artifacts/${{ matrix.artifact }}/*.rpm
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
name: Release
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags:
|
|
6
|
+
- "v*"
|
|
7
|
+
workflow_dispatch:
|
|
8
|
+
inputs:
|
|
9
|
+
publish_pypi:
|
|
10
|
+
description: "Publish the built wheel and sdist to PyPI"
|
|
11
|
+
type: boolean
|
|
12
|
+
default: false
|
|
13
|
+
|
|
14
|
+
permissions:
|
|
15
|
+
contents: read
|
|
16
|
+
|
|
17
|
+
concurrency:
|
|
18
|
+
group: release-${{ github.ref }}
|
|
19
|
+
cancel-in-progress: false
|
|
20
|
+
|
|
21
|
+
jobs:
|
|
22
|
+
build:
|
|
23
|
+
name: Build wheel and sdist
|
|
24
|
+
runs-on: ubuntu-latest
|
|
25
|
+
steps:
|
|
26
|
+
- uses: actions/checkout@v4
|
|
27
|
+
- uses: astral-sh/setup-uv@v6
|
|
28
|
+
with:
|
|
29
|
+
python-version: "3.12"
|
|
30
|
+
- name: Verify tag matches the packaged version
|
|
31
|
+
if: github.ref_type == 'tag'
|
|
32
|
+
run: |
|
|
33
|
+
set -euo pipefail
|
|
34
|
+
version=$(python -c "import ast, pathlib; m = ast.parse(pathlib.Path('src/refhound/_version.py').read_text()); print(next(ast.literal_eval(n.value) for n in m.body if isinstance(n, ast.Assign) and any(getattr(t, 'id', '') == '__version__' for t in n.targets)))")
|
|
35
|
+
if [ "v$version" != "$GITHUB_REF_NAME" ]; then
|
|
36
|
+
echo "Tag $GITHUB_REF_NAME does not match src/refhound/_version.py ($version)" >&2
|
|
37
|
+
exit 1
|
|
38
|
+
fi
|
|
39
|
+
- run: uv build
|
|
40
|
+
- run: uv run --isolated --with twine twine check dist/*
|
|
41
|
+
- uses: actions/upload-artifact@v4
|
|
42
|
+
with:
|
|
43
|
+
name: python-dist
|
|
44
|
+
path: dist/*
|
|
45
|
+
|
|
46
|
+
verify-install:
|
|
47
|
+
name: Install wheel in isolation (Python ${{ matrix.python }})
|
|
48
|
+
needs: build
|
|
49
|
+
strategy:
|
|
50
|
+
fail-fast: false
|
|
51
|
+
matrix:
|
|
52
|
+
python: ["3.12", "3.13"]
|
|
53
|
+
runs-on: ubuntu-latest
|
|
54
|
+
steps:
|
|
55
|
+
- uses: actions/checkout@v4
|
|
56
|
+
- uses: astral-sh/setup-uv@v6
|
|
57
|
+
with:
|
|
58
|
+
python-version: ${{ matrix.python }}
|
|
59
|
+
- uses: actions/download-artifact@v4
|
|
60
|
+
with:
|
|
61
|
+
name: python-dist
|
|
62
|
+
path: dist
|
|
63
|
+
- run: uv run --isolated --python ${{ matrix.python }} --with dist/*.whl refhound --version
|
|
64
|
+
- run: uv run --isolated --python ${{ matrix.python }} --with dist/*.whl refhound --help
|
|
65
|
+
- run: uv run --isolated --python ${{ matrix.python }} --with dist/*.whl refhound scan --help
|
|
66
|
+
- run: uv run --isolated --python ${{ matrix.python }} --with dist/*.whl python -c "import refhound; print(refhound.__version__)"
|
|
67
|
+
|
|
68
|
+
packages:
|
|
69
|
+
name: Native packages
|
|
70
|
+
uses: ./.github/workflows/packages.yml
|
|
71
|
+
|
|
72
|
+
pypi:
|
|
73
|
+
name: Publish to PyPI
|
|
74
|
+
needs: [build, verify-install]
|
|
75
|
+
if: >-
|
|
76
|
+
(github.event_name == 'workflow_dispatch' && inputs.publish_pypi) ||
|
|
77
|
+
(github.event_name == 'push' && vars.PUBLISH_TO_PYPI == 'true')
|
|
78
|
+
runs-on: ubuntu-latest
|
|
79
|
+
environment:
|
|
80
|
+
name: pypi
|
|
81
|
+
url: https://pypi.org/p/refhound
|
|
82
|
+
permissions:
|
|
83
|
+
id-token: write
|
|
84
|
+
contents: read
|
|
85
|
+
steps:
|
|
86
|
+
- uses: actions/download-artifact@v4
|
|
87
|
+
with:
|
|
88
|
+
name: python-dist
|
|
89
|
+
path: dist
|
|
90
|
+
- name: Publish package distributions to PyPI
|
|
91
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
92
|
+
with:
|
|
93
|
+
packages-dir: dist
|
|
94
|
+
print-hash: true
|
|
95
|
+
|
|
96
|
+
github-release:
|
|
97
|
+
name: GitHub release
|
|
98
|
+
needs: [build, verify-install, packages]
|
|
99
|
+
if: github.ref_type == 'tag'
|
|
100
|
+
runs-on: ubuntu-latest
|
|
101
|
+
permissions:
|
|
102
|
+
contents: write
|
|
103
|
+
steps:
|
|
104
|
+
- uses: actions/download-artifact@v4
|
|
105
|
+
with:
|
|
106
|
+
path: release-assets
|
|
107
|
+
- name: Generate checksums
|
|
108
|
+
run: |
|
|
109
|
+
set -euo pipefail
|
|
110
|
+
cd release-assets
|
|
111
|
+
find . -type f ! -name SHA256SUMS -print0 | sort -z | xargs -0 sha256sum > SHA256SUMS
|
|
112
|
+
cat SHA256SUMS
|
|
113
|
+
- name: Create release with all artifacts
|
|
114
|
+
env:
|
|
115
|
+
GH_TOKEN: ${{ github.token }}
|
|
116
|
+
# This job does not check out the repository, so gh needs the repo name.
|
|
117
|
+
GH_REPO: ${{ github.repository }}
|
|
118
|
+
run: |
|
|
119
|
+
set -euo pipefail
|
|
120
|
+
mapfile -t assets < <(find release-assets -type f)
|
|
121
|
+
case "$GITHUB_REF_NAME" in
|
|
122
|
+
*a[0-9]*|*b[0-9]*|*rc[0-9]*) flags=(--prerelease) ;;
|
|
123
|
+
*) flags=() ;;
|
|
124
|
+
esac
|
|
125
|
+
if gh release view "$GITHUB_REF_NAME" >/dev/null 2>&1; then
|
|
126
|
+
gh release upload "$GITHUB_REF_NAME" "${assets[@]}" --clobber
|
|
127
|
+
else
|
|
128
|
+
gh release create "$GITHUB_REF_NAME" \
|
|
129
|
+
--title "RefHound $GITHUB_REF_NAME" \
|
|
130
|
+
--generate-notes \
|
|
131
|
+
"${flags[@]}" \
|
|
132
|
+
"${assets[@]}"
|
|
133
|
+
fi
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*.egg-info/
|
|
5
|
+
.venv/
|
|
6
|
+
venv/
|
|
7
|
+
|
|
8
|
+
# Tooling caches
|
|
9
|
+
.ruff_cache/
|
|
10
|
+
.mypy_cache/
|
|
11
|
+
.pytest_cache/
|
|
12
|
+
|
|
13
|
+
# Build output
|
|
14
|
+
docs/_build/
|
|
15
|
+
dist/
|
|
16
|
+
build/
|
|
17
|
+
.coverage
|
|
18
|
+
coverage.xml
|
|
19
|
+
|
|
20
|
+
# Debian build output
|
|
21
|
+
.pybuild/
|
|
22
|
+
debian/.debhelper/
|
|
23
|
+
debian/debhelper-build-stamp
|
|
24
|
+
debian/files
|
|
25
|
+
debian/refhound/
|
|
26
|
+
debian/*.substvars
|
|
27
|
+
debian/*.debhelper
|
|
28
|
+
|
|
29
|
+
# OS
|
|
30
|
+
.DS_Store
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# Read the Docs configuration for the Sphinx documentation.
|
|
2
|
+
# See https://docs.readthedocs.io/en/stable/config-file/v2.html
|
|
3
|
+
|
|
4
|
+
version: 2
|
|
5
|
+
|
|
6
|
+
build:
|
|
7
|
+
os: ubuntu-22.04
|
|
8
|
+
tools:
|
|
9
|
+
python: "3.12"
|
|
10
|
+
|
|
11
|
+
sphinx:
|
|
12
|
+
configuration: docs/conf.py
|
|
13
|
+
|
|
14
|
+
python:
|
|
15
|
+
install:
|
|
16
|
+
- requirements: docs/requirements.txt
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to RefHound are documented here. The format is based on
|
|
4
|
+
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project
|
|
5
|
+
adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
6
|
+
|
|
7
|
+
## [0.1.0b1] - 2026-11-04
|
|
8
|
+
|
|
9
|
+
The first beta. The Python API is still an alpha surface until 1.0, but the
|
|
10
|
+
CLI, package formats and release pipeline are usable.
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- Public Python API in `refhound.api` with `scan_repository()`, typed
|
|
15
|
+
`ScanOptions` and a documented alpha stability contract.
|
|
16
|
+
- Snapshot serialization moved out of the CLI into `refhound.snapshots`, so
|
|
17
|
+
library users can persist results without importing Typer or Rich.
|
|
18
|
+
- `scan --no-store` for one-off analysis without database reads or writes.
|
|
19
|
+
- Timeline filters for findings on a path or at a severity.
|
|
20
|
+
- `SHA256SUMS` for every GitHub release asset.
|
|
21
|
+
- Documented PyPI trusted publishing and manual release workflow.
|
|
22
|
+
|
|
23
|
+
### Changed
|
|
24
|
+
|
|
25
|
+
- Configuration is validated strictly; unknown keys and mistyped values are
|
|
26
|
+
errors instead of being ignored.
|
|
27
|
+
- Unsupported `--format` values fail with exit code 2 instead of silently
|
|
28
|
+
falling back to the default renderer.
|
|
29
|
+
- The package classifier is now `Development Status :: 4 - Beta`.
|
|
30
|
+
|
|
31
|
+
### Fixed
|
|
32
|
+
|
|
33
|
+
- Incomplete scans are no longer served from the snapshot cache; completeness,
|
|
34
|
+
diagnostics and failed detectors are persisted and restored.
|
|
35
|
+
- Remote URL credentials are sanitized before they reach logs, SQLite or
|
|
36
|
+
repository snapshots, including a migration for existing databases.
|
|
37
|
+
- `--offline` rejects `--unshallow` and `--refresh-remote` before any network
|
|
38
|
+
call.
|
|
39
|
+
- CI help tests are independent of ANSI styling forced by GitHub Actions.
|
|
40
|
+
- Native package builds work in container jobs: git safe-directory handling,
|
|
41
|
+
`file://` local sources for `makepkg`, and openSUSE's `python-rpm-macros`
|
|
42
|
+
package set.
|
|
43
|
+
|
|
44
|
+
## [0.1.0a1] - 2026-11-04
|
|
45
|
+
|
|
46
|
+
### Packaging
|
|
47
|
+
|
|
48
|
+
- Single version source in `src/refhound/_version.py`, read by Hatchling and
|
|
49
|
+
Sphinx.
|
|
50
|
+
- PyPI-ready wheel and sdist metadata with project URLs and Python 3.13.
|
|
51
|
+
- Arch `PKGBUILD`, Debian packaging and an RPM spec for Fedora, RHEL 10+ and
|
|
52
|
+
openSUSE, built and smoke-tested in distribution containers.
|
|
53
|
+
- Tag-driven release workflow with isolated wheel verification, GitHub release
|
|
54
|
+
assets and optional PyPI trusted publishing.
|
|
55
|
+
|
|
56
|
+
### Security
|
|
57
|
+
|
|
58
|
+
- Centralized secret fragment redaction; values of eight characters or less
|
|
59
|
+
now expose no prefix or suffix in models, reports, baselines, or storage.
|
|
60
|
+
- Sanitized remote URL userinfo and credential-like query parameters before
|
|
61
|
+
command descriptions, repository models, logging, and reporting.
|
|
62
|
+
- Enforced whole-process streaming timeouts, stderr draining, child cleanup,
|
|
63
|
+
and OID validation at the batch ``cat-file`` boundary.
|
|
64
|
+
|
|
65
|
+
### Fixed
|
|
66
|
+
|
|
67
|
+
- ``findings --json`` now applies the same severity/category/score filters as
|
|
68
|
+
table output and uses its own versioned schema.
|
|
69
|
+
- Findings use one critical-first deterministic severity order.
|
|
70
|
+
- Detector exceptions now produce structured incomplete-scan diagnostics.
|
|
71
|
+
- Removed a foreign project URL from SARIF metadata.
|
|
72
|
+
|
|
73
|
+
### Changed
|
|
74
|
+
|
|
75
|
+
- Remote acquisition now distinguishes analysis freshness, mirror refresh,
|
|
76
|
+
and offline use; reports carry acquisition and object-format metadata.
|
|
77
|
+
- Profiles are executable pipeline specifications and vendored content uses a
|
|
78
|
+
shared file/secret policy.
|
|
79
|
+
- JSON/baseline/storage schemas are explicitly versioned; incomplete scans use
|
|
80
|
+
exit code 5.
|
|
81
|
+
|
|
82
|
+
### Deprecated
|
|
83
|
+
|
|
84
|
+
- No interfaces are deprecated in this unreleased version.
|
|
85
|
+
|
|
86
|
+
### Removed
|
|
87
|
+
|
|
88
|
+
- Removed the non-functional ``--jobs`` and ``--fetch-lfs`` options. RefHound
|
|
89
|
+
v0.1 does not claim parallel scanning or external LFS payload fetching.
|
|
90
|
+
|
|
91
|
+
### Added
|
|
92
|
+
|
|
93
|
+
- Core scan pipeline: refs, commit graph, blob inventory, secret detection,
|
|
94
|
+
unreachable/lost-chain archaeology, deleted-file and churn analysis,
|
|
95
|
+
timeline/identity/temporal anomaly detection, force-push change
|
|
96
|
+
detection, risk scoring, and interesting-commit ranking.
|
|
97
|
+
- Secret detectors: GitHub/GitLab tokens, AWS/GCP cloud credentials,
|
|
98
|
+
private keys, JWT, database connection URIs, generic passwords, generic
|
|
99
|
+
provider tokens and entropy-only candidates.
|
|
100
|
+
- Reporting: console tables, JSON, Markdown and SARIF 2.1.0, plus
|
|
101
|
+
repository statistics and baselines.
|
|
102
|
+
- Storage: SQLite persistence (SQLAlchemy) of scan ref snapshots for
|
|
103
|
+
ref-change / force-push detection between scans.
|
|
104
|
+
- Internal provider adapter scaffold (not exposed as a supported v0.1 feature).
|
|
105
|
+
- CLI commands: `scan`, `findings`, `secrets`, `refs`, `commits`, `objects`,
|
|
106
|
+
`dangling`, `unreachable`, `lost`, `explain`, `explain-lost`, `timeline`,
|
|
107
|
+
`authors`, `stats`, `history`, `interesting`, `report`, `diff-scan`,
|
|
108
|
+
`baseline`, `doctor`, `cache info/list/refresh/remove/prune`, `analyze churn`.
|
|
109
|
+
- Profiles (`quick`, `standard`, `deep`, `forensic`), `--fail-on`, baselines,
|
|
110
|
+
JSON/SARIF/Markdown output, exit-code contract (0/1/2/3/4/5).
|
|
111
|
+
- Test suite: 78 unit, security, schema and integration tests including
|
|
112
|
+
secret-redaction, SHA-1/SHA-256, repository-shape and lost-chain guarantees.
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# Contributing
|
|
2
|
+
|
|
3
|
+
Thanks for helping with RefHound.
|
|
4
|
+
|
|
5
|
+
## Getting started
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
git clone <url> RefHound
|
|
9
|
+
cd RefHound
|
|
10
|
+
uv sync --all-extras
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Quality gates
|
|
14
|
+
|
|
15
|
+
Every change must pass:
|
|
16
|
+
|
|
17
|
+
```
|
|
18
|
+
uv run ruff check .
|
|
19
|
+
uv run ruff format --check .
|
|
20
|
+
uv run mypy src/refhound
|
|
21
|
+
uv run pytest
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Run them locally before pushing:
|
|
25
|
+
|
|
26
|
+
```
|
|
27
|
+
uv run ruff check . && uv run ruff format . && uv run mypy src/refhound && uv run pytest
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Tests
|
|
31
|
+
|
|
32
|
+
* Unit tests live in `tests/unit/` and never touch git on disk.
|
|
33
|
+
* Integration tests in `tests/integration/` build small repositories with
|
|
34
|
+
the fixtures in `tests/conftest.py` (simple repos, deleted-branch repos,
|
|
35
|
+
bare remotes) and exercise the full `Engine` pipeline.
|
|
36
|
+
* The security contract (full secrets never appear in any output or
|
|
37
|
+
storage) is enforced by tests — keep it that way when adding features.
|
|
38
|
+
|
|
39
|
+
## Conventions
|
|
40
|
+
|
|
41
|
+
* Python 3.12+, `from __future__ import annotations` everywhere.
|
|
42
|
+
* Type everything; mypy is run in `strict` mode.
|
|
43
|
+
* Facts and interpretation stay separate. Any inference that could be wrong
|
|
44
|
+
must carry an explicit confidence value and be labeled a heuristic in
|
|
45
|
+
user-facing text.
|
|
46
|
+
* Redaction: never print, log, persist or serialize a full secret.
|
|
47
|
+
* New detectors go under `src/refhound/detectors/` and are registered in
|
|
48
|
+
`src/refhound/detectors/registry.py` with a test proving redaction.
|
|
49
|
+
* Git is always invoked through `GitRunner`; never use `shell=True` and
|
|
50
|
+
always validate object IDs before interpolation.
|
|
51
|
+
|
|
52
|
+
## Documentation
|
|
53
|
+
|
|
54
|
+
User-facing docs live in `docs/` and are written in reStructuredText. Update
|
|
55
|
+
`docs/index.rst` and the relevant topic page when changing behaviour.
|
|
56
|
+
|
|
57
|
+
## Changelog
|
|
58
|
+
|
|
59
|
+
Add an entry to `CHANGELOG.md` for user-visible changes.
|