Harden the container: non-root, pinned images and dependencies, the key in a file (B-91, SEC-51, SEC-52, SEC-53; closes B-71, B-72)
- app runs as uid 1000; a one-shot data-permissions service chowns ./data to it and closes it to others (works on an older root-owned install with no host sudo); files are created 0600 (umask 077) - python:3.12-slim and caddy:2-alpine pinned by digest; dependencies installed from requirements.lock with --require-hashes - the Fernet key is XPRV_ENCRYPTION_KEY_FILE (./secrets/xprv_encryption.key, a compose secret), out of every environment variable and of a ./data backup - setup.md no longer presents a second way to run the server (B-72) Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
40ac490826
commit
c1b192f6d1
23 files changed
+1500
-183
No files matched your search
@@ -1,6 +1,6 @@
|
||||
# ADR-01 — La piattaforma è custodiale: tutte le chiavi sono generate e tenute dal server
|
||||
|
||||
- **Stato:** Accettata — implementazione da completare (B-91)
|
||||
- **Stato:** Accettata
|
||||
- **Area:** Custodia, chiavi e indirizzi
|
||||
- **Fonte di verità per:** la scelta di progetto descritta qui sotto; i requisiti (`PREFISSO-NN`) che la attuano stanno nei documenti di dominio, indicati nelle *Conseguenze*.
|
||||
|
||||
@@ -23,5 +23,3 @@ git. L'utente non controlla nulla finché non preleva.
|
||||
|
||||
*Codice:* `app/wallet/hd.py:_load_account_key` · `app/wallet/keystore.py:decrypt_xprv` · `app/wallet/hd.py:generate_master_key` ·
|
||||
*Test:* `tests/unit/test_hd.py`
|
||||
*Stato:* da implementare (B-91) · *Oggi:* la chiave di cifratura è la
|
||||
variabile d'ambiente `XPRV_ENCRYPTION_KEY`, visibile con `docker inspect`.
|
||||
@@ -1,6 +1,6 @@
|
||||
# ADR-11 — Il server rifiuta di partire con segreti inadeguati
|
||||
|
||||
- **Stato:** Accettata — implementazione da completare (B-91)
|
||||
- **Stato:** Accettata
|
||||
- **Area:** Configurazione e ambiente
|
||||
- **Fonte di verità per:** la scelta di progetto descritta qui sotto; i requisiti (`PREFISSO-NN`) che la attuano stanno nei documenti di dominio, indicati nelle *Conseguenze*.
|
||||
|
||||
@@ -24,8 +24,6 @@ chiuso a tutti. Il controllo è nel lifespan, non in un validatore di
|
||||
|
||||
*Codice:* `app/config.py:validate_runtime_secrets` · `app/main.py:lifespan` · `app/api/routes/admin.py:require_admin` ·
|
||||
*Test:* `tests/unit/test_config.py`
|
||||
*Stato:* da implementare (B-91) per la parte sulla chiave · *Oggi:* la
|
||||
chiave Fernet arriva da `XPRV_ENCRYPTION_KEY` (variabile d'ambiente); dopo
|
||||
B-91 arriva da un file (`XPRV_ENCRYPTION_KEY_FILE`,
|
||||
La chiave Fernet arriva da un file (`XPRV_ENCRYPTION_KEY_FILE`,
|
||||
[sicurezza.md](../sicurezza.md#sec-53) SEC-53) e il rifiuto riguarda un file
|
||||
mancante o vuoto.
|
||||
+6
-10
@@ -55,9 +55,8 @@ flowchart LR
|
||||
installate da un file di lock con gli hash
|
||||
([sicurezza.md](sicurezza.md#sec-51) SEC-51, SEC-52, SEC-53 (B-91)). I controlli che richiedono il codice
|
||||
applicativo (chiave, quorum, ripristino) sono nel lifespan ([ARC-60]).
|
||||
*Codice:* `Dockerfile` (`CMD`) · `migrations/env.py`
|
||||
*Stato:* da implementare (B-91) · *Oggi:* il container gira come root,
|
||||
`python:3.12-slim` non è fissato per digest e le dipendenze sono `>=`.
|
||||
*Codice:* `Dockerfile` (`CMD`) · `migrations/env.py` · `requirements.lock` ·
|
||||
*Test:* `tests/unit/test_container_hardening.py`
|
||||
- **[ARC-04]** `app` è solo `expose`d sulla rete interna di compose (porta
|
||||
8123), mai pubblicato sull'host: l'unico ingresso è Caddy, che pubblica 80 e
|
||||
443. Su questo si basa la lettura dell'IP del client (vedi
|
||||
@@ -79,9 +78,9 @@ flowchart LR
|
||||
rilascio, così un rollback sa quale immagine rimettere
|
||||
([operativita.md](operativita.md) (B-93)).
|
||||
*Codice:* `docker-compose.yml`
|
||||
*Stato:* da implementare (B-91, B-93) · *Oggi:* la chiave di cifratura è la
|
||||
variabile `XPRV_ENCRYPTION_KEY` in `.env`, i permessi sono quelli di default
|
||||
(`.env` e file di chiave leggibili da tutti) e l'immagine non ha etichetta.
|
||||
*Test:* `tests/unit/test_container_hardening.py`
|
||||
*Stato:* da implementare (B-93), per l'etichetta per rilascio · *Oggi:*
|
||||
l'immagine di `app` ha l'etichetta fissa `plm-lottery-app:local`.
|
||||
|
||||
- **[ARC-61]** **Salute del container.** Il container `app` ha un
|
||||
`healthcheck` che interroga `http://127.0.0.1:8123/health/ready` ogni 30s
|
||||
@@ -415,8 +414,6 @@ implementare» non esistono ancora nel codice (vedi [ARC-60] e [ARC-22]).
|
||||
veri.
|
||||
*Codice:* `app/config.py:validate_runtime_secrets` ·
|
||||
*Test:* `tests/unit/test_config.py`
|
||||
*Stato:* da implementare (B-91) · *Oggi:* si controlla che la variabile
|
||||
`XPRV_ENCRYPTION_KEY` non sia vuota.
|
||||
- **[ARC-20]** Gli endpoint Electrum vengono letti da `ELECTRUM_HOST`,
|
||||
`ELECTRUM_PORT`, `ELECTRUM_USE_SSL` ed `ELECTRUM_FALLBACK_SERVERS`
|
||||
(`host:porta[:notls][:pin=<sha256>]` separati da virgola, [ARC-30]); un
|
||||
@@ -598,8 +595,7 @@ La configurazione è divisa in due posti con regole diverse.
|
||||
| `enable_api_docs` | `ENABLE_API_DOCS` | `False` | Espone Swagger/ReDoc/OpenAPI ([ARC-14]) |
|
||||
|
||||
*Codice:* `app/config.py:Settings` · *Test:* `tests/unit/test_config.py`
|
||||
*Stato:* da implementare (B-91), per `XPRV_ENCRYPTION_KEY_FILE` · *Oggi:* la
|
||||
chiave Fernet è la variabile `XPRV_ENCRYPTION_KEY`.
|
||||
|
||||
- **[ARC-31]** **Parametri operativi e di business** (costo della scommessa,
|
||||
durata e pausa del round, animazione, commissione di rete, timeout RBF,
|
||||
indirizzo commissioni, pausa di manutenzione e suo motivo, conferme dei
|
||||
|
||||
@@ -581,7 +581,7 @@ restano nella tabella come storico.
|
||||
`secrets.token_bytes(32)`. All'inserimento del round si scrivono insieme
|
||||
`server_seed_hash` (`sha256` di quei 32 byte, esadecimale minuscolo) e
|
||||
`server_seed_enc`: il segreto **cifrato con Fernet** usando la stessa chiave
|
||||
che cifra la master xprv (`XPRV_ENCRYPTION_KEY`, dopo B-91 il file di
|
||||
che cifra la master xprv (il file di cifratura,
|
||||
[sicurezza.md](sicurezza.md#sec-53) SEC-53). Il segreto in chiaro non viene
|
||||
mai scritto nel database prima della rivelazione: `server_seed` resta
|
||||
`NULL` e si valorizza (esadecimale minuscolo, 64 caratteri) nella stessa
|
||||
|
||||
+5
-5
@@ -44,9 +44,9 @@ flowchart LR
|
||||
Docker con nome `caddy_data` e `caddy_config`, non in `./data`.
|
||||
*Codice:* `docker-compose.yml`, `Caddyfile`
|
||||
|
||||
Oggi il container gira come root, e i file che scrive in `./data` (database,
|
||||
backup, chiave cifrata dagli script) appartengono a root sull'host. Dopo B-91
|
||||
gira come utente non root, con `./data` in `0700` e i file di chiave in
|
||||
Il container gira come utente non root (uid 1000, quello dell'utente dell'host:
|
||||
un servizio `data-permissions`, a ogni avvio, riporta `./data` a quel
|
||||
proprietario), con `./data` in `0700` e i file di chiave in
|
||||
`0600`, e la chiave di cifratura sta in un file a parte
|
||||
([architettura.md](architettura.md) ARC-03, ARC-05;
|
||||
[SEC-51](sicurezza.md#sec-51)); le procedure che scrivono in `./data`
|
||||
@@ -106,7 +106,7 @@ Il file da salvare è `./data/keys/master.xprv.enc`, quello che il container
|
||||
legge. Su questa installazione esiste anche una seconda copia,
|
||||
`./master.xprv.enc` nella radice del progetto, a cui punta il
|
||||
`MASTER_KEY_PATH` di `.env` (B-71): oggi decifrano alla stessa xprv, ma non è
|
||||
il file dell'app. Dopo B-91 la chiave di cifratura non sta più in `.env` ma
|
||||
il file dell'app. La chiave di cifratura non sta più in `.env` ma
|
||||
nel file `./secrets/xprv_encryption.key` ([SEC-53](sicurezza.md#sec-53),
|
||||
[architettura.md](architettura.md) ARC-05), fuori da `./data/`: si salva come
|
||||
oggi la variabile, separata dalla master key.
|
||||
@@ -750,7 +750,7 @@ con `docker compose logs app` ([architettura.md](architettura.md) ARC-09).
|
||||
root con permessi `0644`: `sudo chmod 600 data/keys/master.xprv.enc`.
|
||||
Gli script scrivono `0600` e rifiutano una xprv i cui
|
||||
indirizzi non coincidono con quelli del database
|
||||
([SEC-50](sicurezza.md#sec-50)); dopo B-91 la chiave sta in un file e non
|
||||
([SEC-50](sicurezza.md#sec-50)); la chiave sta in un file e non
|
||||
in `.env` ([SEC-53](sicurezza.md#sec-53)).
|
||||
|
||||
**Se è trapelata la master key stessa** (file cifrato *e* chiave, oppure la
|
||||
|
||||
+16
-18
@@ -1,13 +1,13 @@
|
||||
# Setup
|
||||
|
||||
Passaggi da eseguire una tantum per preparare un'istanza di PLM Lottery, prima di
|
||||
poterla avviare (in locale o via Docker). Per come avviarla poi ogni volta, vedi
|
||||
poterla avviare. Il server gira sempre in Docker; il venv serve solo a test, migrazioni da generare e script una tantum. Per come avviarla poi ogni volta, vedi
|
||||
[running-the-server.md](running-the-server.md).
|
||||
|
||||
## 1. Prerequisiti
|
||||
|
||||
- Python 3.12+ (serve solo per il workflow locale/venv — puoi saltarlo se usi solo Docker)
|
||||
- Docker + Docker Compose (serve solo per il workflow a container)
|
||||
- Docker + Docker Compose (il server gira solo così)
|
||||
- Python 3.12+ (solo per gli strumenti locali: test, `alembic revision` e gli script delle chiavi)
|
||||
- Un server Electrum raggiungibile per la rete PLM. Il server di bootstrap per lo
|
||||
sviluppo è `santantonio.sytes.net:50002` (SSL) — va bene per i test, ma in
|
||||
produzione conviene usarne uno di cui ci si fida o gestirne uno proprio.
|
||||
@@ -31,7 +31,7 @@ cp .env.example .env
|
||||
|
||||
| Variabile | Scopo | Come generarla |
|
||||
|---|---|---|
|
||||
| `XPRV_ENCRYPTION_KEY` | Chiave simmetrica che cifra a riposo la master xprv del server. **Perdere questa chiave significa perdere per sempre l'accesso ai fondi di tutti gli utenti.** | `python -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())"` |
|
||||
| `XPRV_ENCRYPTION_KEY_FILE` | Percorso del file che contiene la chiave simmetrica che cifra a riposo la master xprv del server (`./secrets/xprv_encryption.key`, cartella `0700`, file `0600`; nel container è montato come secret). **Perdere questa chiave significa perdere per sempre l'accesso ai fondi di tutti gli utenti.** | `mkdir -p secrets && chmod 700 secrets && python -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())" > secrets/xprv_encryption.key && chmod 600 secrets/xprv_encryption.key` |
|
||||
| `JWT_SECRET` | Firma i token di sessione degli utenti. | `python -c "import secrets; print(secrets.token_urlsafe(32))"` |
|
||||
| `ADMIN_TOKEN` | Token bearer richiesto sugli endpoint admin (header `X-Admin-Token`). | `python -c "import secrets; print(secrets.token_urlsafe(32))"` |
|
||||
|
||||
@@ -61,7 +61,7 @@ certificato del primario. Attenzione: un valore scritto male **blocca
|
||||
l'avvio** dell'app — è voluto, meglio accorgersene subito che durante il
|
||||
disservizio in cui il fallback serve davvero.
|
||||
|
||||
`JWT_SECRET` e `XPRV_ENCRYPTION_KEY` vengono verificati all'avvio: se sono vuoti
|
||||
`JWT_SECRET` e la chiave in `XPRV_ENCRYPTION_KEY_FILE` vengono verificati all'avvio: se sono vuoti
|
||||
(o `JWT_SECRET` è più corto di 32 caratteri) il container si rifiuta di partire con
|
||||
un errore esplicito, invece di avviarsi e rompersi al primo login. Nota: `.env`
|
||||
contiene solo segreti e configurazione di infrastruttura — i parametri di
|
||||
@@ -74,15 +74,13 @@ admin dopo l'avvio, non qui — vedi [guida-admin.md](guida-admin.md).
|
||||
|
||||
Il server deriva l'indirizzo di deposito di ogni utente (e l'indirizzo pool) da
|
||||
un'unica master xprv, generata una volta e cifrata a riposo con
|
||||
`XPRV_ENCRYPTION_KEY`. Questo passaggio va eseguito esattamente una volta per
|
||||
ogni deployment, dopo aver impostato `XPRV_ENCRYPTION_KEY` in `.env`:
|
||||
la chiave in `XPRV_ENCRYPTION_KEY_FILE`. Questo passaggio va eseguito esattamente una volta per
|
||||
ogni deployment, dopo aver creato quel file e impostato `XPRV_ENCRYPTION_KEY_FILE` in `.env`:
|
||||
|
||||
- **Locale/venv**: `PYTHONPATH=. python scripts/generate_master_key.py`
|
||||
- **Docker**: `docker compose run --rm app python scripts/generate_master_key.py`
|
||||
Dal venv: `PYTHONPATH=. python scripts/generate_master_key.py`, con `MASTER_KEY_PATH=./data/keys/master.xprv.enc` in `.env`, così lo script scrive esattamente il file che legge il container.
|
||||
|
||||
Questo scrive un file cifrato (`MASTER_KEY_PATH`, default `./master.xprv.enc` in
|
||||
locale o `./data/keys/master.xprv.enc` con Docker). **Fai il backup di questo
|
||||
file insieme a `XPRV_ENCRYPTION_KEY`** — uno dei due da solo è inutile, ma
|
||||
Questo scrive un file cifrato (`MASTER_KEY_PATH`, in Docker `./data/keys/master.xprv.enc`). **Fai il backup di questo
|
||||
file insieme a `secrets/xprv_encryption.key`** — uno dei due da solo è inutile, ma
|
||||
perderli entrambi insieme significa perdere i fondi di tutti gli utenti senza
|
||||
possibilità di recupero.
|
||||
|
||||
@@ -107,10 +105,10 @@ Entrambi vanno eseguiti localmente (o dentro il container via
|
||||
pannello admin: chi ottiene questa xprv ottiene il controllo dei fondi di
|
||||
tutti gli utenti e del pool.
|
||||
|
||||
## 4. Installare le dipendenze (solo workflow locale/venv)
|
||||
## 4. Installare gli strumenti locali (venv)
|
||||
|
||||
Salta questo passaggio se usi solo Docker — l'immagine installa le proprie
|
||||
dipendenze durante la build.
|
||||
Servono per i test e per gli script delle chiavi; il server non ne ha bisogno,
|
||||
perché l'immagine installa le proprie dipendenze (da `requirements.lock`).
|
||||
|
||||
```bash
|
||||
python3 -m venv .venv
|
||||
@@ -120,9 +118,9 @@ pip install -e ".[dev]"
|
||||
|
||||
## 5. Applicare le migrazioni del database
|
||||
|
||||
- **Locale/venv**: `alembic upgrade head`
|
||||
- **Docker**: le migrazioni vengono eseguite automaticamente all'avvio del
|
||||
container (vedi il `CMD` del `Dockerfile`) — nessun passaggio manuale.
|
||||
Le migrazioni vengono eseguite automaticamente all'avvio del container (vedi il
|
||||
`CMD` del `Dockerfile`) — nessun passaggio manuale, e `alembic upgrade head` non
|
||||
si lancia mai a mano.
|
||||
|
||||
## 6. Impostare l'indirizzo delle fee
|
||||
|
||||
|
||||
+4
-26
@@ -104,11 +104,7 @@ Restano **accettati**, e descritti in
|
||||
| Hash delle password | Tabella `users` (SQLite, `./data/db/`) | Argon2id | Attacco offline a dizionario, rallentato dai parametri Argon2 | — | Reset dall'operatore |
|
||||
| JWT dell'utente | Browser, `localStorage` (`plm_token`) | Emesso al login/registrazione | Sessione dell'utente fino alla scadenza (24h) o al cambio password | — | Cambio password o reset dall'operatore |
|
||||
|
||||
*Oggi* (B-91): la chiave di cifratura è la variabile d'ambiente
|
||||
`XPRV_ENCRYPTION_KEY` in `.env`, visibile a chiunque esegua `docker inspect`
|
||||
sul container; `.env` e i file di chiave hanno i permessi predefiniti del
|
||||
sistema (di norma `0644`, leggibili da tutti).
|
||||
`JWT_SECRET` e `ADMIN_TOKEN` restano variabili d'ambiente anche dopo B-91: le
|
||||
`JWT_SECRET` e `ADMIN_TOKEN` restano variabili d'ambiente (B-91): le
|
||||
vede con `docker inspect` solo chi ha accesso a Docker, che è già fidato come
|
||||
l'host.
|
||||
|
||||
@@ -118,10 +114,6 @@ l'host.
|
||||
`master.xprv.enc`, i database `*.db`, `data/` e `logs/` sono esclusi sia da
|
||||
git sia dall'immagine Docker.
|
||||
*Codice:* `app/config.py:Settings`, `.gitignore`, `.dockerignore`
|
||||
*Stato:* da implementare (B-91), per la chiave in un file e l'esclusione di
|
||||
`secrets/` · *Oggi:* anche la chiave di cifratura sta in `.env`
|
||||
(`XPRV_ENCRYPTION_KEY`), e né `.gitignore` né `.dockerignore` escludono
|
||||
`secrets/`.
|
||||
|
||||
- **[SEC-03]** Tutti i parametri operativi stanno nel database
|
||||
(`round_config`); in `.env` restano solo segreti, collegamenti
|
||||
@@ -131,7 +123,6 @@ l'host.
|
||||
riavvio. L'elenco completo è in
|
||||
[architettura.md](architettura.md#configurazione).
|
||||
*Codice:* `app/config.py:Settings`
|
||||
*Stato:* da implementare (B-91), per `XPRV_ENCRYPTION_KEY_FILE`.
|
||||
|
||||
`.env.example` documenta le chiavi necessarie e il comando per generarle;
|
||||
non elenca `DATABASE_URL` né `MASTER_KEY_PATH`, che in Docker sono comunque
|
||||
@@ -204,10 +195,7 @@ sì che un backup dei dati non contenga entrambe.
|
||||
container, quindi `docker inspect` non la mostra, e non finisce in un
|
||||
backup di `./data/`.
|
||||
*Codice:* `app/config.py:Settings`, `app/wallet/keystore.py:decrypt_xprv`,
|
||||
`docker-compose.yml` · *Test:* —
|
||||
*Stato:* da implementare (B-91) · *Oggi:* la chiave è la variabile
|
||||
`XPRV_ENCRYPTION_KEY` di `.env`, passata al container con `env_file` e
|
||||
visibile con `docker inspect`.
|
||||
`docker-compose.yml` · *Test:* `tests/unit/test_container_hardening.py`, `tests/unit/test_config.py`
|
||||
|
||||
### Derivazione
|
||||
|
||||
@@ -265,8 +253,6 @@ riserva è [ELC-19](connessione-electrum.md#1-rotazione-tra-server) (vedi
|
||||
reali.
|
||||
*Codice:* `app/config.py:validate_runtime_secrets`, `app/main.py:lifespan` ·
|
||||
*Test:* `tests/unit/test_config.py`
|
||||
*Stato:* da implementare (B-91), per la chiave letta da file · *Oggi:* si
|
||||
controlla che la variabile `XPRV_ENCRYPTION_KEY` non sia vuota.
|
||||
|
||||
- **[SEC-13]** Un `ADMIN_TOKEN` vuoto **non** blocca l'avvio: il pannello
|
||||
risulta semplicemente chiuso (vedi [SEC-24]).
|
||||
@@ -628,11 +614,7 @@ entra nell'immagine.
|
||||
`0700`; `.env`, `master.xprv.enc` e `./secrets/xprv_encryption.key` hanno
|
||||
`0600`; tutti appartengono all'utente con cui gira il container. I file che
|
||||
l'app crea (database, backup, log) nascono con permessi `0600`.
|
||||
*Codice:* `Dockerfile`, `docker-compose.yml` · *Test:* —
|
||||
*Stato:* da implementare (B-91) · *Oggi:* il container gira come root, i
|
||||
file che scrive in `./data/` appartengono a root sull'host, e `.env` e i
|
||||
file di chiave hanno i permessi predefiniti del sistema (di norma `0644`,
|
||||
leggibili da ogni utente dell'host).
|
||||
*Codice:* `Dockerfile`, `docker-compose.yml` · *Test:* `tests/unit/test_container_hardening.py`
|
||||
|
||||
- **[SEC-52]** Ciò che entra nelle immagini è fissato: le immagini di base
|
||||
(`python:3.12-slim` per `app`, `caddy:2-alpine` per `caddy`) sono indicate
|
||||
@@ -640,11 +622,7 @@ entra nell'immagine.
|
||||
di lock con l'hash di ogni pacchetto (`pip install --require-hashes`), non
|
||||
dagli intervalli `>=` di `pyproject.toml`. Aggiornare una dipendenza o
|
||||
un'immagine significa rigenerare il lock o cambiare il digest in un commit.
|
||||
*Codice:* `Dockerfile`, `docker-compose.yml`, `pyproject.toml` · *Test:* —
|
||||
*Stato:* da implementare (B-91) · *Oggi:* le immagini di base sono
|
||||
indicate per etichetta mobile e le dipendenze con `>=`: ogni build può
|
||||
installare versioni diverse, comprese versioni compromesse pubblicate dopo
|
||||
l'ultima build.
|
||||
*Codice:* `Dockerfile`, `docker-compose.yml`, `requirements.lock` · *Test:* `tests/unit/test_container_hardening.py`
|
||||
|
||||
La chiave di cifratura montata come file, e non come variabile d'ambiente, è
|
||||
[SEC-53]. Le procedure che scrivono in `./data/` devono rispettarne il
|
||||
|
||||
+7
-8
@@ -193,8 +193,8 @@ limiti accettati: lo stato `drawing` non riprende dopo un riavvio (vedi
|
||||
| B-68 | CLAUDE.md e README descrivevano uno stato che il codice aveva superato | Bassa | `162ceed` Describe the code the docs actually ship with | `test_docs_current.py` | ARC |
|
||||
| B-69 | Commenti nel codice non più veri | Bassa | `666cb1a` Retire in-code comments that outlived what they described | `test_code_comments.py` | ARC |
|
||||
| B-70 | Il diagramma diceva che prelievo e scommessa in corso non potevano coesistere, ma il codice non lo impone (e non deve) | Bassa | `526a649` Say what bets and withdrawals actually exclude | `test_withdrawals.py::test_withdrawal_is_allowed_while_a_bet_is_still_unconfirmed`, `::test_the_docs_no_longer_claim_bets_and_withdrawals_are_exclusive` | WDR |
|
||||
| B-71 | `.env` punta `MASTER_KEY_PATH` a una seconda copia della chiave master (e `DATABASE_URL` a un secondo database) invece che ai file in `./data/` letti dal container | Bassa | **aperto** | — | OPS, SEC |
|
||||
| B-72 | `docs/setup.md` presenta ancora l'installazione come «in locale o via Docker», contro la regola «solo Docker» | Bassa | **aperto** | — | OPS |
|
||||
| B-71 | `.env` punta `MASTER_KEY_PATH` a una seconda copia della chiave master (e `DATABASE_URL` a un secondo database) invece che ai file in `./data/` letti dal container | Bassa | corretto con l'allineamento dei file e B-89 | `test_master_key_check.py` | OPS, SEC |
|
||||
| B-72 | `docs/setup.md` presenta ancora l'installazione come «in locale o via Docker», contro la regola «solo Docker» | Bassa | *da registrare* | — (documentazione) | OPS |
|
||||
|
||||
Note:
|
||||
|
||||
@@ -239,7 +239,7 @@ cifrava di nuovo il file sbagliato (vedi [operativita.md](operativita.md)).
|
||||
| B-88 | Le transazioni SQLite non sono quelle che il codice presuppone; `synchronous=NORMAL` può perdere gli ultimi commit | Alta | *da registrare* | `test_sqlite_transactions.py`, `test_db_base.py` | DAT, ARC |
|
||||
| B-89 | Problemi della chiave rilevati solo al primo uso; gli script accettano qualunque xprv e scrivono file leggibili da tutti | Alta | *da registrare* | `test_master_key_check.py` | SEC, OPS |
|
||||
| B-90 | Il ripristino di un backup riapre round già pagati e riassegna indici di derivazione | Alta | *da registrare* | `test_restore_gap.py` | OPS, REG |
|
||||
| B-91 | Container eseguito come root, permessi larghi, dipendenze e immagini non fissate | Alta | **aperto** | — | SEC, OPS |
|
||||
| B-91 | Container eseguito come root, permessi larghi, dipendenze e immagini non fissate | Alta | *da registrare* | `test_container_hardening.py`, `test_config.py` | SEC, OPS |
|
||||
| B-92 | Nessuna verifica di salute o prontezza, stato Electrum invisibile, audit non filtrabile, log incompleti | Media | **aperto** | — | OPS, AUD, API |
|
||||
| B-93 | Deploy senza arresto ordinato, rollback impossibile, `SITE_ADDRESS` che torna a `localhost` | Media | **aperto** | — | OPS |
|
||||
| B-94 | Cambio password, login e bug report senza limiti adeguati; errori non strutturati in alcune risposte | Media | **aperto** | — | SEC, API |
|
||||
@@ -252,11 +252,10 @@ cifrava di nuovo il file sbagliato (vedi [operativita.md](operativita.md)).
|
||||
|
||||
## Stato attuale
|
||||
|
||||
- **Corretti:** B-01 … B-70, tranne B-16, che è chiuso nella sostanza (il
|
||||
- **Corretti:** B-01 … B-72, tranne B-16, che è chiuso nella sostanza (il
|
||||
crash è eliminato e `/guida` serve un segnaposto) ma lascia la guida vera
|
||||
come limite accettato; dalla revisione del 2026-09-28: B-88, B-75, B-97, B-80, B-84, B-76, B-77, B-78, B-83, B-74, B-85, B-86, B-73, B-81, B-79, B-82, B-87, B-89, B-90, B-95, B-98, B-99, B-100.
|
||||
- **Aperti:** B-71, B-72 (gravità bassa, operativi o di documentazione) e
|
||||
B-73 … B-98 dalla revisione del 2026-09-28 tranne i corretti (0 critici,
|
||||
3 alti, 5 medi), da implementare nell'ordine di
|
||||
come limite accettato; dalla revisione del 2026-09-28: B-88, B-75, B-97, B-80, B-84, B-76, B-77, B-78, B-83, B-74, B-85, B-86, B-73, B-81, B-79, B-82, B-87, B-89, B-90, B-91, B-95, B-98, B-99, B-100.
|
||||
- **Aperti:** dalla revisione del 2026-09-28, B-92, B-93, B-94 e B-96 (0 critici,
|
||||
0 alti, 4 medi), da implementare nell'ordine di
|
||||
[piano-correzioni.md](piano-correzioni.md).
|
||||
- **Prossimo numero libero:** `B-101`.
|
||||
Reference in new issue
Block a user