trustcheck 2.0.2__tar.gz → 2.0.4__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.
- trustcheck-2.0.4/.github/workflows/binary-security.yml +218 -0
- {trustcheck-2.0.2 → trustcheck-2.0.4}/.github/workflows/publish.yml +292 -24
- {trustcheck-2.0.2 → trustcheck-2.0.4}/CHANGELOG.md +5 -0
- {trustcheck-2.0.2 → trustcheck-2.0.4}/PKG-INFO +11 -3
- {trustcheck-2.0.2 → trustcheck-2.0.4}/README.md +10 -2
- {trustcheck-2.0.2 → trustcheck-2.0.4}/docs/getting-started/installation.md +18 -0
- {trustcheck-2.0.2 → trustcheck-2.0.4}/docs/index.md +0 -1
- {trustcheck-2.0.2 → trustcheck-2.0.4}/mkdocs.yml +0 -2
- trustcheck-2.0.4/scripts/build_standalone.py +41 -0
- trustcheck-2.0.4/scripts/trustcheck_binary.py +4 -0
- {trustcheck-2.0.2 → trustcheck-2.0.4}/snap/README.md +5 -3
- {trustcheck-2.0.2 → trustcheck-2.0.4}/snap/snapcraft.yaml +4 -0
- {trustcheck-2.0.2 → trustcheck-2.0.4}/src/trustcheck/_version.py +3 -3
- {trustcheck-2.0.2 → trustcheck-2.0.4}/src/trustcheck.egg-info/PKG-INFO +11 -3
- {trustcheck-2.0.2 → trustcheck-2.0.4}/src/trustcheck.egg-info/SOURCES.txt +5 -2
- trustcheck-2.0.4/tests/test_binary_security_workflow.py +83 -0
- trustcheck-2.0.4/tests/test_release_executable.py +85 -0
- {trustcheck-2.0.2 → trustcheck-2.0.4}/tests/test_snap_packaging.py +29 -25
- trustcheck-2.0.2/docs/guides/development.md +0 -69
- trustcheck-2.0.2/docs/guides/release-publishing.md +0 -94
- {trustcheck-2.0.2 → trustcheck-2.0.4}/.github/CODEOWNERS +0 -0
- {trustcheck-2.0.2 → trustcheck-2.0.4}/.github/ISSUE_TEMPLATE/general.yml +0 -0
- {trustcheck-2.0.2 → trustcheck-2.0.4}/.github/dependabot.yml +0 -0
- {trustcheck-2.0.2 → trustcheck-2.0.4}/.github/trustcheck-action-fail-policy.json +0 -0
- {trustcheck-2.0.2 → trustcheck-2.0.4}/.github/workflows/action-integration.yml +0 -0
- {trustcheck-2.0.2 → trustcheck-2.0.4}/.github/workflows/bandit.yml +0 -0
- {trustcheck-2.0.2 → trustcheck-2.0.4}/.github/workflows/ci.yml +0 -0
- {trustcheck-2.0.2 → trustcheck-2.0.4}/.github/workflows/codeql.yml +0 -0
- {trustcheck-2.0.2 → trustcheck-2.0.4}/.github/workflows/docs.yml +0 -0
- {trustcheck-2.0.2 → trustcheck-2.0.4}/.github/workflows/semgrep.yml +0 -0
- {trustcheck-2.0.2 → trustcheck-2.0.4}/.github/workflows/source-build.yml +0 -0
- {trustcheck-2.0.2 → trustcheck-2.0.4}/.gitignore +0 -0
- {trustcheck-2.0.2 → trustcheck-2.0.4}/CONTRIBUTING.md +0 -0
- {trustcheck-2.0.2 → trustcheck-2.0.4}/LICENSE +0 -0
- {trustcheck-2.0.2 → trustcheck-2.0.4}/SECURITY.md +0 -0
- {trustcheck-2.0.2 → trustcheck-2.0.4}/action.yml +0 -0
- {trustcheck-2.0.2 → trustcheck-2.0.4}/docs/assets/images/coverage.svg +0 -0
- {trustcheck-2.0.2 → trustcheck-2.0.4}/docs/assets/images/logo-bg-less.png +0 -0
- {trustcheck-2.0.2 → trustcheck-2.0.4}/docs/assets/images/logo.png +0 -0
- {trustcheck-2.0.2 → trustcheck-2.0.4}/docs/assets/javascripts/disable-search-shortcut.js +0 -0
- {trustcheck-2.0.2 → trustcheck-2.0.4}/docs/changelog.md +0 -0
- {trustcheck-2.0.2 → trustcheck-2.0.4}/docs/cli/configuration.md +0 -0
- {trustcheck-2.0.2 → trustcheck-2.0.4}/docs/cli/exit-codes.md +0 -0
- {trustcheck-2.0.2 → trustcheck-2.0.4}/docs/cli/index.md +0 -0
- {trustcheck-2.0.2 → trustcheck-2.0.4}/docs/cli/policies.md +0 -0
- {trustcheck-2.0.2 → trustcheck-2.0.4}/docs/getting-started/quickstart.md +0 -0
- {trustcheck-2.0.2 → trustcheck-2.0.4}/docs/guides/ci-integration.md +0 -0
- {trustcheck-2.0.2 → trustcheck-2.0.4}/docs/reference/compatibility.md +0 -0
- {trustcheck-2.0.2 → trustcheck-2.0.4}/docs/reference/json-contract.md +0 -0
- {trustcheck-2.0.2 → trustcheck-2.0.4}/docs/reference/python-api.md +0 -0
- {trustcheck-2.0.2 → trustcheck-2.0.4}/docs/reference/recommendations.md +0 -0
- {trustcheck-2.0.2 → trustcheck-2.0.4}/docs/reference/trust-model.md +0 -0
- {trustcheck-2.0.2 → trustcheck-2.0.4}/pyproject.toml +0 -0
- {trustcheck-2.0.2 → trustcheck-2.0.4}/scripts/update_coverage_badge.py +0 -0
- {trustcheck-2.0.2 → trustcheck-2.0.4}/setup.cfg +0 -0
- {trustcheck-2.0.2 → trustcheck-2.0.4}/snap/gui/icon.png +0 -0
- {trustcheck-2.0.2 → trustcheck-2.0.4}/src/trustcheck/__init__.py +0 -0
- {trustcheck-2.0.2 → trustcheck-2.0.4}/src/trustcheck/__main__.py +0 -0
- {trustcheck-2.0.2 → trustcheck-2.0.4}/src/trustcheck/advisories.py +0 -0
- {trustcheck-2.0.2 → trustcheck-2.0.4}/src/trustcheck/artifacts.py +0 -0
- {trustcheck-2.0.2 → trustcheck-2.0.4}/src/trustcheck/cli.py +0 -0
- {trustcheck-2.0.2 → trustcheck-2.0.4}/src/trustcheck/contract.py +0 -0
- {trustcheck-2.0.2 → trustcheck-2.0.4}/src/trustcheck/github_action.py +0 -0
- {trustcheck-2.0.2 → trustcheck-2.0.4}/src/trustcheck/lockfiles.py +0 -0
- {trustcheck-2.0.2 → trustcheck-2.0.4}/src/trustcheck/models.py +0 -0
- {trustcheck-2.0.2 → trustcheck-2.0.4}/src/trustcheck/policy.py +0 -0
- {trustcheck-2.0.2 → trustcheck-2.0.4}/src/trustcheck/py.typed +0 -0
- {trustcheck-2.0.2 → trustcheck-2.0.4}/src/trustcheck/pypi.py +0 -0
- {trustcheck-2.0.2 → trustcheck-2.0.4}/src/trustcheck/schemas.py +0 -0
- {trustcheck-2.0.2 → trustcheck-2.0.4}/src/trustcheck/service.py +0 -0
- {trustcheck-2.0.2 → trustcheck-2.0.4}/src/trustcheck.egg-info/dependency_links.txt +0 -0
- {trustcheck-2.0.2 → trustcheck-2.0.4}/src/trustcheck.egg-info/entry_points.txt +0 -0
- {trustcheck-2.0.2 → trustcheck-2.0.4}/src/trustcheck.egg-info/requires.txt +0 -0
- {trustcheck-2.0.2 → trustcheck-2.0.4}/src/trustcheck.egg-info/top_level.txt +0 -0
- {trustcheck-2.0.2 → trustcheck-2.0.4}/tests/_tmp/bad-scan.toml +0 -0
- {trustcheck-2.0.2 → trustcheck-2.0.4}/tests/_tmp/cache/5e491d79f8ba9e36d864ae50c690989677616cd509e5b99abb9272c8ad976435.json +0 -0
- {trustcheck-2.0.2 → trustcheck-2.0.4}/tests/_tmp/config_non_object.json +0 -0
- {trustcheck-2.0.2 → trustcheck-2.0.4}/tests/_tmp/empty-scan.toml +0 -0
- {trustcheck-2.0.2 → trustcheck-2.0.4}/tests/_tmp/empty-scan.txt +0 -0
- {trustcheck-2.0.2 → trustcheck-2.0.4}/tests/_tmp/invalid-scan.txt +0 -0
- {trustcheck-2.0.2 → trustcheck-2.0.4}/tests/_tmp/policy_non_object.json +0 -0
- {trustcheck-2.0.2 → trustcheck-2.0.4}/tests/_tmp/scan-poetry.toml +0 -0
- {trustcheck-2.0.2 → trustcheck-2.0.4}/tests/_tmp/scan-project.toml +0 -0
- {trustcheck-2.0.2 → trustcheck-2.0.4}/tests/fixtures/client_config.json +0 -0
- {trustcheck-2.0.2 → trustcheck-2.0.4}/tests/fixtures/policy_require_expected_repo.json +0 -0
- {trustcheck-2.0.2 → trustcheck-2.0.4}/tests/snapshots/contract_schema.json +0 -0
- {trustcheck-2.0.2 → trustcheck-2.0.4}/tests/snapshots/report_minimal.json +0 -0
- {trustcheck-2.0.2 → trustcheck-2.0.4}/tests/snapshots/report_verified.json +0 -0
- {trustcheck-2.0.2 → trustcheck-2.0.4}/tests/test_advisories.py +0 -0
- {trustcheck-2.0.2 → trustcheck-2.0.4}/tests/test_artifacts.py +0 -0
- {trustcheck-2.0.2 → trustcheck-2.0.4}/tests/test_cli.py +0 -0
- {trustcheck-2.0.2 → trustcheck-2.0.4}/tests/test_contract.py +0 -0
- {trustcheck-2.0.2 → trustcheck-2.0.4}/tests/test_edge_cases.py +0 -0
- {trustcheck-2.0.2 → trustcheck-2.0.4}/tests/test_github_action.py +0 -0
- {trustcheck-2.0.2 → trustcheck-2.0.4}/tests/test_integration_live.py +0 -0
- {trustcheck-2.0.2 → trustcheck-2.0.4}/tests/test_lockfiles.py +0 -0
- {trustcheck-2.0.2 → trustcheck-2.0.4}/tests/test_public_api.py +0 -0
- {trustcheck-2.0.2 → trustcheck-2.0.4}/tests/test_pypi.py +0 -0
- {trustcheck-2.0.2 → trustcheck-2.0.4}/tests/test_release_readiness.py +0 -0
- {trustcheck-2.0.2 → trustcheck-2.0.4}/tests/test_service.py +0 -0
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
name: Binary Security
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
pull_request:
|
|
6
|
+
workflow_dispatch:
|
|
7
|
+
|
|
8
|
+
permissions:
|
|
9
|
+
contents: read
|
|
10
|
+
|
|
11
|
+
jobs:
|
|
12
|
+
windows-defender:
|
|
13
|
+
name: Windows Defender
|
|
14
|
+
runs-on: windows-latest
|
|
15
|
+
|
|
16
|
+
steps:
|
|
17
|
+
- name: Check out repository
|
|
18
|
+
uses: actions/checkout@v6
|
|
19
|
+
with:
|
|
20
|
+
fetch-depth: 0
|
|
21
|
+
persist-credentials: false
|
|
22
|
+
|
|
23
|
+
- name: Set up Python
|
|
24
|
+
uses: actions/setup-python@v6
|
|
25
|
+
with:
|
|
26
|
+
python-version: "3.12"
|
|
27
|
+
|
|
28
|
+
- name: Install package and binary tooling
|
|
29
|
+
run: |
|
|
30
|
+
python -m pip install --upgrade pip
|
|
31
|
+
python -m pip install -e . "pyinstaller>=6.20,<7"
|
|
32
|
+
|
|
33
|
+
- name: Build Windows executable
|
|
34
|
+
run: python scripts/build_standalone.py
|
|
35
|
+
|
|
36
|
+
- name: Smoke test Windows executable
|
|
37
|
+
shell: pwsh
|
|
38
|
+
run: |
|
|
39
|
+
$binary = (Resolve-Path "dist\standalone\trustcheck.exe").Path
|
|
40
|
+
& $binary --version
|
|
41
|
+
if ($LASTEXITCODE -ne 0) {
|
|
42
|
+
exit $LASTEXITCODE
|
|
43
|
+
}
|
|
44
|
+
& $binary --help | Out-Null
|
|
45
|
+
if ($LASTEXITCODE -ne 0) {
|
|
46
|
+
exit $LASTEXITCODE
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
- name: Locate Microsoft Defender CLI
|
|
50
|
+
id: defender
|
|
51
|
+
shell: pwsh
|
|
52
|
+
run: |
|
|
53
|
+
$platformRoot = Join-Path $env:ProgramData "Microsoft\Windows Defender\Platform"
|
|
54
|
+
$candidates = @()
|
|
55
|
+
if (Test-Path $platformRoot) {
|
|
56
|
+
$candidates += Get-ChildItem -Path $platformRoot -Directory |
|
|
57
|
+
Sort-Object Name -Descending |
|
|
58
|
+
ForEach-Object { Join-Path $_.FullName "MpCmdRun.exe" }
|
|
59
|
+
}
|
|
60
|
+
$candidates += Join-Path $env:ProgramFiles "Windows Defender\MpCmdRun.exe"
|
|
61
|
+
$defender = $candidates |
|
|
62
|
+
Where-Object { Test-Path $_ } |
|
|
63
|
+
Select-Object -First 1
|
|
64
|
+
if (-not $defender) {
|
|
65
|
+
throw "MpCmdRun.exe was not found on the Windows runner."
|
|
66
|
+
}
|
|
67
|
+
"path=$defender" | Out-File -FilePath $env:GITHUB_OUTPUT -Append
|
|
68
|
+
Write-Host "Using Microsoft Defender CLI: $defender"
|
|
69
|
+
|
|
70
|
+
- name: Verify Microsoft Defender is enabled
|
|
71
|
+
shell: pwsh
|
|
72
|
+
run: |
|
|
73
|
+
Set-Service -Name wuauserv -StartupType Manual
|
|
74
|
+
Start-Service -Name wuauserv
|
|
75
|
+
$status = Get-MpComputerStatus
|
|
76
|
+
$status |
|
|
77
|
+
Select-Object AMServiceEnabled, AntivirusEnabled, AntivirusSignatureVersion |
|
|
78
|
+
Format-List |
|
|
79
|
+
Out-File defender-status.txt
|
|
80
|
+
Get-Content defender-status.txt
|
|
81
|
+
if (-not $status.AMServiceEnabled -or -not $status.AntivirusEnabled) {
|
|
82
|
+
throw "Microsoft Defender Antivirus is not enabled on the runner."
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
- name: Update Microsoft Defender signatures
|
|
86
|
+
shell: pwsh
|
|
87
|
+
env:
|
|
88
|
+
DEFENDER_CLI: ${{ steps.defender.outputs.path }}
|
|
89
|
+
run: |
|
|
90
|
+
"Microsoft Defender signature update" |
|
|
91
|
+
Set-Content -Path defender-signature-update.txt
|
|
92
|
+
$output = & $env:DEFENDER_CLI -SignatureUpdate 2>&1
|
|
93
|
+
$exitCode = $LASTEXITCODE
|
|
94
|
+
$output | Tee-Object -FilePath defender-signature-update.txt -Append
|
|
95
|
+
if ($exitCode -ne 0) {
|
|
96
|
+
Write-Error "Microsoft Defender signature update failed with exit code $exitCode."
|
|
97
|
+
exit $exitCode
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
- name: Scan executable with Microsoft Defender
|
|
101
|
+
shell: pwsh
|
|
102
|
+
env:
|
|
103
|
+
DEFENDER_CLI: ${{ steps.defender.outputs.path }}
|
|
104
|
+
run: |
|
|
105
|
+
$binary = (Resolve-Path "dist\standalone\trustcheck.exe").Path
|
|
106
|
+
"Microsoft Defender custom scan: $binary" |
|
|
107
|
+
Set-Content -Path defender-scan.txt
|
|
108
|
+
$output = & $env:DEFENDER_CLI `
|
|
109
|
+
-Scan `
|
|
110
|
+
-ScanType 3 `
|
|
111
|
+
-File $binary `
|
|
112
|
+
-DisableRemediation 2>&1
|
|
113
|
+
$exitCode = $LASTEXITCODE
|
|
114
|
+
$output | Tee-Object -FilePath defender-scan.txt -Append
|
|
115
|
+
if ($exitCode -ne 0) {
|
|
116
|
+
Write-Error "Microsoft Defender scan failed with exit code $exitCode."
|
|
117
|
+
exit $exitCode
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
- name: Generate Windows executable checksum
|
|
121
|
+
shell: pwsh
|
|
122
|
+
run: |
|
|
123
|
+
Get-FileHash "dist\standalone\trustcheck.exe" -Algorithm SHA256 |
|
|
124
|
+
Format-List |
|
|
125
|
+
Out-File "dist\standalone\trustcheck.exe.sha256.txt"
|
|
126
|
+
|
|
127
|
+
- name: Upload Microsoft Defender reports
|
|
128
|
+
if: always()
|
|
129
|
+
uses: actions/upload-artifact@v7
|
|
130
|
+
with:
|
|
131
|
+
name: windows-defender-reports-${{ github.sha }}
|
|
132
|
+
path: defender-*.txt
|
|
133
|
+
if-no-files-found: warn
|
|
134
|
+
|
|
135
|
+
- name: Upload clean Windows executable
|
|
136
|
+
uses: actions/upload-artifact@v7
|
|
137
|
+
with:
|
|
138
|
+
name: trustcheck-windows-${{ github.sha }}
|
|
139
|
+
path: |
|
|
140
|
+
dist/standalone/trustcheck.exe
|
|
141
|
+
dist/standalone/trustcheck.exe.sha256.txt
|
|
142
|
+
if-no-files-found: error
|
|
143
|
+
|
|
144
|
+
linux-clamav:
|
|
145
|
+
name: ClamAV
|
|
146
|
+
runs-on: ubuntu-latest
|
|
147
|
+
|
|
148
|
+
steps:
|
|
149
|
+
- name: Check out repository
|
|
150
|
+
uses: actions/checkout@v6
|
|
151
|
+
with:
|
|
152
|
+
fetch-depth: 0
|
|
153
|
+
persist-credentials: false
|
|
154
|
+
|
|
155
|
+
- name: Set up Python
|
|
156
|
+
uses: actions/setup-python@v6
|
|
157
|
+
with:
|
|
158
|
+
python-version: "3.12"
|
|
159
|
+
|
|
160
|
+
- name: Install package and binary tooling
|
|
161
|
+
run: |
|
|
162
|
+
python -m pip install --upgrade pip
|
|
163
|
+
python -m pip install -e . "pyinstaller>=6.20,<7"
|
|
164
|
+
|
|
165
|
+
- name: Build Linux executable
|
|
166
|
+
run: python scripts/build_standalone.py
|
|
167
|
+
|
|
168
|
+
- name: Smoke test Linux executable
|
|
169
|
+
run: |
|
|
170
|
+
test -x dist/standalone/trustcheck
|
|
171
|
+
dist/standalone/trustcheck --version
|
|
172
|
+
dist/standalone/trustcheck --help > /dev/null
|
|
173
|
+
|
|
174
|
+
- name: Install ClamAV
|
|
175
|
+
run: |
|
|
176
|
+
sudo apt-get update
|
|
177
|
+
sudo apt-get install --yes clamav
|
|
178
|
+
|
|
179
|
+
- name: Update ClamAV signatures
|
|
180
|
+
run: |
|
|
181
|
+
sudo systemctl stop clamav-freshclam.service 2>/dev/null || true
|
|
182
|
+
sudo freshclam --verbose | tee clamav-signature-update.txt
|
|
183
|
+
|
|
184
|
+
- name: Scan executable with ClamAV
|
|
185
|
+
run: |
|
|
186
|
+
set +e
|
|
187
|
+
clamscan \
|
|
188
|
+
--official-db-only=yes \
|
|
189
|
+
--verbose \
|
|
190
|
+
--log=clamav-scan.txt \
|
|
191
|
+
dist/standalone/trustcheck
|
|
192
|
+
scan_status=$?
|
|
193
|
+
set -e
|
|
194
|
+
cat clamav-scan.txt
|
|
195
|
+
if [ "$scan_status" -ne 0 ]; then
|
|
196
|
+
echo "ClamAV scan failed with exit code ${scan_status}." >&2
|
|
197
|
+
exit "$scan_status"
|
|
198
|
+
fi
|
|
199
|
+
|
|
200
|
+
- name: Generate Linux executable checksum
|
|
201
|
+
run: sha256sum dist/standalone/trustcheck > dist/standalone/trustcheck.sha256
|
|
202
|
+
|
|
203
|
+
- name: Upload ClamAV reports
|
|
204
|
+
if: always()
|
|
205
|
+
uses: actions/upload-artifact@v7
|
|
206
|
+
with:
|
|
207
|
+
name: linux-clamav-reports-${{ github.sha }}
|
|
208
|
+
path: clamav-*.txt
|
|
209
|
+
if-no-files-found: warn
|
|
210
|
+
|
|
211
|
+
- name: Upload clean Linux executable
|
|
212
|
+
uses: actions/upload-artifact@v7
|
|
213
|
+
with:
|
|
214
|
+
name: trustcheck-linux-${{ github.sha }}
|
|
215
|
+
path: |
|
|
216
|
+
dist/standalone/trustcheck
|
|
217
|
+
dist/standalone/trustcheck.sha256
|
|
218
|
+
if-no-files-found: error
|
|
@@ -86,8 +86,8 @@ jobs:
|
|
|
86
86
|
- name: Run type checks
|
|
87
87
|
run: mypy src
|
|
88
88
|
|
|
89
|
-
|
|
90
|
-
needs:
|
|
89
|
+
matrix-build:
|
|
90
|
+
needs: qa
|
|
91
91
|
strategy:
|
|
92
92
|
fail-fast: false
|
|
93
93
|
matrix:
|
|
@@ -159,9 +159,7 @@ jobs:
|
|
|
159
159
|
python -m build --sdist --wheel --outdir "$archive_root/dist" "$archive_root"
|
|
160
160
|
|
|
161
161
|
coverage-build:
|
|
162
|
-
needs:
|
|
163
|
-
- qa
|
|
164
|
-
- test-matrix
|
|
162
|
+
needs: matrix-build
|
|
165
163
|
runs-on: ubuntu-latest
|
|
166
164
|
|
|
167
165
|
steps:
|
|
@@ -235,10 +233,54 @@ jobs:
|
|
|
235
233
|
name: dist-${{ github.sha }}
|
|
236
234
|
path: dist/*
|
|
237
235
|
|
|
236
|
+
clam-av:
|
|
237
|
+
needs: coverage-build
|
|
238
|
+
runs-on: ubuntu-latest
|
|
239
|
+
|
|
240
|
+
steps:
|
|
241
|
+
- name: Download build artifacts
|
|
242
|
+
uses: actions/download-artifact@v8
|
|
243
|
+
with:
|
|
244
|
+
name: dist-${{ github.sha }}
|
|
245
|
+
path: dist
|
|
246
|
+
|
|
247
|
+
- name: Install ClamAV
|
|
248
|
+
run: |
|
|
249
|
+
sudo apt-get update
|
|
250
|
+
sudo apt-get install --yes clamav
|
|
251
|
+
|
|
252
|
+
- name: Update ClamAV signatures
|
|
253
|
+
run: |
|
|
254
|
+
sudo systemctl stop clamav-freshclam.service 2>/dev/null || true
|
|
255
|
+
sudo freshclam --verbose | tee clamav-signature-update.txt
|
|
256
|
+
|
|
257
|
+
- name: Scan release build with ClamAV
|
|
258
|
+
run: |
|
|
259
|
+
set +e
|
|
260
|
+
clamscan \
|
|
261
|
+
--recursive \
|
|
262
|
+
--official-db-only=yes \
|
|
263
|
+
--verbose \
|
|
264
|
+
--log=clamav-scan.txt \
|
|
265
|
+
dist
|
|
266
|
+
scan_status=$?
|
|
267
|
+
set -e
|
|
268
|
+
cat clamav-scan.txt
|
|
269
|
+
if [ "$scan_status" -ne 0 ]; then
|
|
270
|
+
echo "ClamAV scan failed with exit code ${scan_status}." >&2
|
|
271
|
+
exit "$scan_status"
|
|
272
|
+
fi
|
|
273
|
+
|
|
274
|
+
- name: Upload ClamAV reports
|
|
275
|
+
if: always()
|
|
276
|
+
uses: actions/upload-artifact@v7
|
|
277
|
+
with:
|
|
278
|
+
name: release-clamav-reports-${{ github.sha }}
|
|
279
|
+
path: clamav-*.txt
|
|
280
|
+
if-no-files-found: warn
|
|
281
|
+
|
|
238
282
|
snap-qa:
|
|
239
|
-
needs:
|
|
240
|
-
- qa
|
|
241
|
-
- test-matrix
|
|
283
|
+
needs: clam-av
|
|
242
284
|
runs-on: ubuntu-latest
|
|
243
285
|
|
|
244
286
|
steps:
|
|
@@ -310,6 +352,25 @@ jobs:
|
|
|
310
352
|
export PATH="/snap/bin:$PATH"
|
|
311
353
|
test "$(command -v trustcheck)" = "/snap/bin/trustcheck"
|
|
312
354
|
trustcheck --help > /dev/null
|
|
355
|
+
trustcheck inspect sampleproject \
|
|
356
|
+
--version 4.0.0 \
|
|
357
|
+
--format json \
|
|
358
|
+
> snap-verification-report.json
|
|
359
|
+
python - <<'PY'
|
|
360
|
+
import json
|
|
361
|
+
from pathlib import Path
|
|
362
|
+
|
|
363
|
+
payload = json.loads(
|
|
364
|
+
Path("snap-verification-report.json").read_text(encoding="utf-8")
|
|
365
|
+
)
|
|
366
|
+
report = payload["report"]
|
|
367
|
+
assert report["coverage"]["verified_files"] > 0, report["coverage"]
|
|
368
|
+
failures = report["diagnostics"]["artifact_failures"]
|
|
369
|
+
assert not any(
|
|
370
|
+
item["subcode"] == "unexpected_verification_error"
|
|
371
|
+
for item in failures
|
|
372
|
+
), failures
|
|
373
|
+
PY
|
|
313
374
|
|
|
314
375
|
- name: Generate snap checksum
|
|
315
376
|
env:
|
|
@@ -330,10 +391,204 @@ jobs:
|
|
|
330
391
|
${{ steps.snapcraft.outputs.snap }}.sha256
|
|
331
392
|
if-no-files-found: error
|
|
332
393
|
|
|
394
|
+
build-windows-executable:
|
|
395
|
+
needs: coverage-build
|
|
396
|
+
runs-on: windows-latest
|
|
397
|
+
|
|
398
|
+
steps:
|
|
399
|
+
- name: Check out tagged commit
|
|
400
|
+
uses: actions/checkout@v6
|
|
401
|
+
with:
|
|
402
|
+
fetch-depth: 0
|
|
403
|
+
ref: ${{ github.sha }}
|
|
404
|
+
persist-credentials: false
|
|
405
|
+
|
|
406
|
+
- name: Set up Python
|
|
407
|
+
uses: actions/setup-python@v6
|
|
408
|
+
with:
|
|
409
|
+
python-version: "3.12"
|
|
410
|
+
|
|
411
|
+
- name: Install PyInstaller tooling
|
|
412
|
+
run: |
|
|
413
|
+
python -m pip install --upgrade pip
|
|
414
|
+
python -m pip install -e . "pyinstaller>=6.20,<7"
|
|
415
|
+
|
|
416
|
+
- name: Build standalone Windows executable
|
|
417
|
+
run: python scripts/build_standalone.py
|
|
418
|
+
|
|
419
|
+
- name: Smoke test and name release executable
|
|
420
|
+
id: executable
|
|
421
|
+
shell: pwsh
|
|
422
|
+
env:
|
|
423
|
+
TAG_NAME: ${{ github.ref_name }}
|
|
424
|
+
run: |
|
|
425
|
+
$binary = (Resolve-Path "dist\standalone\trustcheck.exe").Path
|
|
426
|
+
$releaseVersion = $env:TAG_NAME.TrimStart("v")
|
|
427
|
+
$versionOutput = & $binary --version
|
|
428
|
+
if ($LASTEXITCODE -ne 0) {
|
|
429
|
+
throw "The standalone executable failed its version smoke test."
|
|
430
|
+
}
|
|
431
|
+
if (-not $versionOutput.StartsWith("trustcheck $releaseVersion ")) {
|
|
432
|
+
throw "Executable version '$versionOutput' does not match $releaseVersion."
|
|
433
|
+
}
|
|
434
|
+
& $binary --help | Out-Null
|
|
435
|
+
if ($LASTEXITCODE -ne 0) {
|
|
436
|
+
throw "The standalone executable failed its help smoke test."
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
New-Item -ItemType Directory -Force "release\windows" | Out-Null
|
|
440
|
+
$releaseName = "trustcheck-$releaseVersion-windows-x86_64.exe"
|
|
441
|
+
$releasePath = Join-Path "release\windows" $releaseName
|
|
442
|
+
Move-Item -LiteralPath $binary -Destination $releasePath
|
|
443
|
+
"path=$releasePath" >> $env:GITHUB_OUTPUT
|
|
444
|
+
"name=$releaseName" >> $env:GITHUB_OUTPUT
|
|
445
|
+
|
|
446
|
+
- name: Generate executable checksum
|
|
447
|
+
shell: pwsh
|
|
448
|
+
env:
|
|
449
|
+
EXECUTABLE_NAME: ${{ steps.executable.outputs.name }}
|
|
450
|
+
EXECUTABLE_PATH: ${{ steps.executable.outputs.path }}
|
|
451
|
+
run: |
|
|
452
|
+
$hash = (Get-FileHash $env:EXECUTABLE_PATH -Algorithm SHA256).Hash.ToLowerInvariant()
|
|
453
|
+
"$hash $env:EXECUTABLE_NAME" |
|
|
454
|
+
Set-Content -Encoding ascii "$env:EXECUTABLE_PATH.sha256"
|
|
455
|
+
|
|
456
|
+
- name: Attest executable build provenance
|
|
457
|
+
uses: actions/attest-build-provenance@v4
|
|
458
|
+
with:
|
|
459
|
+
subject-path: ${{ steps.executable.outputs.path }}
|
|
460
|
+
|
|
461
|
+
- name: Upload executable for Microsoft Defender scan
|
|
462
|
+
uses: actions/upload-artifact@v7
|
|
463
|
+
with:
|
|
464
|
+
name: windows-executable-unscanned-${{ github.sha }}
|
|
465
|
+
path: |
|
|
466
|
+
${{ steps.executable.outputs.path }}
|
|
467
|
+
${{ steps.executable.outputs.path }}.sha256
|
|
468
|
+
if-no-files-found: error
|
|
469
|
+
|
|
470
|
+
windows-defender-scan:
|
|
471
|
+
needs: build-windows-executable
|
|
472
|
+
runs-on: windows-latest
|
|
473
|
+
|
|
474
|
+
steps:
|
|
475
|
+
- name: Download built Windows executable
|
|
476
|
+
uses: actions/download-artifact@v8
|
|
477
|
+
with:
|
|
478
|
+
name: windows-executable-unscanned-${{ github.sha }}
|
|
479
|
+
path: windows-executable
|
|
480
|
+
|
|
481
|
+
- name: Locate built Windows executable
|
|
482
|
+
id: executable
|
|
483
|
+
shell: pwsh
|
|
484
|
+
run: |
|
|
485
|
+
$binary = Get-ChildItem -Path "windows-executable" -Filter "*.exe" -File |
|
|
486
|
+
Select-Object -First 1
|
|
487
|
+
if (-not $binary) {
|
|
488
|
+
throw "The Windows executable from the build job was not found."
|
|
489
|
+
}
|
|
490
|
+
"path=$($binary.FullName)" >> $env:GITHUB_OUTPUT
|
|
491
|
+
Write-Host "Scanning executable produced by the build job: $($binary.FullName)"
|
|
492
|
+
|
|
493
|
+
- name: Locate Microsoft Defender CLI
|
|
494
|
+
id: defender
|
|
495
|
+
shell: pwsh
|
|
496
|
+
run: |
|
|
497
|
+
$platformRoot = Join-Path $env:ProgramData "Microsoft\Windows Defender\Platform"
|
|
498
|
+
$candidates = @()
|
|
499
|
+
if (Test-Path $platformRoot) {
|
|
500
|
+
$candidates += Get-ChildItem -Path $platformRoot -Directory |
|
|
501
|
+
Sort-Object Name -Descending |
|
|
502
|
+
ForEach-Object { Join-Path $_.FullName "MpCmdRun.exe" }
|
|
503
|
+
}
|
|
504
|
+
$candidates += Join-Path $env:ProgramFiles "Windows Defender\MpCmdRun.exe"
|
|
505
|
+
$defender = $candidates |
|
|
506
|
+
Where-Object { Test-Path $_ } |
|
|
507
|
+
Select-Object -First 1
|
|
508
|
+
if (-not $defender) {
|
|
509
|
+
throw "MpCmdRun.exe was not found on the Windows runner."
|
|
510
|
+
}
|
|
511
|
+
"path=$defender" >> $env:GITHUB_OUTPUT
|
|
512
|
+
Write-Host "Using Microsoft Defender CLI: $defender"
|
|
513
|
+
|
|
514
|
+
- name: Verify Microsoft Defender is enabled
|
|
515
|
+
shell: pwsh
|
|
516
|
+
run: |
|
|
517
|
+
Set-Service -Name wuauserv -StartupType Manual
|
|
518
|
+
Start-Service -Name wuauserv
|
|
519
|
+
$status = Get-MpComputerStatus
|
|
520
|
+
$status |
|
|
521
|
+
Select-Object AMServiceEnabled, AntivirusEnabled, AntivirusSignatureVersion |
|
|
522
|
+
Format-List |
|
|
523
|
+
Out-File defender-status.txt
|
|
524
|
+
Get-Content defender-status.txt
|
|
525
|
+
if (-not $status.AMServiceEnabled -or -not $status.AntivirusEnabled) {
|
|
526
|
+
throw "Microsoft Defender Antivirus is not enabled on the runner."
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
- name: Update Microsoft Defender signatures
|
|
530
|
+
shell: pwsh
|
|
531
|
+
env:
|
|
532
|
+
DEFENDER_CLI: ${{ steps.defender.outputs.path }}
|
|
533
|
+
run: |
|
|
534
|
+
"Microsoft Defender signature update" |
|
|
535
|
+
Set-Content -Path defender-signature-update.txt
|
|
536
|
+
$output = & $env:DEFENDER_CLI -SignatureUpdate 2>&1
|
|
537
|
+
$exitCode = $LASTEXITCODE
|
|
538
|
+
$output | Tee-Object -FilePath defender-signature-update.txt -Append
|
|
539
|
+
if ($exitCode -ne 0) {
|
|
540
|
+
Write-Error "Microsoft Defender signature update failed with exit code $exitCode."
|
|
541
|
+
exit $exitCode
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
- name: Scan built executable with Microsoft Defender
|
|
545
|
+
shell: pwsh
|
|
546
|
+
env:
|
|
547
|
+
DEFENDER_CLI: ${{ steps.defender.outputs.path }}
|
|
548
|
+
EXECUTABLE_PATH: ${{ steps.executable.outputs.path }}
|
|
549
|
+
run: |
|
|
550
|
+
"Microsoft Defender custom scan: $env:EXECUTABLE_PATH" |
|
|
551
|
+
Set-Content -Path defender-scan.txt
|
|
552
|
+
$output = & $env:DEFENDER_CLI `
|
|
553
|
+
-Scan `
|
|
554
|
+
-ScanType 3 `
|
|
555
|
+
-File $env:EXECUTABLE_PATH `
|
|
556
|
+
-DisableRemediation 2>&1
|
|
557
|
+
$exitCode = $LASTEXITCODE
|
|
558
|
+
$output | Tee-Object -FilePath defender-scan.txt -Append
|
|
559
|
+
if ($exitCode -ne 0) {
|
|
560
|
+
Write-Error "Microsoft Defender scan failed with exit code $exitCode."
|
|
561
|
+
exit $exitCode
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
- name: Regenerate executable checksum after scan
|
|
565
|
+
shell: pwsh
|
|
566
|
+
env:
|
|
567
|
+
EXECUTABLE_PATH: ${{ steps.executable.outputs.path }}
|
|
568
|
+
run: |
|
|
569
|
+
$name = Split-Path -Leaf $env:EXECUTABLE_PATH
|
|
570
|
+
$hash = (Get-FileHash $env:EXECUTABLE_PATH -Algorithm SHA256).Hash.ToLowerInvariant()
|
|
571
|
+
"$hash $name" | Set-Content -Encoding ascii "$env:EXECUTABLE_PATH.sha256"
|
|
572
|
+
|
|
573
|
+
- name: Upload Microsoft Defender reports
|
|
574
|
+
if: always()
|
|
575
|
+
uses: actions/upload-artifact@v7
|
|
576
|
+
with:
|
|
577
|
+
name: windows-defender-reports-${{ github.sha }}
|
|
578
|
+
path: defender-*.txt
|
|
579
|
+
if-no-files-found: warn
|
|
580
|
+
|
|
581
|
+
- name: Upload scanned Windows executable
|
|
582
|
+
uses: actions/upload-artifact@v7
|
|
583
|
+
with:
|
|
584
|
+
name: windows-executable-${{ github.sha }}
|
|
585
|
+
path: |
|
|
586
|
+
windows-executable/*.exe
|
|
587
|
+
windows-executable/*.exe.sha256
|
|
588
|
+
if-no-files-found: error
|
|
589
|
+
|
|
333
590
|
publish-pypi:
|
|
334
|
-
needs:
|
|
335
|
-
- coverage-build
|
|
336
|
-
- snap-qa
|
|
591
|
+
needs: coverage-build
|
|
337
592
|
runs-on: ubuntu-latest
|
|
338
593
|
|
|
339
594
|
steps:
|
|
@@ -358,7 +613,6 @@ jobs:
|
|
|
358
613
|
publish-github-action:
|
|
359
614
|
needs:
|
|
360
615
|
- coverage-build
|
|
361
|
-
- snap-qa
|
|
362
616
|
- verify-tag
|
|
363
617
|
runs-on: ubuntu-latest
|
|
364
618
|
|
|
@@ -378,12 +632,6 @@ jobs:
|
|
|
378
632
|
name: dist-${{ github.sha }}
|
|
379
633
|
path: dist
|
|
380
634
|
|
|
381
|
-
- name: Download verified snap
|
|
382
|
-
uses: actions/download-artifact@v8
|
|
383
|
-
with:
|
|
384
|
-
name: snap-${{ github.sha }}
|
|
385
|
-
path: snap-artifact
|
|
386
|
-
|
|
387
635
|
- name: Create GitHub Release with generated notes
|
|
388
636
|
uses: softprops/action-gh-release@v3
|
|
389
637
|
with:
|
|
@@ -394,20 +642,19 @@ jobs:
|
|
|
394
642
|
body: |
|
|
395
643
|
Published from immutable commit `${{ github.sha }}`.
|
|
396
644
|
The release workflow publishes PyPI, GitHub Action, and Snap Store
|
|
397
|
-
distributions
|
|
645
|
+
distributions after shared tag verification, QA, matrix, and coverage builds.
|
|
398
646
|
|
|
399
647
|
Release artifacts:
|
|
400
648
|
- `dist/*`
|
|
401
649
|
- `dist/SHA256SUMS.txt`
|
|
402
650
|
- `dist/trustcheck-sbom.json`
|
|
403
|
-
-
|
|
651
|
+
- standalone `trustcheck-*-windows-x86_64.exe` with checksum
|
|
404
652
|
|
|
405
653
|
GitHub Action:
|
|
406
654
|
- Immutable: `uses: Halfblood-Prince/trustcheck@${{ github.ref_name }}`
|
|
407
655
|
- Compatible major: `uses: Halfblood-Prince/trustcheck@${{ needs['verify-tag'].outputs.action_major_tag }}`
|
|
408
656
|
files: |
|
|
409
657
|
dist/*
|
|
410
|
-
snap-artifact/*
|
|
411
658
|
|
|
412
659
|
- name: Publish moving major action tag
|
|
413
660
|
env:
|
|
@@ -463,10 +710,31 @@ jobs:
|
|
|
463
710
|
echo "[Edit ${RELEASE_TAG} and select **Publish this Action to the GitHub Marketplace**](https://github.com/${GITHUB_REPOSITORY}/releases/edit/${RELEASE_TAG})."
|
|
464
711
|
} >> "$GITHUB_STEP_SUMMARY"
|
|
465
712
|
|
|
466
|
-
|
|
713
|
+
upload-windows-executable:
|
|
467
714
|
needs:
|
|
468
|
-
-
|
|
469
|
-
-
|
|
715
|
+
- windows-defender-scan
|
|
716
|
+
- publish-github-action
|
|
717
|
+
runs-on: ubuntu-latest
|
|
718
|
+
|
|
719
|
+
steps:
|
|
720
|
+
- name: Download Windows executable
|
|
721
|
+
uses: actions/download-artifact@v8
|
|
722
|
+
with:
|
|
723
|
+
name: windows-executable-${{ github.sha }}
|
|
724
|
+
path: windows-executable
|
|
725
|
+
|
|
726
|
+
- name: Attach executable to GitHub Release
|
|
727
|
+
env:
|
|
728
|
+
GH_TOKEN: ${{ secrets.RELEASE_TOKEN || github.token }}
|
|
729
|
+
RELEASE_TAG: ${{ github.ref_name }}
|
|
730
|
+
run: |
|
|
731
|
+
gh release upload "$RELEASE_TAG" \
|
|
732
|
+
windows-executable/* \
|
|
733
|
+
--clobber \
|
|
734
|
+
--repo "$GITHUB_REPOSITORY"
|
|
735
|
+
|
|
736
|
+
publish-snap:
|
|
737
|
+
needs: snap-qa
|
|
470
738
|
runs-on: ubuntu-latest
|
|
471
739
|
|
|
472
740
|
steps:
|
|
@@ -31,6 +31,11 @@ The project follows Semantic Versioning for the supported public API described i
|
|
|
31
31
|
- Renamed the reusable GitHub Action and Marketplace display name to `TrustCheck Package Scanner`.
|
|
32
32
|
- Expanded the Snap Store listing with richer feature copy, quick-start examples, project links, and a dedicated storefront icon.
|
|
33
33
|
- Updated Snap release smoke tests and installation documentation to verify the public `trustcheck` command and diagnose shells where `/snap/bin` is missing from `PATH`.
|
|
34
|
+
- Redirected Sigstore XDG data, cache, and configuration into writable Snap-owned storage, fixing errno 13 provenance verification failures under strict confinement.
|
|
35
|
+
- Expanded Snap release QA to perform live verified-provenance inspection from the installed snap and reject unexpected verification errors.
|
|
36
|
+
- Added a push-triggered binary security workflow that builds standalone Windows and Linux executables with PyInstaller.
|
|
37
|
+
- Added a parallel release job that builds, smoke-tests, checksums, and attests a versioned Windows executable, then attaches it to the GitHub release.
|
|
38
|
+
- Added Microsoft Defender CLI and ClamAV scanning, retained scan reports, clean binary artifacts, checksums, and independent README check-run badges.
|
|
34
39
|
|
|
35
40
|
## [1.9.0] - 2026-06-09
|
|
36
41
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: trustcheck
|
|
3
|
-
Version: 2.0.
|
|
3
|
+
Version: 2.0.4
|
|
4
4
|
Summary: Package trust and provenance verification for PyPI consumers.
|
|
5
5
|
License: Trustcheck Personal Use License
|
|
6
6
|
|
|
@@ -113,13 +113,15 @@ Dynamic: license-file
|
|
|
113
113
|
[](https://github.com/Halfblood-Prince/trustcheck/actions/workflows/ci.yml)
|
|
114
114
|
[](https://github.com/Halfblood-Prince/trustcheck/actions/workflows/bandit.yml)
|
|
115
115
|
[](https://github.com/Halfblood-Prince/trustcheck/actions/workflows/semgrep.yml)
|
|
116
|
+
[](https://github.com/Halfblood-Prince/trustcheck/actions/workflows/binary-security.yml)
|
|
117
|
+
[](https://github.com/Halfblood-Prince/trustcheck/actions/workflows/binary-security.yml)
|
|
116
118
|
[](https://github.com/Halfblood-Prince/trustcheck/actions/workflows/ci.yml)
|
|
117
119
|
[](https://github.com/Halfblood-Prince/trustcheck/actions/workflows/ci.yml)
|
|
118
120
|
[](https://github.com/Halfblood-Prince/trustcheck/actions/workflows/ci.yml)
|
|
119
121
|
[](https://pypi.org/project/trustcheck/)
|
|
120
122
|
[](https://github.com/Halfblood-Prince/trustcheck/actions/workflows/ci.yml)
|
|
121
123
|
[](https://pepy.tech/projects/trustcheck)
|
|
122
|
-
[](https://github.com/
|
|
124
|
+
[](https://github.com/marketplace/actions/trustcheck-package-scanner)
|
|
123
125
|
|
|
124
126
|
`trustcheck` is a Python package and CLI for evaluating the trust posture of PyPI releases before they are installed, promoted, or approved.
|
|
125
127
|
|
|
@@ -140,6 +142,12 @@ For a selected package version, `trustcheck` can:
|
|
|
140
142
|
- optionally inspect wheel and sdist contents without importing or executing package code
|
|
141
143
|
- emit concise text output or structured JSON for automation
|
|
142
144
|
|
|
145
|
+
Every push also builds standalone Windows and Linux executables. The Windows
|
|
146
|
+
artifact is scanned with Microsoft Defender's `MpCmdRun.exe`; the Linux
|
|
147
|
+
artifact is scanned with ClamAV. Clean binaries, checksums, and scanner reports
|
|
148
|
+
are retained as workflow artifacts by
|
|
149
|
+
[Binary Security](https://github.com/Halfblood-Prince/trustcheck/actions/workflows/binary-security.yml).
|
|
150
|
+
|
|
143
151
|
## Installation
|
|
144
152
|
|
|
145
153
|
Install from PyPI:
|
|
@@ -318,7 +326,7 @@ Full documentation: https://halfblood-prince.github.io/trustcheck/
|
|
|
318
326
|
- Integrations: [JSON contract](https://halfblood-prince.github.io/trustcheck/reference/json-contract/), [Python API](https://halfblood-prince.github.io/trustcheck/reference/python-api/), and [Compatibility](https://halfblood-prince.github.io/trustcheck/reference/compatibility/)
|
|
319
327
|
- Trust model: [Verification model and repository matching](https://halfblood-prince.github.io/trustcheck/reference/trust-model/)
|
|
320
328
|
- Automation: [CI integration](https://halfblood-prince.github.io/trustcheck/guides/ci-integration/)
|
|
321
|
-
- Project details: [
|
|
329
|
+
- Project details: [Changelog](https://halfblood-prince.github.io/trustcheck/changelog/)
|
|
322
330
|
|
|
323
331
|
Project support:
|
|
324
332
|
|