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.
Files changed (159) hide show
  1. refhound-0.1.0b1/.github/workflows/ci.yml +63 -0
  2. refhound-0.1.0b1/.github/workflows/packages.yml +176 -0
  3. refhound-0.1.0b1/.github/workflows/release.yml +133 -0
  4. refhound-0.1.0b1/.gitignore +30 -0
  5. refhound-0.1.0b1/.readthedocs.yaml +16 -0
  6. refhound-0.1.0b1/CHANGELOG.md +112 -0
  7. refhound-0.1.0b1/CONTRIBUTING.md +59 -0
  8. refhound-0.1.0b1/IMPLEMENTATION_REPORT.md +131 -0
  9. refhound-0.1.0b1/LICENSE +21 -0
  10. refhound-0.1.0b1/PKG-INFO +164 -0
  11. refhound-0.1.0b1/README.md +121 -0
  12. refhound-0.1.0b1/SECURITY.md +61 -0
  13. refhound-0.1.0b1/benchmarks/README.md +10 -0
  14. refhound-0.1.0b1/benchmarks/run.py +84 -0
  15. refhound-0.1.0b1/debian/changelog +8 -0
  16. refhound-0.1.0b1/debian/control +37 -0
  17. refhound-0.1.0b1/debian/copyright +25 -0
  18. refhound-0.1.0b1/debian/rules +9 -0
  19. refhound-0.1.0b1/debian/source/format +1 -0
  20. refhound-0.1.0b1/debian/tests/control +3 -0
  21. refhound-0.1.0b1/debian/tests/smoke +7 -0
  22. refhound-0.1.0b1/docs/api.rst +108 -0
  23. refhound-0.1.0b1/docs/compatibility.rst +61 -0
  24. refhound-0.1.0b1/docs/concepts.rst +32 -0
  25. refhound-0.1.0b1/docs/conf.py +46 -0
  26. refhound-0.1.0b1/docs/configuration.rst +19 -0
  27. refhound-0.1.0b1/docs/design.rst +89 -0
  28. refhound-0.1.0b1/docs/detectors.rst +74 -0
  29. refhound-0.1.0b1/docs/development.rst +40 -0
  30. refhound-0.1.0b1/docs/index.rst +138 -0
  31. refhound-0.1.0b1/docs/installation.rst +84 -0
  32. refhound-0.1.0b1/docs/limitations.rst +40 -0
  33. refhound-0.1.0b1/docs/methodology.rst +13 -0
  34. refhound-0.1.0b1/docs/performance.rst +12 -0
  35. refhound-0.1.0b1/docs/releasing.rst +99 -0
  36. refhound-0.1.0b1/docs/reports.rst +24 -0
  37. refhound-0.1.0b1/docs/requirements.txt +3 -0
  38. refhound-0.1.0b1/docs/scanning.rst +29 -0
  39. refhound-0.1.0b1/docs/security.rst +65 -0
  40. refhound-0.1.0b1/docs/troubleshooting.rst +9 -0
  41. refhound-0.1.0b1/docs/usage.rst +363 -0
  42. refhound-0.1.0b1/packaging/README.md +76 -0
  43. refhound-0.1.0b1/packaging/arch/PKGBUILD +52 -0
  44. refhound-0.1.0b1/packaging/rpm/refhound.spec +62 -0
  45. refhound-0.1.0b1/pyproject.toml +95 -0
  46. refhound-0.1.0b1/src/refhound/__init__.py +5 -0
  47. refhound-0.1.0b1/src/refhound/__main__.py +6 -0
  48. refhound-0.1.0b1/src/refhound/_version.py +7 -0
  49. refhound-0.1.0b1/src/refhound/analysis/__init__.py +1 -0
  50. refhound-0.1.0b1/src/refhound/analysis/chronology.py +108 -0
  51. refhound-0.1.0b1/src/refhound/analysis/commit_anomalies.py +130 -0
  52. refhound-0.1.0b1/src/refhound/analysis/correlations.py +150 -0
  53. refhound-0.1.0b1/src/refhound/analysis/data.py +120 -0
  54. refhound-0.1.0b1/src/refhound/analysis/deletion_analysis.py +157 -0
  55. refhound-0.1.0b1/src/refhound/analysis/force_push_analysis.py +107 -0
  56. refhound-0.1.0b1/src/refhound/analysis/identity_analysis.py +101 -0
  57. refhound-0.1.0b1/src/refhound/analysis/scoring.py +96 -0
  58. refhound-0.1.0b1/src/refhound/api.py +90 -0
  59. refhound-0.1.0b1/src/refhound/baseline.py +78 -0
  60. refhound-0.1.0b1/src/refhound/cache.py +73 -0
  61. refhound-0.1.0b1/src/refhound/cli.py +1436 -0
  62. refhound-0.1.0b1/src/refhound/cli_cache.py +84 -0
  63. refhound-0.1.0b1/src/refhound/config.py +199 -0
  64. refhound-0.1.0b1/src/refhound/detectors/__init__.py +1 -0
  65. refhound-0.1.0b1/src/refhound/detectors/base.py +125 -0
  66. refhound-0.1.0b1/src/refhound/detectors/cloud.py +100 -0
  67. refhound-0.1.0b1/src/refhound/detectors/database.py +66 -0
  68. refhound-0.1.0b1/src/refhound/detectors/entropy.py +80 -0
  69. refhound-0.1.0b1/src/refhound/detectors/generic.py +42 -0
  70. refhound-0.1.0b1/src/refhound/detectors/github.py +33 -0
  71. refhound-0.1.0b1/src/refhound/detectors/gitlab.py +45 -0
  72. refhound-0.1.0b1/src/refhound/detectors/jwt.py +68 -0
  73. refhound-0.1.0b1/src/refhound/detectors/passwords.py +98 -0
  74. refhound-0.1.0b1/src/refhound/detectors/private_keys.py +41 -0
  75. refhound-0.1.0b1/src/refhound/detectors/registry.py +54 -0
  76. refhound-0.1.0b1/src/refhound/errors.py +63 -0
  77. refhound-0.1.0b1/src/refhound/git/__init__.py +1 -0
  78. refhound-0.1.0b1/src/refhound/git/command.py +289 -0
  79. refhound-0.1.0b1/src/refhound/git/commits.py +196 -0
  80. refhound-0.1.0b1/src/refhound/git/diff.py +87 -0
  81. refhound-0.1.0b1/src/refhound/git/fsck.py +70 -0
  82. refhound-0.1.0b1/src/refhound/git/graph.py +110 -0
  83. refhound-0.1.0b1/src/refhound/git/lfs.py +47 -0
  84. refhound-0.1.0b1/src/refhound/git/notes.py +37 -0
  85. refhound-0.1.0b1/src/refhound/git/objects.py +161 -0
  86. refhound-0.1.0b1/src/refhound/git/reflog.py +90 -0
  87. refhound-0.1.0b1/src/refhound/git/refs.py +84 -0
  88. refhound-0.1.0b1/src/refhound/git/repository.py +188 -0
  89. refhound-0.1.0b1/src/refhound/git/submodules.py +117 -0
  90. refhound-0.1.0b1/src/refhound/git/tags.py +52 -0
  91. refhound-0.1.0b1/src/refhound/models/__init__.py +1 -0
  92. refhound-0.1.0b1/src/refhound/models/anomaly.py +67 -0
  93. refhound-0.1.0b1/src/refhound/models/commit.py +62 -0
  94. refhound-0.1.0b1/src/refhound/models/diagnostic.py +20 -0
  95. refhound-0.1.0b1/src/refhound/models/finding.py +119 -0
  96. refhound-0.1.0b1/src/refhound/models/object.py +96 -0
  97. refhound-0.1.0b1/src/refhound/models/repository.py +87 -0
  98. refhound-0.1.0b1/src/refhound/models/secret.py +45 -0
  99. refhound-0.1.0b1/src/refhound/models/statistics.py +79 -0
  100. refhound-0.1.0b1/src/refhound/providers/__init__.py +1 -0
  101. refhound-0.1.0b1/src/refhound/providers/base.py +71 -0
  102. refhound-0.1.0b1/src/refhound/providers/generic.py +20 -0
  103. refhound-0.1.0b1/src/refhound/providers/github.py +56 -0
  104. refhound-0.1.0b1/src/refhound/providers/gitlab.py +55 -0
  105. refhound-0.1.0b1/src/refhound/reporting/__init__.py +1 -0
  106. refhound-0.1.0b1/src/refhound/reporting/console.py +106 -0
  107. refhound-0.1.0b1/src/refhound/reporting/json.py +166 -0
  108. refhound-0.1.0b1/src/refhound/reporting/markdown.py +209 -0
  109. refhound-0.1.0b1/src/refhound/reporting/sarif.py +114 -0
  110. refhound-0.1.0b1/src/refhound/reporting/statistics.py +46 -0
  111. refhound-0.1.0b1/src/refhound/repository_key.py +40 -0
  112. refhound-0.1.0b1/src/refhound/scanners/__init__.py +1 -0
  113. refhound-0.1.0b1/src/refhound/scanners/base.py +79 -0
  114. refhound-0.1.0b1/src/refhound/scanners/ci.py +57 -0
  115. refhound-0.1.0b1/src/refhound/scanners/engine.py +514 -0
  116. refhound-0.1.0b1/src/refhound/scanners/files.py +48 -0
  117. refhound-0.1.0b1/src/refhound/scanners/history.py +46 -0
  118. refhound-0.1.0b1/src/refhound/scanners/identity.py +46 -0
  119. refhound-0.1.0b1/src/refhound/scanners/refs.py +34 -0
  120. refhound-0.1.0b1/src/refhound/scanners/secrets.py +202 -0
  121. refhound-0.1.0b1/src/refhound/scanners/timeline.py +66 -0
  122. refhound-0.1.0b1/src/refhound/scanners/unreachable.py +120 -0
  123. refhound-0.1.0b1/src/refhound/snapshots.py +155 -0
  124. refhound-0.1.0b1/src/refhound/storage/__init__.py +1 -0
  125. refhound-0.1.0b1/src/refhound/storage/database.py +322 -0
  126. refhound-0.1.0b1/src/refhound/storage/migrations/__init__.py +1 -0
  127. refhound-0.1.0b1/src/refhound/storage/schema.py +120 -0
  128. refhound-0.1.0b1/src/refhound/util/__init__.py +1 -0
  129. refhound-0.1.0b1/src/refhound/util/dates.py +97 -0
  130. refhound-0.1.0b1/src/refhound/util/hashing.py +71 -0
  131. refhound-0.1.0b1/src/refhound/util/output.py +34 -0
  132. refhound-0.1.0b1/src/refhound/util/paths.py +207 -0
  133. refhound-0.1.0b1/src/refhound/util/sanitize.py +60 -0
  134. refhound-0.1.0b1/src/refhound/util/sorting.py +26 -0
  135. refhound-0.1.0b1/tests/conftest.py +109 -0
  136. refhound-0.1.0b1/tests/corpus/secrets/negative/ordinary.txt +4 -0
  137. refhound-0.1.0b1/tests/corpus/secrets/positive/synthetic.txt +11 -0
  138. refhound-0.1.0b1/tests/integration/test_api.py +70 -0
  139. refhound-0.1.0b1/tests/integration/test_incomplete_cache.py +85 -0
  140. refhound-0.1.0b1/tests/integration/test_object_formats.py +41 -0
  141. refhound-0.1.0b1/tests/integration/test_offline_guard.py +58 -0
  142. refhound-0.1.0b1/tests/integration/test_repository_shapes.py +97 -0
  143. refhound-0.1.0b1/tests/integration/test_scan.py +409 -0
  144. refhound-0.1.0b1/tests/schemas/.gitkeep +1 -0
  145. refhound-0.1.0b1/tests/schemas/sarif-2.1.0.json +2882 -0
  146. refhound-0.1.0b1/tests/security/test_security_contracts.py +83 -0
  147. refhound-0.1.0b1/tests/unit/test_cache.py +39 -0
  148. refhound-0.1.0b1/tests/unit/test_cli_contract.py +73 -0
  149. refhound-0.1.0b1/tests/unit/test_cli_filters.py +63 -0
  150. refhound-0.1.0b1/tests/unit/test_cli_validation.py +33 -0
  151. refhound-0.1.0b1/tests/unit/test_config.py +65 -0
  152. refhound-0.1.0b1/tests/unit/test_detectors.py +139 -0
  153. refhound-0.1.0b1/tests/unit/test_git.py +135 -0
  154. refhound-0.1.0b1/tests/unit/test_git_features.py +28 -0
  155. refhound-0.1.0b1/tests/unit/test_profiles.py +33 -0
  156. refhound-0.1.0b1/tests/unit/test_reporting_contracts.py +78 -0
  157. refhound-0.1.0b1/tests/unit/test_storage_security.py +108 -0
  158. refhound-0.1.0b1/tests/unit/test_util.py +94 -0
  159. 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.