Skip to content

Commit c7143f0

Browse files
authored
Merge pull request #14 from tanem/docs/release-env-protection
docs: release wiring + accuracy pass across living docs
2 parents 181ba8d + 413b395 commit c7143f0

3 files changed

Lines changed: 10 additions & 9 deletions

File tree

CONTRIBUTING.md

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ uv run pre-commit install # enable the gitleaks secret-scan hook
1212
## Running tests and hooks
1313

1414
```bash
15-
uv run pytest # run the test suite (coverage gate: ≥95%)
15+
uv run pytest # run the test suite (coverage gate: 100%)
1616
uv run pre-commit run --all-files # run the gitleaks hook manually
1717
uv run ruff check src/ tests/ # lint
1818
uv run ruff format --check src/ tests/ # check formatting
@@ -71,20 +71,20 @@ Don't hand-bump these versions — let Renovate's PRs flow through.
7171

7272
Releases publish to PyPI via [Trusted Publishing](https://docs.pypi.org/trusted-publishers/) — there is no stored API token. The publish workflow is [`.github/workflows/release.yml`](.github/workflows/release.yml).
7373

74-
**One-time setup (already done for an existing project, required once per index):**
74+
**How it's wired:**
7575

76-
- On PyPI and TestPyPI, register a pending publisher: owner `tanem`, repository `mt5-pnl-exporter`, workflow `release.yml`, environment `pypi` (PyPI) / `testpypi` (TestPyPI).
77-
- On GitHub, create the `pypi` and `testpypi` repository Environments.
76+
- The repo has two GitHub Environments, `pypi` and `testpypi`. The `pypi` Environment requires a reviewer (`tanem`), so a real publish pauses for manual approval before the immutable upload. "Prevent self-review" is off, so the solo maintainer approves their own release.
77+
- Both indices have a pending publisher registered — owner `tanem`, repository `mt5-pnl-exporter`, workflow `release.yml`, environment `pypi` (PyPI) / `testpypi` (TestPyPI). Each index needs this registered once before its first publish.
7878

79-
**Rehearse to TestPyPI** (validates the OIDC handshake and the rendered page without burning a real version):
79+
**Rehearse to TestPyPI** validates the OIDC handshake and the rendered page without burning a real version:
8080

81-
1. Actions tab → `release` workflow → Run workflow (`workflow_dispatch`). This builds and uploads to TestPyPI.
81+
1. Actions tab → `release` workflow → Run workflow (`workflow_dispatch`). Builds and uploads to TestPyPI.
8282

8383
**Publish a real release:**
8484

8585
1. Tag the commit, e.g. `git tag v1.0.0`.
8686
2. Draft a GitHub Release against that tag with release notes (the notes are the changelog).
87-
3. Publish the Release. The `release: published` event runs `release.yml`, which builds and uploads to PyPI.
87+
3. Publish the Release. The `release: published` event runs `release.yml` and the publish job pauses on the `pypi` Environment. Approve it in the Actions tab; the job then builds and uploads to PyPI.
8888

8989
A PyPI version is immutable once uploaded — the version number cannot be reused. The TestPyPI rehearsal de-risks the first upload.
9090

SECURITY.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
## Scope
44

5-
This tool handles **read-only MT5 investor passwords** — credentials that can view account data but cannot place or modify trades — and a **snapshot encryption passphrase** used to age-encrypt the on-disk snapshot. Both live only on the Windows host where `poll` runs, in its OS keychain via `keyring` (Windows Credential Manager). Neither is written to disk, logs, or the snapshot file.
5+
This tool handles **read-only MT5 investor passwords** — credentials that can view account data but cannot place or modify trades — and a **snapshot encryption passphrase** used to age-encrypt the on-disk snapshot. Both live only on the Windows host where `export` runs, in its OS keychain via `keyring` (Windows Credential Manager). Neither is written to disk, logs, or the snapshot file.
66

77
Vulnerabilities in scope:
88

@@ -22,6 +22,7 @@ Out of scope:
2222
- **GitHub Actions are pinned to commit SHAs** (not mutable tags), so a compromised or retagged action cannot inject code into CI. [Renovate](https://docs.renovatebot.com/) keeps the pins current via `helpers:pinGitHubActionDigests`.
2323
- **`lockFileMaintenance`** periodically refreshes `uv.lock` so transitive dependency security patches are picked up rather than pinned indefinitely.
2424
- Dependency update PRs (Renovate) must pass the full `tests` workflow before merging; see [`renovate.json`](renovate.json) and [`CONTRIBUTING.md`](CONTRIBUTING.md).
25+
- **Releases publish via PyPI [Trusted Publishing](https://docs.pypi.org/trusted-publishers/)** (OIDC), so no long-lived PyPI API token is stored in the repo or CI; the `pypi` deployment environment also gates a real publish on manual approval. See [`CONTRIBUTING.md`](CONTRIBUTING.md)'s Releasing section.
2526

2627
## Reporting
2728

docs/security-audit.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ Findings are listed below the table with patch references.
2020
| `secrets.py::get_encryption_passphrase` | passphrase | keychain read | n/a | n/a | ✓ returns to caller |
2121
| `secrets.py::set_encryption_passphrase` | passphrase | keychain write ||| ✓ accepts from caller |
2222
| `config.py::resolve_passwords` | investor pw | keychain read → dict || ✓ (registered with `redact_filter` on read) | ✓ scoped to `MT5Source._passwords` |
23-
| `cli.py::poll` (encryption passphrase load) | passphrase | keychain read → `snapshot.read/write` || ✓ (registered with `redact_filter` on read) | ✓ goes only to `pyrage.passphrase.encrypt/decrypt` |
23+
| `cli.py::export` (encryption passphrase load) | passphrase | keychain read → `snapshot.read/write` || ✓ (registered with `redact_filter` on read) | ✓ goes only to `pyrage.passphrase.encrypt/decrypt` |
2424
| `secrets.py::redact_filter` | n/a (boundary) | log filter | n/a | n/a | covers `logging` handlers only; `rich.Console` output bypasses it — see **Finding 2** |
2525
| `config.py::check_file_perms` | n/a (boundary) | warn on `config.yaml` group/other-read | n/a | n/a | POSIX-only; no-op on Windows |
2626

0 commit comments

Comments
 (0)