diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index f811fc50..93833d47 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,6 +1,13 @@ # How to contribute -This repository is maintained by the website committee of [Tartarus](https://tartarus.science.ru.nl/), reachable at [tartaruswebsite@science.ru.nl](mailto:tartaruswebsite@science.ru.nl). The project is open-source — issues and pull requests are welcome. +This repository is maintained by the website committee of [Tartarus](https://tartarus.science.ru.nl/), reachable at . The project is open source; issues and pull requests are welcome. + +If you're an AI coding agent (Claude Code, Cursor, Aider, …), read [`AGENTS.md`](AGENTS.md) instead — it carries the same conventions but is condensed to what an agent needs to make safe edits. Humans should keep reading here. + +> [!IMPORTANT] +> Before you build something, read the next section. The scope check is the single load-bearing rule in this project. + +--- ## Before you build something: does it belong in TOSTI? @@ -14,9 +21,23 @@ If a proposed feature fails any of these tests, it does not belong in TOSTI. The **When in doubt, open an issue first** so the website committee can weigh in before code is written. -## Getting set up +--- + +## Quick start -The README's "🛠️ Development Setup" section has the canonical instructions. Briefly: clone, `uv sync`, `uv run python website/manage.py migrate`, `uv run python website/manage.py runserver`. SAML is disabled in development; use `/admin-login` to sign in as a Django superuser. +1. **Set up locally.** Follow the [Local development](README.md#-local-development) section in the README — clone, `uv sync`, `migrate`, `runserver`. SAML is disabled in development; use `/admin-login` to sign in as a Django superuser. +2. **Branch off `master`.** Pick a short, descriptive branch name (e.g. `fix-spotify-auto-start`, `add-tampon-form`). +3. **Make your change.** Keep the diff focused — one feature or fix per PR. +4. **Run the checks locally** before opening the PR: + ```bash + cd website + uv run python manage.py test # tests + uv run black website # format + uv run flake8 --exclude="migrations,website/tosti/settings/*.py" \ + --max-line-length=119 website # lint + uv run python manage.py spectacular --validate --api-version v1 > /dev/null + ``` +5. **Open the PR** against `master`. CI must pass (test + lint + image build). Merging to `master` triggers an automatic deploy to via the self-hosted runner. See [`deploy/README.md`](deploy/README.md). ## Project conventions @@ -79,6 +100,8 @@ The site runs under a strict Content Security Policy (`tosti/settings/base.py`). The `yivi/` and `age/` apps implement legally-loaded age verification for the beer fridge. A bug here can result in underage students opening the fridge. Treat these apps as critical: changes need close review, no shortcuts, and any modification of the verification path should come with a corresponding test demonstrating the legal-compliance behaviour still holds. Don't refactor opportunistically; if a change isn't strictly necessary, don't make it. +If you spot a security-sensitive bug in these (or anywhere else), see [`SECURITY.md`](SECURITY.md) — don't open a public issue for it. + ### Privacy and PII TOSTI handles personal data: names, emails, age verifications, transactions, song requests. New features that touch user data must: @@ -92,11 +115,12 @@ TOSTI handles personal data: names, emails, age verifications, transactions, son - Each app keeps its own tests under `/tests/test_*.py`. We use a `tests/` package per app — single-file `tests.py` is being phased out. - Cross-cutting tests live in `tosti/tests/`. For example, the MCP transport / auth-gate / discovery tests are there, while individual MCP tool behaviour is tested in the owning app's test module. - Use existing fixtures (e.g. `venues.json`) when your test needs venues; don't reinvent setup logic in every test class. -- Run the full suite: `uv run python website/manage.py test website/`. +- Don't delete or `@skip` failing tests to make CI pass — fix the underlying bug (or, if the test was wrong, restate the expected behaviour explicitly). +- Run the full suite locally before you push: `uv run python website/manage.py test website/`. ### Code style -- `black` for formatting, `flake8` for linting (line length 119). CI runs both. +- `black` for formatting, `flake8` for linting (line length 119), `pydocstyle` for docstring style. CI runs them all. - The site is in **English**. Don't introduce Dutch strings into UI text — the canteens are used by international students. The handful of existing Dutch fragments are scoped to legacy or external integrations; don't grow that footprint. - Heading hierarchy: pages should have one `

` and use `

`/`

` for sections. Use `

` for branding subtitles, not `

`. - For dropping shared reading-width on text-heavy pages, use the project's `.prose` utility class. @@ -105,8 +129,39 @@ TOSTI handles personal data: names, emails, age verifications, transactions, son Vendored under `website//static//` or `website/tosti/static/tosti/`. See the README's "Vendored frontend libraries" section for the upgrade procedure. Don't pull in new CDN dependencies — keep CSP tight. -## Workflow +## Opening a pull request + +A good PR is short, focused, and self-explanatory: + +- **One feature or fix per PR.** Bundling unrelated changes makes review slow and rollbacks risky. +- **PR description.** Say what changed and *why*. Link the issue if there is one. If the change touches an external system (Sentry, Yivi, Spotify, Silvasoft, SURFconext), call that out — it's the kind of thing reviewers can miss. +- **Tests.** If you fix a bug, add a regression test that fails on master and passes on your branch. If you add a feature, cover the happy path plus at least one edge case. +- **Migrations.** If you touched a model, run `makemigrations` and commit the file in the same PR. Flag destructive migrations in the description. +- **Don't squash other people's commits.** Rebases are fine for your own branch; rewriting shared history is not. +- **Don't `--force-push` master or `--no-verify` past hooks.** Ever. + +CI runs tests + flake8 + black + image build. All must pass before merge. + +## Reviewer checklist + +If you're reviewing a PR (whether you're a fellow committee member or a maintainer): + +- [ ] Does the change satisfy the scope check at the top of this document? +- [ ] Is it isolated to one app where possible? Cross-app imports justified? +- [ ] If a model changed, is there a migration? +- [ ] If a sensitive app (`age`, `yivi`, `fridges`, `silvasoft`) changed, is there a test that proves the contract still holds? +- [ ] Is there new PII being logged or stored? If stored, is the privacy policy updated? +- [ ] Are new frontend assets vendored, not CDN-loaded? +- [ ] CI green? + +Once merged, the change ships to automatically. Treat every merge as a deploy. + +## Getting help + +If you're stuck: -1. Branch off `master`. -2. Open a PR. CI must pass (test + lint + image build). -3. Merging to `master` triggers a deploy to `tosti.science.ru.nl` via the self-hosted runner. See `deploy/README.md`. +- Open a draft PR and tag the website committee — early feedback beats a long-running branch. +- Email . +- For security-sensitive questions, see [`SECURITY.md`](SECURITY.md). + + \ No newline at end of file diff --git a/SECURITY.md b/SECURITY.md index e4162839..330a6daf 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -1,9 +1,58 @@ -# Security Policy +# Security policy -## Supported Versions +TOSTI handles personal data for Radboud students, processes payments and refunds, and includes legally-loaded surfaces like Yivi-backed age verification for the beer fridge. If you find a security issue, we want to hear about it before anyone else does. -Only the most recent version of this product is supported. +## Supported versions -## Reporting a Vulnerability +Only the most recent version of TOSTI — whatever is currently deployed at — is supported. We do not back-port fixes to older deployments. -In case you find a vulnerability in this product that cannot responsibly be published by creating an issue, please contact the maintainers via [www-tosti@science.ru.nl](mailto:www-tosti@science.ru.nl) +The same applies to the [TOSTI-fridge-client](https://github.com/KiOui/TOSTI-fridge-client) firmware running on the Raspberry Pi inside each beer fridge: only the most recent release is supported. + +## Reporting a vulnerability + +**Do not open a public GitHub issue or pull request for security-sensitive bugs.** Email instead. + +Please include: + +- A description of the issue and what an attacker could do with it. +- Steps to reproduce. A minimal proof-of-concept — a `curl` invocation, a script, a request payload — helps us confirm the issue quickly. +- The TOSTI version or commit you tested against (if you know it). +- Whether you've already shared the details with anyone else. + +You should hear back from the website committee within **7 days** with an acknowledgement. We'll work with you to confirm the issue, agree on a fix timeline, and credit you in the release notes if you'd like. + +If you do not get a response within 14 days, the report has likely been missed — please re-send the email and consider also reaching out to the Tartarus board directly. + +## What counts as a security issue + +When in doubt, report it — we'd rather field a false alarm than miss something. As a rough guide, these are squarely in scope: + +- **Authentication / authorisation bypasses** — anything that lets a user access another user's account, place orders or transactions on someone else's behalf, or read data without the right scope. +- **Age-verification bypass** for the beer fridge. This is the most legally-loaded surface in the project; treat anything that lets an underage user open a fridge as a high-severity issue. +- **OAuth / MCP issues** — flaws in the authorize / token / consent flow at `/oauth/authorize/`, `/oauth/token/`, `/oauth/register/`, or the MCP endpoints. Scope-confusion, PKCE bypass, token leakage, replayable codes, etc. +- **SAML / SURFconext integration** issues — signature bypass, assertion replay, IdP impersonation. +- **Privacy leaks** — endpoints that return PII (names, emails, age verifications, transaction history, song requests) to viewers who shouldn't see it. +- **Stored XSS / CSRF / SQL injection / SSRF** in any user-reachable surface. +- **Secret leakage** — credentials, tokens, or keys exposed in the repo, in logs, in Sentry events, or in client-side responses. +- **Supply chain** — vulnerabilities in our vendored frontend assets (Vue, Bootstrap, Swagger UI, etc.) or in dependencies that meaningfully affect TOSTI. + +Generally out of scope (open a regular GitHub issue for these): + +- Bugs in feature behaviour that don't have a security impact. +- Cosmetic issues, broken links, typos. +- Reports against domains we don't operate (e.g. `radboudnet.nl`, `surf.nl`). +- Findings from automated scanners without a working proof-of-concept — we appreciate the heads-up, but please verify the finding manually first. + +## What we ask of reporters + +- **Don't pivot.** Once you've demonstrated the issue exists, stop. Don't download other people's data, modify production state, or escalate to other surfaces. +- **Don't test in production unless you have to.** A local dev instance reproduces almost everything. +- **Coordinate disclosure.** Don't publish details — talks, blog posts, social media — until we've shipped a fix and agreed on a publication date with you. + +## Maintainer contact + +- Website committee — + +This is the same address that handles general project correspondence; flag your message clearly as a security issue in the subject line so it gets routed appropriately. + + \ No newline at end of file