mirror of
https://github.com/0xMarcio/cve.git
synced 2026-10-27 16:51:03 +00:00
162 lines
6.2 KiB
YAML
162 lines
6.2 KiB
YAML
name: Archive Public PoCs
|
|
|
|
on:
|
|
workflow_dispatch:
|
|
inputs:
|
|
limit:
|
|
description: Maximum new targets in this batch
|
|
type: choice
|
|
default: '200'
|
|
options: ['200', '100', '10', '3']
|
|
max_seconds:
|
|
description: Maximum worker time in seconds
|
|
type: choice
|
|
default: '2400'
|
|
options: ['2400', '2100', '300']
|
|
schedule:
|
|
- cron: '47 */6 * * *'
|
|
|
|
permissions:
|
|
contents: write
|
|
actions: write
|
|
|
|
jobs:
|
|
capture:
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 80
|
|
concurrency:
|
|
group: wayback-captures
|
|
queue: max
|
|
cancel-in-progress: false
|
|
permissions:
|
|
contents: read
|
|
actions: read
|
|
steps:
|
|
- uses: actions/checkout@v7
|
|
with:
|
|
ref: main
|
|
persist-credentials: false
|
|
- uses: actions/setup-python@v7
|
|
with:
|
|
python-version: '3.13'
|
|
- name: Find the last durable worker checkpoint
|
|
id: previous
|
|
env:
|
|
GH_TOKEN: ${{ github.token }}
|
|
run: |
|
|
gh api "repos/$GITHUB_REPOSITORY/actions/artifacts?name=archive-checkpoints&per_page=100" \
|
|
--jq '[.artifacts[] | select(.expired == false)] | sort_by(.created_at) | last | if . == null then empty else "artifact=\(.id)\nrun=\(.workflow_run.id)" end' >> "$GITHUB_OUTPUT"
|
|
- name: Recover state even if the previous publish failed
|
|
if: steps.previous.outputs.artifact != ''
|
|
uses: actions/download-artifact@v7
|
|
with:
|
|
artifact-ids: ${{ steps.previous.outputs.artifact }}
|
|
github-token: ${{ github.token }}
|
|
run-id: ${{ steps.previous.outputs.run }}
|
|
path: /tmp/previous-archive-checkpoint
|
|
merge-multiple: true
|
|
- name: Merge the recovered checkpoint
|
|
if: steps.previous.outputs.artifact != ''
|
|
run: python3 scripts/archive_pocs.py --merge-from /tmp/previous-archive-checkpoint
|
|
- name: Discover actual PoC source files at immutable revisions
|
|
id: sources
|
|
continue-on-error: true
|
|
timeout-minutes: 12
|
|
env:
|
|
GITHUB_TOKEN: ${{ github.token }}
|
|
run: python3 -u scripts/archive_sources.py --limit 100 --requests 800 --max-seconds 600
|
|
- name: Resume 2026-onward captures within service limits
|
|
id: capture
|
|
continue-on-error: true
|
|
timeout-minutes: 45
|
|
env:
|
|
IA_ACCESS_KEY: ${{ secrets.IA_ACCESS_KEY }}
|
|
IA_SECRET_KEY: ${{ secrets.IA_SECRET_KEY }}
|
|
ARCHIVE_LIMIT: ${{ inputs.limit || '200' }}
|
|
ARCHIVE_SECONDS: ${{ inputs.max_seconds || '2400' }}
|
|
run: |
|
|
case "$ARCHIVE_LIMIT" in 3|10|100|200) ;; *) exit 1 ;; esac
|
|
case "$ARCHIVE_SECONDS" in 300|2100|2400) ;; *) exit 1 ;; esac
|
|
python3 -u scripts/archive_pocs.py --capture --min-year 2026 --limit "$ARCHIVE_LIMIT" --max-seconds "$ARCHIVE_SECONDS"
|
|
- name: Find and verify existing captures across the 2026 backlog
|
|
id: lookup
|
|
continue-on-error: true
|
|
timeout-minutes: 12
|
|
run: python3 -u scripts/archive_pocs.py --lookup-pages --min-year 2026 --limit 500 --max-seconds 600
|
|
- name: Preserve checkpoints even after interruption
|
|
if: always()
|
|
uses: actions/upload-artifact@v6
|
|
with:
|
|
name: archive-checkpoints
|
|
path: index/archives/
|
|
if-no-files-found: error
|
|
retention-days: 14
|
|
overwrite: true
|
|
- name: Report archive failures
|
|
if: steps.sources.outcome == 'failure' || steps.capture.outcome == 'failure' || steps.lookup.outcome == 'failure'
|
|
run: |
|
|
echo '::error::Archive processing failed. See the failed step; checkpoints were preserved.'
|
|
exit 1
|
|
|
|
publish:
|
|
if: always() && needs.capture.result != 'skipped'
|
|
needs: capture
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 10
|
|
concurrency:
|
|
group: repository-updates
|
|
queue: max
|
|
cancel-in-progress: false
|
|
steps:
|
|
- uses: actions/checkout@v7
|
|
with:
|
|
ref: main
|
|
- uses: actions/setup-python@v7
|
|
with:
|
|
python-version: '3.13'
|
|
- uses: actions/download-artifact@v7
|
|
with:
|
|
name: archive-checkpoints
|
|
path: /tmp/archive-checkpoints
|
|
- name: Merge durable capture results into current data
|
|
id: snapshots
|
|
run: |
|
|
python3 -c 'import sys,json; sys.path.insert(0,"scripts"); import archive_pocs; print(json.dumps(archive_pocs.published_snapshots(),sort_keys=True))' > /tmp/archive-before.json
|
|
python3 scripts/archive_pocs.py --merge-from /tmp/archive-checkpoints
|
|
python3 scripts/archive_pocs.py --stats >> "$GITHUB_STEP_SUMMARY"
|
|
python3 -c 'import sys,json; sys.path.insert(0,"scripts"); import archive_pocs; print(json.dumps(archive_pocs.published_snapshots(),sort_keys=True))' > /tmp/archive-after.json
|
|
if ! cmp -s /tmp/archive-before.json /tmp/archive-after.json; then
|
|
echo 'changed=true' >> "$GITHUB_OUTPUT"
|
|
fi
|
|
- name: Refresh counts for preserved sources
|
|
if: steps.snapshots.outputs.changed == 'true'
|
|
run: |
|
|
python3 .github/hero.py
|
|
python3 .github/getTrending.py --counts-only
|
|
- name: Publish verified archive links
|
|
id: commit
|
|
run: |
|
|
git config user.name 'CVE-BOT'
|
|
git config user.email '[email protected]'
|
|
git add --all -- index/archives/ README.md docs/hero.svg docs/social-card.svg docs/stats.json
|
|
if git diff --cached --quiet; then
|
|
echo 'No archive updates'
|
|
exit 0
|
|
fi
|
|
git commit -m "Preserve public PoC snapshots $(date -u +'%Y-%m-%d')"
|
|
for attempt in 1 2 3; do
|
|
if git push origin HEAD:main; then
|
|
echo 'pushed=true' >> "$GITHUB_OUTPUT"
|
|
exit 0
|
|
fi
|
|
echo "Push rejected; replaying archive updates (attempt $attempt)"
|
|
git fetch --unshallow origin main 2>/dev/null || git fetch origin main
|
|
git rebase FETCH_HEAD || { git rebase --abort; exit 1; }
|
|
done
|
|
exit 1
|
|
- name: Deploy archive links
|
|
if: steps.commit.outputs.pushed == 'true' && steps.snapshots.outputs.changed == 'true'
|
|
env:
|
|
GH_TOKEN: ${{ github.token }}
|
|
run: gh workflow run site.yml --ref main
|