mirror of
https://github.com/Anon-Planet/thgtoa.git
synced 2026-05-06 11:34:18 +02:00
Compare commits
14 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 1bb0acc3e8 | |||
| 25bc901ece | |||
| 78a0a37ee8 | |||
| aeb63cd7ba | |||
| 64ddd18535 | |||
| 7c9847e7d1 | |||
| 1e8c90513f | |||
| 2d09d7c01c | |||
| 1938e031ee | |||
| 8483d6336b | |||
| 1c168691c5 | |||
| ae50911375 | |||
| df2dd61676 | |||
| 904fa24478 |
@@ -53,61 +53,23 @@ jobs:
|
||||
sudo apt-get update
|
||||
sudo apt-get install gnupg
|
||||
|
||||
- name: 🖨️ Build PDFs
|
||||
- name: 🖨️ Build & Hash PDFs
|
||||
env:
|
||||
CI: true
|
||||
run: python scripts/build_guide_pdf.py --${{ inputs.build_mode || 'both' }}
|
||||
|
||||
- name: 🔒 Sign SHA256 hash file with GPG
|
||||
env:
|
||||
GPG_KEY: ${{ secrets.GPG_PRIVATE_KEY }}
|
||||
GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }}
|
||||
run: |
|
||||
cd ${{ github.workspace }}
|
||||
|
||||
# Import GPG key
|
||||
export GPG_TTY=$(tty)
|
||||
echo "$GPG_KEY" | gpg --batch --import 2>/dev/null || true
|
||||
|
||||
|
||||
|
||||
- name: 🔒 Sign PDF files with GPG
|
||||
env:
|
||||
GPG_KEY: ${{ secrets.GPG_PRIVATE_KEY }}
|
||||
GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }}
|
||||
run: |
|
||||
cd ${{ github.workspace }}
|
||||
|
||||
# Import GPG key if not already imported
|
||||
export GPG_TTY=$(tty)
|
||||
echo "$GPG_KEY" | gpg --batch --import 2>/dev/null || true
|
||||
|
||||
# Create combined hash file with all PDFs
|
||||
sha256sum export/thgtoa.pdf > export/checksums.sha256
|
||||
sha256sum export/thgtoa-dark.pdf >> export/checksums.sha256
|
||||
|
||||
# Sign the checksum file
|
||||
gpg --batch --yes --armor --detach-sign --output export/checksums.sha256.sig export/checksums.sha256 2>/dev/null || true
|
||||
|
||||
# Sign each PDF file individually with detached signature
|
||||
for pdf_file in export/*.pdf; do
|
||||
if [ -f "$pdf_file" ]; then
|
||||
base_name=$(basename "$pdf_file")
|
||||
echo "Signing $base_name..."
|
||||
gpg --default-key 17ECA05F768DEDF6 --batch --yes --armor --detach-sign --output "export/${pdf_file}.sig" "$pdf_file" 2>/dev/null || true
|
||||
fi
|
||||
done
|
||||
|
||||
# Verify signatures were created
|
||||
ls -la export/*.sig 2>/dev/null || echo "No signature files found in export/"
|
||||
python scripts/build_guide_pdf.py --${{ inputs.build_mode || 'both' }}
|
||||
for f in ./export/*.pdf; do
|
||||
echo "sha256sums: $f"; sha256sum "$f" >> export/sha256sums.txt; done
|
||||
for f in ./export/*.pdf; do
|
||||
echo "b2sums: $f"; b2sum "$f" >> export/b2sums.txt; done
|
||||
|
||||
- name: 🦠 Upload PDFs to VirusTotal
|
||||
uses: crazy-max/ghaction-virustotal@v5
|
||||
with:
|
||||
vt_api_key: ${{ secrets.VT_API_KEY }}
|
||||
files: |
|
||||
./export/thgtoa.pdf
|
||||
./export/thgtoa-dark.pdf
|
||||
export/thgtoa.pdf
|
||||
export/thgtoa-dark.pdf
|
||||
|
||||
- name: 📊 Extract VT scan results
|
||||
id: vt-scan
|
||||
@@ -125,7 +87,7 @@ jobs:
|
||||
### thgtoa.pdf (Light Mode)
|
||||
- **VT Report:** https://www.virustotal.com/gui/file/\$(sha256sum export/thgtoa.pdf | cut -d' ' -f1)
|
||||
|
||||
### thgtoa-dark.pdf (Dark Mode)
|
||||
### thgtoa-dark.pdf (Dark Mode) (currently broken)
|
||||
- **VT Report:** https://www.virustotal.com/gui/file/\$(sha256sum export/thgtoa-dark.pdf | cut -d' ' -f1)
|
||||
|
||||
---
|
||||
@@ -135,12 +97,9 @@ jobs:
|
||||
- name: 📤 Upload export directory as artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: pdf-export-${{ inputs.build_mode || 'both' }}
|
||||
name: export
|
||||
path: |
|
||||
export/*.pdf
|
||||
export/*.sig
|
||||
export/*.sha256
|
||||
export/virus-total-results.md
|
||||
export/*
|
||||
if-no-files-found: error
|
||||
retention-days: 90
|
||||
compression-level: 0
|
||||
|
||||
@@ -1,20 +1,34 @@
|
||||
---
|
||||
title: "TBA"
|
||||
description: ""
|
||||
schema:
|
||||
"@context": https://schema.org
|
||||
"@type": Organization
|
||||
"@id": https://www.anonymousplanet.org/
|
||||
name: Anonymous Planet
|
||||
url: https://www.anonymousplanet.org/authors/
|
||||
logo: ../media/favicon.png
|
||||
sameAs:
|
||||
- https://github.com/Anon-Planet
|
||||
- https://opencollective.com/anonymousplanetorg
|
||||
- https://mastodon.social/@anonymousplanet
|
||||
---
|
||||
|
||||
# Changelog
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
### Added
|
||||
|
||||
- This changelog page
|
||||
- Add ways to verify the files
|
||||
|
||||
### Changed
|
||||
|
||||
- Refactored GitHub Actions workflow **Build PDF** (`scripts\build_guide_pdf.py`): now builds both light and dark mode PDFs (`export/thgtoa.pdf` and `export/thgtoa-dark.pdf` respectively).
|
||||
- Restored previous VT scans workflow **VirusTotal Scan** (`.github/workflows/vt-scan.yml`): submit files to VT for malware scanning. Links will be published on the site.
|
||||
- Restored previous VT scans
|
||||
|
||||
## Fixed
|
||||
|
||||
@@ -49,3 +63,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
|
||||
[Unreleased]: https://github.com/Anon-Planet/thgtoa/compare/v1.2.1...HEAD
|
||||
[1.2.1]: https://github.com/Anon-Planet/thgtoa/releases/tag/v1.2.1
|
||||
|
||||
***The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),***
|
||||
***and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).***
|
||||
+15
-158
@@ -1,81 +1,36 @@
|
||||
# Development
|
||||
|
||||
## Overview
|
||||
|
||||
This repository now includes an automated workflow that handles PDF generation, verification, and distribution with the following features:
|
||||
|
||||
??? Note "How the pipeline works"
|
||||
|
||||
1. **Automatic PDF Generation** - Builds both light and dark mode PDFs from MkDocs source
|
||||
2. **SHA256 Hash Generation** - Creates hash files for integrity verification
|
||||
3. **GPG Signature Signing** - Signs all PDFs and hash files with repository GPG key
|
||||
4. **VirusTotal Scanning** - Automatically scans PDFs and updates release notes
|
||||
5. **Release Automation** - Packages everything into GitHub releases
|
||||
**Automatic PDF Generation:** - Builds both light and dark mode PDFs from MkDocs source
|
||||
**SHA256 Hash Generation:** - Creates hash files for integrity verification
|
||||
**GPG Signature Signing:** - Signs all PDFs and hash files with repository GPG key
|
||||
**VirusTotal Scanning:** - Automatically scans PDFs and updates release notes
|
||||
**Release Automation:** - Packages everything into GitHub releases
|
||||
|
||||
## Workflow Architecture
|
||||
|
||||
### 1. Build PDF Workflow (`build-pdf.yml`)
|
||||
|
||||
**Trigger:** Push to main, pull requests, or manual dispatch
|
||||
### Build PDF Workflow (`build-sign-release.yml`)
|
||||
|
||||
??? Note "Steps"
|
||||
|
||||
- Checkout repository
|
||||
- Set up Python 3.13 and MkDocs Material
|
||||
- Set up Python and MkDocs Material
|
||||
- Install Chromium browser
|
||||
- Generate both light and dark mode PDFs
|
||||
- Create SHA256 hash files
|
||||
- Sign all files with GPG
|
||||
- Upload artifacts to GitHub Actions
|
||||
- Publish release
|
||||
- Generate both light and dark mode PDFs with `scripts\build_guide_pdf.py`
|
||||
- Create SHA256 and blake2 hash files in `export/`
|
||||
- Sign all files with GPG in `export/`
|
||||
- Upload artifacts to GitHub Actions **manually**
|
||||
|
||||
### 2. VirusTotal Scan Workflow (`vt-scan.yml`)
|
||||
### SHA256 Hash Verification
|
||||
|
||||
**Trigger:** Push to main, tags, or manual dispatch (runs after build-pdf)
|
||||
!!! Note "**How it works**"
|
||||
|
||||
??? Note "Steps"
|
||||
|
||||
- Download PDF artifacts from build workflow
|
||||
- Scan both PDFs with VirusTotal API
|
||||
- Extract scan results and generate report links
|
||||
- Update release notes with VT scan status and URLs
|
||||
|
||||
## File Structure
|
||||
|
||||
After a successful build, the repository will contain:
|
||||
|
||||
```
|
||||
.../
|
||||
├── export/
|
||||
│ ├── thgtoa.pdf # Light mode PDF
|
||||
│ ├── thgtoa-dark.pdf # Dark mode PDF
|
||||
│ ├── thgtoa.pdf.sig # GPG signature (light)
|
||||
│ └── thgtoa-dark.pdf.sig # GPG signature (dark)
|
||||
├── thgtoa.pdf.sha256 # Hash file (light)
|
||||
├── thgtoa-dark.pdf.sha256 # Hash file (dark)
|
||||
├── sha256sum-light.txt # Combined hash file
|
||||
└── scripts/
|
||||
├── build_guide_pdf.py # PDF generation script
|
||||
└── verify_pdf.py # Verification utility
|
||||
```
|
||||
|
||||
## Security Features
|
||||
|
||||
### 1. SHA256 Hash Verification
|
||||
|
||||
**Purpose:** Ensure file integrity during download/transit
|
||||
|
||||
**How it works:**
|
||||
- Each PDF gets a unique SHA256 hash calculated at build time
|
||||
- Hash stored in `.sha256` files alongside the PDFs
|
||||
- Combined `sha256sum-light.txt` for batch verification
|
||||
- Combined `sha256sum.txt` for batch verification
|
||||
|
||||
**Verification command:**
|
||||
```bash
|
||||
sha256sum -c sha256sum-light.txt
|
||||
```
|
||||
|
||||
### 2. GPG Signature Verification
|
||||
### GPG Signature Verification
|
||||
|
||||
**Purpose:** Verify authenticity and prevent tampering
|
||||
|
||||
@@ -90,104 +45,6 @@ gpg --import pgp/anonymousplanet-master.asc
|
||||
gpg --verify export/thgtoa.pdf.sig export/thgtoa.pdf
|
||||
```
|
||||
|
||||
### 3. VirusTotal Integration
|
||||
|
||||
**Purpose:** Malware detection and security scanning
|
||||
|
||||
??? Note "How it works"
|
||||
|
||||
- Automatic scan of all generated PDFs
|
||||
- Results published in release notes with direct links
|
||||
- Provides third-party validation of file safety
|
||||
|
||||
## Usage Examples
|
||||
|
||||
### Local Development
|
||||
|
||||
```bash
|
||||
# Build PDFs locally
|
||||
python scripts/build_guide_pdf.py --both
|
||||
|
||||
# Verify hashes
|
||||
python scripts/verify_pdf.py --hashes
|
||||
|
||||
# Verify signatures (requires GPG installed)
|
||||
python scripts/verify_pdf.py --signatures
|
||||
|
||||
# Full verification with VirusTotal check
|
||||
export VT_API_KEY=your_api_key
|
||||
python scripts/verify_pdf.py --all
|
||||
```
|
||||
|
||||
### CI/CD Verification
|
||||
|
||||
The workflows automatically verify everything during the build process. To manually trigger:
|
||||
|
||||
1. Go to Actions tab
|
||||
2. Select "Build guide PDF" or "VirusTotal Scan"
|
||||
3. Click "Run workflow"
|
||||
4. Download artifacts from successful run
|
||||
|
||||
## Release Process
|
||||
|
||||
When you create a tag (e.g., `v1.0.0`):
|
||||
|
||||
1. Push the tag: `git push origin v1.0.0`
|
||||
2. Build PDF workflow triggers automatically
|
||||
3. VirusTotal scan workflow runs after build completes
|
||||
4. Both workflows update/create GitHub release with:
|
||||
- Light and dark mode PDFs
|
||||
- GPG signatures for all files
|
||||
- Hash files for verification
|
||||
- Release notes with VT scan results
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Common Issues
|
||||
|
||||
**GPG signing fails:**
|
||||
- Check that `GPG_PRIVATE_KEY` is in ASCII armor format
|
||||
- Verify passphrase is correct
|
||||
- Ensure key has signing capability
|
||||
|
||||
**Hash mismatch after download:**
|
||||
- Re-download the file (corruption during transfer)
|
||||
- Verify you're using the correct hash file
|
||||
- Check disk integrity
|
||||
|
||||
**VirusTotal scan fails:**
|
||||
- Verify `VT_API_KEY` is set correctly
|
||||
- Check API quota limits (free tier: 4 requests/minute)
|
||||
- Ensure PDF files exist before scanning
|
||||
|
||||
### Debug Mode
|
||||
|
||||
Enable verbose output by adding to workflow:
|
||||
```yaml
|
||||
- name: Debug
|
||||
run: |
|
||||
echo "Current directory:" && pwd
|
||||
echo "Files in export:" && ls -la export/
|
||||
echo "Hash file contents:" && cat sha256sum-light.txt
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Always verify signatures** before opening PDFs from untrusted sources
|
||||
2. **Check VirusTotal results** for any suspicious detections
|
||||
3. **Keep GPG keys secure** - never commit private keys to repository
|
||||
4. **Monitor API usage** for VirusTotal to avoid rate limiting
|
||||
5. **Test locally** before pushing tags to production
|
||||
|
||||
## Future Enhancements
|
||||
|
||||
Potential improvements:
|
||||
- Multi-signature support (multiple maintainers)
|
||||
- Automated changelog generation with hashes
|
||||
- Cross-platform signature verification scripts
|
||||
- Integration with additional malware scanners
|
||||
- Automatic mirror updates with verified files
|
||||
|
||||
---
|
||||
|
||||
*This workflow is designed for security-conscious users who need to verify the authenticity and integrity of downloaded documents.*
|
||||
|
||||
+1
-2
@@ -18,13 +18,12 @@ schema:
|
||||
|
||||
**9FA5 436D 0EE3 6098 5157 3825 17EC A05F 768D EDF6**
|
||||
|
||||
This is the master signing key fingerprint for Anonymous Planet.
|
||||
You'll use it to [**verify the checksum** and **GPG signature** of all files for authenticity.](verify/index.md)
|
||||
Please share this project if you enjoy it and you think it might be useful to others.
|
||||
|
||||
{ align=right }
|
||||
|
||||
Anonymous Planet is a collective of volunteers and contributors. No one person is considered more valuable than another, and no one person should be viewed as having "more impact" on Anonymous Planet.
|
||||
Anonymous Planet is a collective of volunteers.
|
||||
|
||||
??? person "Das Kolburn"
|
||||
|
||||
|
||||
@@ -62,9 +62,12 @@ gpg --verify export/thgtoa-dark.pdf.sig export/thgtoa-dark.pdf
|
||||
|
||||
Expected output for successful verification:
|
||||
```
|
||||
gpg: Signature made [date]
|
||||
gpg: using RSA key [key-id]
|
||||
gpg: Good signature from "[owner]"
|
||||
gpg: Signature made Mon 20 Apr 2026 01:46:40 AM EDT
|
||||
gpg: using EDDSA key 9FA5436D0EE360985157382517ECA05F768DEDF6
|
||||
gpg: Good signature from "Anonymous Planet Master Signing Key" [unknown]
|
||||
gpg: WARNING: This key is not certified with a trusted signature!
|
||||
gpg: There is no indication that the signature belongs to the owner.
|
||||
Primary key fingerprint: 9FA5 436D 0EE3 6098 5157 3825 17EC A05F 768D EDF6
|
||||
```
|
||||
|
||||
#### 3. Check VirusTotal Status
|
||||
@@ -114,7 +117,7 @@ The GitHub Actions workflows automatically:
|
||||
|
||||
## Key Information
|
||||
|
||||
**Signing Key:** Anonymous Planet Master Key
|
||||
**Signing Key:** Anonymous Planet Master Signing Key ("MSK")
|
||||
**Key ID:** See `pgp/anonymousplanet-master.asc` for details
|
||||
**Fingerprint:** Verify from the repository's official documentation
|
||||
|
||||
|
||||
Binary file not shown.
@@ -0,0 +1,8 @@
|
||||
-----BEGIN PGP SIGNATURE-----
|
||||
|
||||
iJEEABYKADkWIQSfpUNtDuNgmFFXOCUX7KBfdo3t9gUCaeXaqxsUgAAAAAAEAA5t
|
||||
YW51MiwyLjUrMS4xMiwyLDIACgkQF+ygX3aN7fY6QAD/YCGJqs9HiRllFrF9EluE
|
||||
Ga4XUEQ/R6Q2zc+X6lX856sBAJIpxeMxUmMUXyr3xBAHxUf5eV+nQYkQQMKI81L1
|
||||
x8gL
|
||||
=VX6l
|
||||
-----END PGP SIGNATURE-----
|
||||
@@ -0,0 +1 @@
|
||||
f212d0425b38d5cd10da6dc804b60f143da23d4b07051aae31d0966082519b300af0e1c423683e0223738b33b138c687232b1c8bd68cf643777bbc5b588152bd ./export/thgtoa-dark.pdf
|
||||
@@ -0,0 +1,8 @@
|
||||
-----BEGIN PGP SIGNATURE-----
|
||||
|
||||
iJEEABYKADkWIQSfpUNtDuNgmFFXOCUX7KBfdo3t9gUCaeXaqxsUgAAAAAAEAA5t
|
||||
YW51MiwyLjUrMS4xMiwyLDIACgkQF+ygX3aN7fbdDgEAoSslLR47ydW/3r1wJOPY
|
||||
X/waLkVbkGZpHqwd4RjywwcA/3B7Ci+jUg+yP5TRsuChagEhwyO5vw2DxSlUGoB4
|
||||
+ksH
|
||||
=2ja9
|
||||
-----END PGP SIGNATURE-----
|
||||
@@ -0,0 +1,8 @@
|
||||
-----BEGIN PGP SIGNATURE-----
|
||||
|
||||
iJEEABYKADkWIQSfpUNtDuNgmFFXOCUX7KBfdo3t9gUCaeXaqxsUgAAAAAAEAA5t
|
||||
YW51MiwyLjUrMS4xMiwyLDIACgkQF+ygX3aN7faErgD/Svj1G+B7gmrZQ6AsLZ5J
|
||||
HfeldxjmrXE99dig1iHtl5IBAMndZZb+95TO03IZ9eLGfYuyTz4GCUanmftsY9yv
|
||||
LAIN
|
||||
=MEd0
|
||||
-----END PGP SIGNATURE-----
|
||||
Binary file not shown.
@@ -0,0 +1,8 @@
|
||||
-----BEGIN PGP SIGNATURE-----
|
||||
|
||||
iJEEABYKADkWIQSfpUNtDuNgmFFXOCUX7KBfdo3t9gUCaeXaqxsUgAAAAAAEAA5t
|
||||
YW51MiwyLjUrMS4xMiwyLDIACgkQF+ygX3aN7favvgEAvFFSB5NrsrKMYvGG5ZYB
|
||||
iLIyt8Sn1rZmlVkibssMPq0BAImpZe8S7hWNkbukyEC4sLbKiOYvjbVipQHnrIUV
|
||||
xPMH
|
||||
=0hnj
|
||||
-----END PGP SIGNATURE-----
|
||||
@@ -0,0 +1 @@
|
||||
436ed0df78c299f95b8d5ff94f43f26ec2e7825d92d843fc15419630d55ed5e0c98485e738c12715a2b6242633faae38e8a98935b361d44ddde97a1692cb01a1 ./export/thgtoa.pdf
|
||||
@@ -0,0 +1,8 @@
|
||||
-----BEGIN PGP SIGNATURE-----
|
||||
|
||||
iJEEABYKADkWIQSfpUNtDuNgmFFXOCUX7KBfdo3t9gUCaeXaqxsUgAAAAAAEAA5t
|
||||
YW51MiwyLjUrMS4xMiwyLDIACgkQF+ygX3aN7fatsgEAixDzH+zTnKYMEx3sikWp
|
||||
dsNTiHTU6wJY/brVJIU879UBAJntBIq72vqwKtMb/ZlVvomdDvKVllZw8ZsYBz1n
|
||||
aTkM
|
||||
=vkgy
|
||||
-----END PGP SIGNATURE-----
|
||||
@@ -0,0 +1,8 @@
|
||||
-----BEGIN PGP SIGNATURE-----
|
||||
|
||||
iJEEABYKADkWIQSfpUNtDuNgmFFXOCUX7KBfdo3t9gUCaeXaqxsUgAAAAAAEAA5t
|
||||
YW51MiwyLjUrMS4xMiwyLDIACgkQF+ygX3aN7faAGQEAyEhVKrRoXIsV3E5f1FZg
|
||||
8fcsmbxCnKBqxichCkf0dWYBAIvbI146mQLHaNqLDaTIqCUQbkq1aE/YMFDGykUG
|
||||
ngsJ
|
||||
=/0RY
|
||||
-----END PGP SIGNATURE-----
|
||||
@@ -0,0 +1,16 @@
|
||||
## VirusTotal Scan Results
|
||||
|
||||
**Scan Date:** 2026-04-19 01:48 UTC
|
||||
|
||||
---
|
||||
|
||||
### thgtoa.pdf
|
||||
- **SHA256 Hash:** `f82f6f53319315568fc2524b4eaf01126fe52356a20363cd358ad5977388ba28`
|
||||
- **VirusTotal Report:** VT_API_KEY not configured, scan skipped
|
||||
|
||||
### thgtoa-dark.pdf
|
||||
- **SHA256 Hash:** `94a0c8e3b81b0aeeb921029a41713d81b836da893a9bc9f905ca7296e82bd70f`
|
||||
- **VirusTotal Report:** VT_API_KEY not configured, scan skipped
|
||||
|
||||
---
|
||||
*Scan performed automatically by GitHub Actions*
|
||||
@@ -0,0 +1,8 @@
|
||||
-----BEGIN PGP SIGNATURE-----
|
||||
|
||||
iJEEABYKADkWIQSfpUNtDuNgmFFXOCUX7KBfdo3t9gUCaeXaqxsUgAAAAAAEAA5t
|
||||
YW51MiwyLjUrMS4xMiwyLDIACgkQF+ygX3aN7fYpCgEA209U3QewChp7mdrrFjH1
|
||||
CaBMIk2sCHwRMCcmbMDkNTAA/RIchAKex13ZjZWC9xsJpZEktvBENFsQLsNPReqR
|
||||
UZ8C
|
||||
=TYsa
|
||||
-----END PGP SIGNATURE-----
|
||||
@@ -133,6 +133,7 @@ nav:
|
||||
- Constitution: constitution/index.md
|
||||
- Mirrors: mirrors/index.md
|
||||
- Twitter: twitter/index.md
|
||||
- TBA: changelog/index.md
|
||||
|
||||
copyright: |
|
||||
© 2023-2026 <a href="https://anonymousplanet.org/" target="_blank" rel="noopener">Anonymous Planet</a>
|
||||
|
||||
@@ -0,0 +1,132 @@
|
||||
# PDF Build, Scan & Release Scripts
|
||||
|
||||
This directory contains scripts for building PDFs from MkDocs documentation, scanning them with VirusTotal, generating hashes, and uploading artifacts to GitHub releases.
|
||||
|
||||
## Scripts
|
||||
|
||||
### `pdf_release.sh` (v2 - Recommended)
|
||||
The main script that handles:
|
||||
- SHA256 hash generation for PDF files
|
||||
- VirusTotal scanning of PDFs
|
||||
- Release creation/update on GitHub
|
||||
- GPG signature verification support
|
||||
|
||||
**Usage:**
|
||||
```bash
|
||||
./scripts/pdf_release.sh --build <light|dark|both> --release <tag|latest> [--vt-api-key VT_KEY] [--github-token TOKEN]
|
||||
```
|
||||
|
||||
**Options:**
|
||||
- `--build`: PDF build mode (`light`, `dark`, or `both`) - Required
|
||||
- `--release`: Release update mode (`tag` for tagged releases, `latest` to always update) - Default: `tag`
|
||||
- `--vt-api-key`: VirusTotal API key (optional)
|
||||
- `--github-token`: GitHub token for release operations (optional)
|
||||
|
||||
### `build_guide_pdf.py`
|
||||
Python script that builds MkDocs documentation and converts it to PDF using Chromium/Chrome.
|
||||
|
||||
**Usage:**
|
||||
```bash
|
||||
python scripts/build_guide_pdf.py --both # Build both light and dark mode
|
||||
python scripts/build_guide_pdf.py --dark-mode # Dark mode only
|
||||
python scripts/build_guide_pdf.py --skip-mkdocs # Skip MkDocs build, use existing site
|
||||
```
|
||||
|
||||
## GitHub Actions Workflow
|
||||
|
||||
The workflow `.github/workflows/build-pdf-combined.yml` combines all operations:
|
||||
|
||||
1. **Build PDFs** - Generates light/dark mode PDFs with GPG signatures
|
||||
2. **Scan & Release** - Scans with VirusTotal and updates/releases artifacts
|
||||
|
||||
### Required Secrets
|
||||
|
||||
Add these to your repository settings under **Settings > Secrets and variables > Actions**:
|
||||
|
||||
- `GPG_PRIVATE_KEY`: Your GPG private key for signing
|
||||
- `GPG_PASSPHRASE`: Passphrase for the GPG key (if any)
|
||||
- `VT_API_KEY`: VirusTotal API key for malware scanning
|
||||
- `GITHUB_TOKEN`: Automatically available, but can be manually added
|
||||
|
||||
### Workflow Triggers
|
||||
|
||||
The workflow runs on:
|
||||
- Manual dispatch (`workflow_dispatch`) with customizable options
|
||||
- Push to main branch when docs, mkdocs.yml, or scripts change
|
||||
|
||||
## Output Files
|
||||
|
||||
After running the build and release process, you'll get:
|
||||
|
||||
```
|
||||
export/
|
||||
├── thgtoa.pdf # Light mode PDF
|
||||
├── thgtoa-dark.pdf # Dark mode PDF
|
||||
├── thgtoa.pdf.sig # GPG signature for light PDF
|
||||
├── thgtoa-dark.pdf.sig # GPG signature for dark PDF
|
||||
├── thgtoa.pdf.sha256 # SHA256 hash for light PDF
|
||||
├── thgtoa-dark.pdf.sha256 # SHA256 hash for dark PDF
|
||||
├── sha256sum-combined.txt # Combined hash file
|
||||
├── sha256sum-combined.txt.sig # GPG signature for combined hashes
|
||||
└── virus-total-results.md # VirusTotal scan results
|
||||
```
|
||||
|
||||
## Hash Verification
|
||||
|
||||
To verify the integrity of downloaded PDFs:
|
||||
|
||||
```bash
|
||||
# Verify against individual hash file
|
||||
sha256sum -c thgtoa.pdf.sha256
|
||||
|
||||
# Or verify against combined hash file
|
||||
sha256sum -c sha256sum-combined.txt
|
||||
```
|
||||
|
||||
## VirusTotal Integration
|
||||
|
||||
When a `VT_API_KEY` is provided, the script will:
|
||||
1. Upload each PDF to VirusTotal's API
|
||||
2. Generate individual scan reports
|
||||
3. Include VT report links in release notes and artifacts
|
||||
|
||||
The VT results file (`virus-total-results.md`) contains:
|
||||
- Scan timestamp
|
||||
- SHA256 hashes for each PDF
|
||||
- Direct links to VirusTotal GUI reports
|
||||
|
||||
## Release Management
|
||||
|
||||
The script supports two release modes:
|
||||
|
||||
1. **Tag mode** (`--release tag`): Updates the release matching the current git tag
|
||||
2. **Latest mode** (`--release latest`): Always updates the most recent release (useful for continuous deployment)
|
||||
|
||||
When running in a GitHub Actions workflow with a tag push, it will automatically create or update the corresponding release.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### PDF Build Fails
|
||||
- Ensure Chrome/Chromium is installed: `sudo apt install chromium-browser`
|
||||
- Check MkDocs configuration is valid: `mkdocs build --strict`
|
||||
- Verify all documentation files are present and properly formatted
|
||||
|
||||
### VirusTotal Scan Fails
|
||||
- Check VT_API_KEY secret is correctly set in repository settings
|
||||
- Verify the API key has sufficient quota (free tier allows 4 requests/minute)
|
||||
- Check network connectivity to VirusTotal API
|
||||
|
||||
### Release Upload Fails
|
||||
- Ensure GITHUB_TOKEN has appropriate permissions (repo scope)
|
||||
- For existing releases, use `--release latest` instead of `tag`
|
||||
- Check that the release tag format matches GitHub's requirements (e.g., `v1.0.0`)
|
||||
|
||||
## Security Notes
|
||||
|
||||
- **GPG Keys**: Never commit private keys to version control. Use GitHub Secrets.
|
||||
- **VT API Key**: Keep your VirusTotal API key secret and rotate periodically.
|
||||
- **Release Artifacts**: All uploaded artifacts are publicly visible on your releases page.
|
||||
|
||||
## License
|
||||
|
||||
These scripts are part of the "The How-To Guide To Anonymity" project and follow the same licensing as the main repository.
|
||||
@@ -177,13 +177,13 @@ def main() -> int:
|
||||
"--pdf-light",
|
||||
type=Path,
|
||||
default=root / "export" / "thgtoa.pdf",
|
||||
help="Output PDF path for light mode (default: ./export/guide.pdf)",
|
||||
help="Output PDF path for light mode (default: ./export/thgtoa.pdf)",
|
||||
)
|
||||
ap.add_argument(
|
||||
"--pdf-dark",
|
||||
type=Path,
|
||||
default=root / "export" / "thgtoa-dark.pdf",
|
||||
help="Output PDF path for dark mode (default: ./export/guide-dark.pdf)",
|
||||
help="Output PDF path for dark mode (default: ./export/thgtoa-dark.pdf)",
|
||||
)
|
||||
ap.add_argument("--skip-mkdocs", action="store_true", help="Reuse existing site dir; only run print-to-pdf.")
|
||||
ap.add_argument("--dark-mode", action="store_true", help="Generate dark mode PDF only")
|
||||
|
||||
@@ -1,365 +0,0 @@
|
||||
!/bin/bash
|
||||
set -e
|
||||
|
||||
# PDF Hashing, Scanning, Release and Management script (hSCRAM)
|
||||
# Usage: ./pdf_release.sh --build <light|dark|both> --release <tag|latest> [--vt-api-key]
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
ROOT_DIR="$(dirname "$SCRIPT_DIR")"
|
||||
EXPORT_DIR="$ROOT_DIR/export"
|
||||
|
||||
# Default values
|
||||
MODE="both" # light, dark, or both
|
||||
RELEASE_MODE="tag" # tag (e.g. "v2.1.2") or "latest"
|
||||
VT_API_KEY=""
|
||||
GITHUB_TOKEN=""
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case $1 in
|
||||
--build)
|
||||
MODE="$2"
|
||||
shift 2
|
||||
;;
|
||||
--release)
|
||||
RELEASE_MODE="$2"
|
||||
shift 2
|
||||
;;
|
||||
--vt-api-key)
|
||||
VT_API_KEY="$2"
|
||||
shift 2
|
||||
;;
|
||||
--github-token)
|
||||
GITHUB_TOKEN="$2"
|
||||
shift 2
|
||||
;;
|
||||
*)
|
||||
echo "Unknown option: $1"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [[ ! "$MODE" =~ ^(light|dark|both)$ ]]; then
|
||||
echo "Error: Invalid build mode '$MODE'. Must be 'light', 'dark', or 'both'."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Hashing, Scanning, Release and Management script (hSCRAM)"
|
||||
echo "Mode: $MODE"
|
||||
echo "Release Mode: $RELEASE_MODE"
|
||||
|
||||
generate_hash() {
|
||||
local file="$1"
|
||||
sha256sum "$file" | cut -d' ' -f1
|
||||
}
|
||||
|
||||
create_hash_file() {
|
||||
local pdf_path="$1"
|
||||
local base_name=$(basename "$pdf_path")
|
||||
local hash_file="${EXPORT_DIR}/${base_name}.sha256"
|
||||
|
||||
(cd "$EXPORT_DIR" && sha256sum "$base_name") > "$hash_file"
|
||||
echo "Created: $hash_file"
|
||||
}
|
||||
|
||||
scan_with_virustotal() {
|
||||
local pdf_path="$1"
|
||||
local base_name=$(basename "$pdf_path")
|
||||
|
||||
if [[ -z "$VT_API_KEY" ]]; then
|
||||
echo "Warning: VT_API_KEY not provided, skipping VirusTotal scan for $base_name"
|
||||
return 1
|
||||
fi
|
||||
|
||||
echo "Scanning $base_name with VirusTotal..."
|
||||
|
||||
local upload_response=$(curl -s -X POST \
|
||||
-H "x-apikey: $VT_API_KEY" \
|
||||
-F "file=@$pdf_path" \
|
||||
https://www.virustotal.com/api/v3/files)
|
||||
|
||||
if [[ $? -ne 0 ]]; then
|
||||
echo "Error uploading $base_name to VirusTotal"
|
||||
return 1
|
||||
fi
|
||||
|
||||
local file_id=$(echo "$upload_response" | python3 -c "import sys, json; print(json.load(sys.stdin)['data']['id'])" 2>/dev/null || echo "")
|
||||
|
||||
if [[ -z "$file_id" ]]; then
|
||||
echo "Error: Could not extract file ID from VirusTotal response for $base_name"
|
||||
echo "Response: $upload_response"
|
||||
return 1
|
||||
fi
|
||||
|
||||
local vt_url="https://www.virustotal.com/gui/file/$file_id"
|
||||
echo "$vt_url"
|
||||
}
|
||||
|
||||
scan_all_pdfs() {
|
||||
local results_file="$EXPORT_DIR/virus-total-results.md"
|
||||
|
||||
cat > "$results_file" << 'HEADER'
|
||||
## VirusTotal Scan Results
|
||||
|
||||
**Scan Date:** TIMESTAMP
|
||||
|
||||
---
|
||||
HEADER
|
||||
|
||||
sed -i "s/TIMESTAMP/$(date -u +"%Y-%m-%d %H:%M UTC")/" "$results_file"
|
||||
|
||||
local pdf_files=()
|
||||
if [[ "$MODE" == "light" || "$MODE" == "both" ]]; then
|
||||
pdf_files+=("$EXPORT_DIR/thgtoa.pdf")
|
||||
fi
|
||||
if [[ "$MODE" == "dark" || "$MODE" == "both" ]]; then
|
||||
pdf_files+=("$EXPORT_DIR/thgtoa-dark.pdf")
|
||||
fi
|
||||
|
||||
for pdf in "${pdf_files[@]}"; do
|
||||
if [[ -f "$pdf" ]]; then
|
||||
local base_name=$(basename "$pdf")
|
||||
local hash=$(generate_hash "$pdf")
|
||||
|
||||
echo "" >> "$results_file"
|
||||
echo "### $base_name" >> "$results_file"
|
||||
echo "- **SHA256 Hash:** \`$hash\`" >> "$results_file"
|
||||
|
||||
if [[ -n "$VT_API_KEY" ]]; then
|
||||
local vt_url=$(scan_with_virustotal "$pdf")
|
||||
if [[ $? -eq 0 && -n "$vt_url" ]]; then
|
||||
echo "- **VirusTotal Report:** [$vt_url]($vt_url)" >> "$results_file"
|
||||
else
|
||||
echo "- **VirusTotal Report:** Scan failed or API key not provided" >> "$results_file"
|
||||
fi
|
||||
else
|
||||
echo "- **VirusTotal Report:** VT_API_KEY not configured, scan skipped" >> "$results_file"
|
||||
fi
|
||||
|
||||
create_hash_file "$pdf"
|
||||
else
|
||||
echo "Warning: $pdf does not exist, skipping..."
|
||||
fi
|
||||
done
|
||||
|
||||
cat >> "$results_file" << 'FOOTER'
|
||||
|
||||
---
|
||||
*Scan performed automatically by GitHub Actions*
|
||||
FOOTER
|
||||
|
||||
echo "VirusTotal results saved to: $results_file"
|
||||
}
|
||||
|
||||
update_release() {
|
||||
local tag="${1:-}"
|
||||
local release_notes="$EXPORT_DIR/release-notes.md"
|
||||
|
||||
if [[ "$RELEASE_MODE" == "tag" && -z "$tag" ]]; then
|
||||
tag=$(git describe --tags --abbrev=0 2>/dev/null || echo "")
|
||||
fi
|
||||
|
||||
if [[ -z "$tag" ]]; then
|
||||
echo "Warning: No release tag found, skipping release update."
|
||||
return 1
|
||||
fi
|
||||
|
||||
echo "Updating release for tag: $tag"
|
||||
|
||||
cat > "$release_notes" << EOF
|
||||
# Release Notes - $tag
|
||||
|
||||
**Release Date:** $(date -u +"%Y-%m-%d %H:%M UTC")
|
||||
|
||||
## PDF Files
|
||||
|
||||
EOF
|
||||
|
||||
if [[ "$MODE" == "light" || "$MODE" == "both" ]]; then
|
||||
local hash=$(generate_hash "$EXPORT_DIR/thgtoa.pdf")
|
||||
echo "- **thgtoa.pdf (Light Mode)**" >> "$release_notes"
|
||||
echo " - SHA256: \`$hash\`" >> "$release_notes"
|
||||
if [[ -f "$EXPORT_DIR/thgtoa.pdf.sig" ]]; then
|
||||
echo " - Signature: \`thgtoa.pdf.sig\` (GPG signed)" >> "$release_notes"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ "$MODE" == "dark" || "$MODE" == "both" ]]; then
|
||||
local hash=$(generate_hash "$EXPORT_DIR/thgtoa-dark.pdf")
|
||||
echo "- **thgtoa-dark.pdf (Dark Mode)**" >> "$release_notes"
|
||||
echo " - SHA256: \`$hash\`" >> "$release_notes"
|
||||
if [[ -f "$EXPORT_DIR/thgtoa-dark.pdf.sig" ]]; then
|
||||
echo " - Signature: \`thgtoa-dark.pdf.sig\` (GPG signed)" >> "$release_notes"
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "" >> "$release_notes"
|
||||
echo "---" >> "$release_notes"
|
||||
|
||||
if [[ -f "$EXPORT_DIR/virus-total-results.md" ]]; then
|
||||
echo "## VirusTotal Scan Results" >> "$release_notes"
|
||||
echo "" >> "$release_notes"
|
||||
cat "$EXPORT_DIR/virus-total-results.md" >> "$release_notes"
|
||||
echo "" >> "$release_notes"
|
||||
fi
|
||||
|
||||
local files_to_upload=""
|
||||
|
||||
if [[ -f "$EXPORT_DIR/thgtoa.pdf" ]]; then
|
||||
files_to_upload+="$EXPORT_DIR/thgtoa.pdf "
|
||||
fi
|
||||
if [[ -f "$EXPORT_DIR/thgtoa-dark.pdf" ]]; then
|
||||
files_to_upload+="$EXPORT_DIR/thgtoa-dark.pdf "
|
||||
fi
|
||||
if [[ -f "$EXPORT_DIR/thgtoa.pdf.sig" ]]; then
|
||||
files_to_upload+="$EXPORT_DIR/thgtoa.pdf.sig "
|
||||
fi
|
||||
if [[ -f "$EXPORT_DIR/thgtoa-dark.pdf.sig" ]]; then
|
||||
files_to_upload+="$EXPORT_DIR/thgtoa-dark.pdf.sig "
|
||||
fi
|
||||
|
||||
local combined_hash_file="$EXPORT_DIR/sha256sum-combined.txt"
|
||||
|
||||
if [[ -f "$EXPORT_DIR/thgtoa.pdf.sha256" ]]; then
|
||||
cat "$EXPORT_DIR/thgtoa.pdf.sha256" >> "$combined_hash_file" 2>/dev/null || true
|
||||
fi
|
||||
if [[ -f "$EXPORT_DIR/thgtoa-dark.pdf.sha256" ]]; then
|
||||
echo "" >> "$combined_hash_file"
|
||||
cat "$EXPORT_DIR/thgtoa-dark.pdf.sha256" >> "$combined_hash_file"
|
||||
fi
|
||||
|
||||
files_to_upload+="$combined_hash_file "
|
||||
|
||||
if [[ -n "${GPG_PRIVATE_KEY:-}" && -n "${GPG_PASSPHRASE:-}" ]]; then
|
||||
echo "$GPG_PRIVATE_KEY" | gpg --batch --import 2>/dev/null || true
|
||||
gpg --batch --yes --armor --detach-sign --output "$combined_hash_file.sig" "$combined_hash_file" 2>/dev/null || true
|
||||
if [[ -f "$combined_hash_file.sig" ]]; then
|
||||
files_to_upload+="$combined_hash_file.sig "
|
||||
fi
|
||||
fi
|
||||
|
||||
if command -v gh &> /dev/null && [[ -n "$GITHUB_TOKEN" ]]; then
|
||||
echo "Uploading release with GitHub CLI..."
|
||||
|
||||
local release_exists=$(gh release view "$tag" 2>/dev/null && echo "yes" || echo "no")
|
||||
|
||||
if [[ "$release_exists" == "yes" ]]; then
|
||||
gh release edit "$tag" --notes-file "$release_notes" 2>/dev/null || {
|
||||
echo "Warning: Failed to update release notes"
|
||||
}
|
||||
|
||||
for file in $files_to_upload; do
|
||||
if [[ -f "$file" ]]; then
|
||||
local file_name=$(basename "$file")
|
||||
gh release upload "$tag" "$file" 2>/dev/null || {
|
||||
echo "Warning: Failed to upload $file_name"
|
||||
}
|
||||
fi
|
||||
done
|
||||
else
|
||||
gh release create "$tag" \
|
||||
--title "Release $tag" \
|
||||
--notes-file "$release_notes" \
|
||||
$files_to_upload 2>/dev/null || {
|
||||
echo "Error: Failed to create release"
|
||||
return 1
|
||||
}
|
||||
fi
|
||||
|
||||
else
|
||||
if [[ -n "$GITHUB_TOKEN" && -n "$tag" ]]; then
|
||||
echo "Using GitHub API to upload release..."
|
||||
|
||||
local repo="${GITHUB_REPOSITORY:-}"
|
||||
local api_url="https://api.github.com/repos/$repo/releases"
|
||||
|
||||
local existing_release=$(curl -s -H "Authorization: token $GITHUB_TOKEN" \
|
||||
"$api_url/tags/$tag")
|
||||
|
||||
if [[ $(echo "$existing_release" | grep -c '"id":') -gt 0 ]]; then
|
||||
echo "Release already exists, updating..."
|
||||
local release_id=$(echo "$existing_release" | python3 -c "import sys, json; print(json.load(sys.stdin)['id'])" 2>/dev/null || echo "")
|
||||
|
||||
curl -X PATCH \
|
||||
-H "Authorization: token $GITHUB_TOKEN" \
|
||||
-H "Accept: application/vnd.github.v3+json" \
|
||||
"$api_url/$release_id" \
|
||||
-d "{\"body\":\"$(cat "$release_notes")\"}" 2>/dev/null || true
|
||||
|
||||
for file in $files_to_upload; do
|
||||
if [[ -f "$file" ]]; then
|
||||
local file_name=$(basename "$file")
|
||||
local mime_type=$(file --mime-type -b "$file")
|
||||
|
||||
curl -X POST \
|
||||
-H "Authorization: token $GITHUB_TOKEN" \
|
||||
-H "Content-Type: $mime_type" \
|
||||
--data-binary @"$file" \
|
||||
"https://uploads.github.com/repos/$repo/releases/$release_id/assets?name=$file_name" 2>/dev/null || {
|
||||
echo "Warning: Failed to upload $file_name"
|
||||
}
|
||||
fi
|
||||
done
|
||||
else
|
||||
echo "Creating new release..."
|
||||
|
||||
local create_response=$(curl -s -X POST \
|
||||
-H "Authorization: token $GITHUB_TOKEN" \
|
||||
-H "Accept: application/vnd.github.v3+json" \
|
||||
"$api_url" \
|
||||
-d "{\"tag_name\":\"$tag\",\"name\":\"Release $tag\",\"body\":\"$(cat "$release_notes")\"}")
|
||||
|
||||
local release_id=$(echo "$create_response" | python3 -c "import sys, json; print(json.load(sys.stdin)['id'])" 2>/dev/null || echo "")
|
||||
|
||||
for file in $files_to_upload; do
|
||||
if [[ -f "$file" ]]; then
|
||||
local file_name=$(basename "$file")
|
||||
local mime_type=$(file --mime-type -b "$file")
|
||||
|
||||
curl -X POST \
|
||||
-H "Authorization: token $GITHUB_TOKEN" \
|
||||
-H "Content-Type: $mime_type" \
|
||||
--data-binary @"$file" \
|
||||
"https://uploads.github.com/repos/$repo/releases/$release_id/assets?name=$file_name" 2>/dev/null || {
|
||||
echo "Warning: Failed to upload $file_name"
|
||||
}
|
||||
fi
|
||||
done
|
||||
fi
|
||||
else
|
||||
echo "Error: GITHUB_TOKEN required for release upload."
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "Release update complete!"
|
||||
}
|
||||
|
||||
echo ""
|
||||
echo "Step 1: Generating hashes..."
|
||||
if [[ "$MODE" == "light" || "$MODE" == "both" ]]; then
|
||||
if [[ -f "$EXPORT_DIR/thgtoa.pdf" ]]; then
|
||||
create_hash_file "$EXPORT_DIR/thgtoa.pdf"
|
||||
else
|
||||
echo "Warning: $EXPORT_DIR/thgtoa.pdf not found. Ensure PDF is built first."
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ "$MODE" == "dark" || "$MODE" == "both" ]]; then
|
||||
if [[ -f "$EXPORT_DIR/thgtoa-dark.pdf" ]]; then
|
||||
create_hash_file "$EXPORT_DIR/thgtoa-dark.pdf"
|
||||
else
|
||||
echo "Warning: $EXPORT_DIR/thgtoa-dark.pdf not found. Ensure PDF is built first."
|
||||
fi
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "Step 2: Scanning with VirusTotal..."
|
||||
scan_all_pdfs
|
||||
|
||||
echo ""
|
||||
echo "Step 3: Updating release..."
|
||||
update_release "$GITHUB_REF_NAME"
|
||||
|
||||
echo ""
|
||||
echo "PDF Release Script Complete!"
|
||||
@@ -169,44 +169,12 @@ GitHub repository:
|
||||
|
||||
3. VT_API_KEY (optional but recommended)
|
||||
- VirusTotal API key for malware scanning
|
||||
- Get a free key at: https://www.virustotal.com/gui/join-us
|
||||
|
||||
|
||||
HOW TO ADD SECRETS:
|
||||
|
||||
1. Go to your repository on GitHub
|
||||
2. Click 'Settings' → 'Secrets and variables' → 'Actions'
|
||||
3. Click 'New repository secret' for each secret below:
|
||||
|
||||
Secret Name | Value Format
|
||||
---------------------|--------------------------------------------------
|
||||
GPG_PRIVATE_KEY | Paste the entire ASCII armored key (BEGIN PGP...)
|
||||
GPG_PASSPHRASE | Your key's passphrase (no special characters issues)
|
||||
VT_API_KEY | Your VirusTotal API key
|
||||
|
||||
|
||||
VERIFYING YOUR SETUP:
|
||||
|
||||
After adding secrets, you can test by:
|
||||
1. Going to 'Actions' tab
|
||||
2. Selecting 'Build guide PDF' workflow
|
||||
3. Clicking 'Run workflow'
|
||||
4. Checking if the workflow completes successfully
|
||||
|
||||
|
||||
TROUBLESHOOTING:
|
||||
|
||||
- If GPG signing fails: Check that your key has signing capability ('s' flag)
|
||||
- If passphrase is wrong: Verify you're using the correct passphrase
|
||||
- If VT scan fails: Ensure API key is valid and within rate limits
|
||||
|
||||
|
||||
SECURITY NOTES:
|
||||
|
||||
⚠ NEVER share your private key or passphrase publicly
|
||||
⚠ Always use repository secrets, never hardcode in scripts
|
||||
⚠ Rotate keys periodically if compromised
|
||||
⚠ Use strong passphrases (12+ characters recommended)
|
||||
""")
|
||||
|
||||
|
||||
@@ -220,8 +188,6 @@ def main() -> int:
|
||||
print("⚠ WARNING: GPG is not installed or not in PATH")
|
||||
print("Please install GPG before continuing:")
|
||||
print(" - Linux: sudo apt install gnupg")
|
||||
print(" - macOS: brew install gnupg")
|
||||
print(" - Windows: https://www.gpg4win.org/")
|
||||
print("\nContinuing anyway...")
|
||||
|
||||
# List available keys
|
||||
@@ -313,7 +279,7 @@ To get your private key for the GPG_PRIVATE_KEY secret:
|
||||
print("1. Export your private key (see instructions above)")
|
||||
print("2. Add all three secrets to GitHub repository settings")
|
||||
print("3. Test the workflow by triggering a manual build")
|
||||
print("\nFor more information, see: docs/guide/pdf-workflow.md\n")
|
||||
print("\nFor more information, see: docs/guide/dev-workflow.md\n")
|
||||
|
||||
return 0
|
||||
|
||||
|
||||
@@ -26,11 +26,9 @@ import subprocess
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def repo_root() -> Path:
|
||||
return Path(__file__).resolve().parent.parent
|
||||
|
||||
|
||||
def calculate_sha256(file_path: Path) -> str:
|
||||
"""Calculate SHA256 hash of a file."""
|
||||
sha256_hash = hashlib.sha256()
|
||||
@@ -39,7 +37,6 @@ def calculate_sha256(file_path: Path) -> str:
|
||||
sha256_hash.update(byte_block)
|
||||
return sha256_hash.hexdigest()
|
||||
|
||||
|
||||
def verify_hash(file_path: Path, expected_hash: str) -> bool:
|
||||
"""Verify file hash against expected value."""
|
||||
actual_hash = calculate_sha256(file_path)
|
||||
@@ -50,7 +47,6 @@ def verify_hash(file_path: Path, expected_hash: str) -> bool:
|
||||
print(f" Actual: {actual_hash}")
|
||||
return is_valid
|
||||
|
||||
|
||||
def verify_signature(file_path: Path, sig_file: Path) -> bool:
|
||||
"""Verify GPG signature of a file."""
|
||||
if not sig_file.exists():
|
||||
@@ -81,7 +77,6 @@ def verify_signature(file_path: Path, sig_file: Path) -> bool:
|
||||
print("⚠ WARNING: GPG not installed. Skipping signature verification.")
|
||||
return None
|
||||
|
||||
|
||||
def verify_from_hash_file(file_path: Path, hash_file: Path) -> bool:
|
||||
"""Verify file hash from a hash file."""
|
||||
if not hash_file.exists():
|
||||
@@ -102,7 +97,6 @@ def verify_from_hash_file(file_path: Path, hash_file: Path) -> bool:
|
||||
|
||||
return verify_hash(file_path, expected_hash)
|
||||
|
||||
|
||||
def check_virustotal(file_hash: str, api_key: str | None = None) -> dict | None:
|
||||
"""Check VirusTotal scan status for a file hash."""
|
||||
if not api_key:
|
||||
@@ -137,7 +131,6 @@ def check_virustotal(file_hash: str, api_key: str | None = None) -> dict | None:
|
||||
print(f"⚠ ERROR checking VirusTotal: {e}")
|
||||
return None
|
||||
|
||||
|
||||
def main() -> int:
|
||||
root = repo_root()
|
||||
ap = argparse.ArgumentParser(description="Verify PDF files (hashes, signatures, VT).")
|
||||
@@ -158,7 +151,7 @@ def main() -> int:
|
||||
ap.add_argument(
|
||||
"--hash-file",
|
||||
type=Path,
|
||||
default=root / "sha256sum-light.txt",
|
||||
default=root / "export" / "thgtoa.pdf.sha256",
|
||||
help="Hash file to verify against",
|
||||
)
|
||||
|
||||
@@ -217,6 +210,5 @@ def main() -> int:
|
||||
print("✗ Some verifications FAILED")
|
||||
return 1
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
|
||||
Reference in New Issue
Block a user