ctf_status.py is now just the command line; the work lives in tracker/. The rules that decide whether a challenge is believed are in tracker/checks.py, one function per rule, listed in README_CHECKS and REPO_CHECKS. Output is unchanged. Read the config from .env only (ctf_status.env.example -> .env.example), let single_run_lock() hold its own lock files, and rewrite the README as a short overview of what the repo does and how to run it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
127 lines
4.4 KiB
Markdown
127 lines
4.4 KiB
Markdown
# CTF challenge status tracker
|
|
|
|
Tracks the progress of the h4tum CTF challenges. It reads every repo in the
|
|
GitLab challenges group, works out each challenge's status, category and
|
|
difficulty from its `README.md`, and checks that challenges marked as ready
|
|
really are. The result goes to:
|
|
|
|
- **a HedgeDoc note**: an overview of all challenges, refreshed every 30 minutes
|
|
- **`challs.txt` in the deployment repo**: the clone URLs of every challenge
|
|
ready for infra testing, updated daily
|
|
- **the terminal**: the same overview, for a quick look
|
|
|
|
## Requirements
|
|
|
|
- Python 3.9+
|
|
- [`requests`](https://pypi.org/project/requests/) and [`PyYAML`](https://pypi.org/project/PyYAML/)
|
|
(`python-requests python-yaml` on Arch, `python3-requests python3-yaml` on Debian)
|
|
|
|
## Configuration
|
|
|
|
```sh
|
|
cp .env.example .env && chmod 600 .env
|
|
```
|
|
|
|
| Key | Required | Meaning |
|
|
|---|---|---|
|
|
| `GITLAB_GROUP_URL` | yes | GitLab group that holds one repo per challenge |
|
|
| `GITLAB_TOKEN` | yes | Personal access token, see below |
|
|
| `HEDGEDOC_NOTE` | for `--update` | URL of an existing note; its permission must be **freely** |
|
|
| `HEDGEDOC_TAGS` | no | Tags written into the note's front matter |
|
|
| `CHALLS_REPO_URL` | for `--challs` | Deployment repo that holds `challs.txt` |
|
|
| `CHALLS_PATH` | no | Path of the file in that repo (default `2026/challs.txt`) |
|
|
| `CHALLS_BRANCH` | no | Branch to commit to (default: the repo's default branch) |
|
|
|
|
The token needs **Project: Read** and **Repository: Read** on the challenges
|
|
group. For `--challs` it also needs **Repository: Read + Write** on the deployment
|
|
repo, and permission to push to the branch if it is protected.
|
|
|
|
Environment variables override the values in `.env`.
|
|
|
|
## Usage
|
|
|
|
```sh
|
|
./ctf_status.py # print the overview
|
|
./ctf_status.py -o overview.md # also write the note's markdown to a file
|
|
./ctf_status.py --update # overwrite the HedgeDoc note
|
|
./ctf_status.py --challs # commit challs.txt, if it changed
|
|
./ctf_status.py -q --challs-out - # preview challs.txt without committing
|
|
```
|
|
|
|
`./ctf_status.py --help` lists all options. With `--quiet`, a run prints nothing
|
|
unless something changed or failed.
|
|
|
|
## Deployment
|
|
|
|
On the server, with `.env` in place:
|
|
|
|
```sh
|
|
./install-timer.sh
|
|
```
|
|
|
|
This installs and starts two systemd user timers:
|
|
|
|
| Timer | Runs | Does |
|
|
|---|---|---|
|
|
| `ctf-status.timer` | every 30 minutes | `--update`: refresh the HedgeDoc note |
|
|
| `ctf-challs.timer` | daily at 04:00 | `--challs`: commit `challs.txt` |
|
|
|
|
Re-running the script is safe. To check on the timers:
|
|
|
|
```sh
|
|
systemctl --user list-timers 'ctf-*'
|
|
journalctl --user -u ctf-status.service -n 50
|
|
journalctl --user -u ctf-challs.service -n 50
|
|
```
|
|
|
|
## How challenges are evaluated
|
|
|
|
Each challenge repo starts from a template whose README begins with:
|
|
|
|
```
|
|
### Challenge Name
|
|
- Category: crypto
|
|
- Difficulty: medium
|
|
- Event: ...
|
|
- Author: ...
|
|
- Tester: ...
|
|
- Status: Testing Ready
|
|
```
|
|
|
|
The status is one of **Planning → PoC Draft → Testing Ready → Tested → Ready**.
|
|
Loosely written values are normalised. If a status names several stages, the
|
|
least advanced one counts.
|
|
|
|
Two kinds of finding are reported:
|
|
|
|
- **Problems** are missing or unclear README fields, such as no category, an
|
|
unreadable status, or a Testing Ready challenge with no tester. They are
|
|
listed under *Needs attention* and change nothing else.
|
|
- **Blockers** are signs that a challenge claiming Testing Ready or beyond is not
|
|
actually finished: `description.yml` is missing or still the template, its
|
|
category or flag is wrong, a `deploy_dir` or its `Dockerfile` is missing, or
|
|
there is no `solve.py`. A blocked challenge (⛔) is kept out of `challs.txt`.
|
|
|
|
Passing every check does not prove that a challenge builds or solves. That is
|
|
what testing is for.
|
|
|
|
All rules are in [`tracker/checks.py`](tracker/checks.py), one function each.
|
|
|
|
## Project layout
|
|
|
|
```
|
|
ctf_status.py command line entry point
|
|
tracker/
|
|
checks.py the rules: README problems and repo blockers
|
|
challenge.py reads a README header into a Challenge
|
|
gitlab.py GitLab API access
|
|
overview.py runs parsing and checks over all repos
|
|
terminal.py terminal output
|
|
markdown.py HedgeDoc note
|
|
challs.py challs.txt
|
|
hedgedoc.py writes the note to HedgeDoc
|
|
config.py reads the configuration
|
|
install-timer.sh installs the systemd units below
|
|
ctf-*.service/.timer systemd unit templates
|
|
```
|