cer-motore è un motore di calcolo puro. Non apre socket, non fa richieste di rete, non
esegue un server, non parla con un database, non ha un'interfaccia web, non gestisce
autenticazione né sessioni, non ha dipendenze di runtime oltre alla libreria standard di
Python. Le funzioni di calcolo non toccano nemmeno il disco: l'I/O vive in tre posti soli
(mock.py, __main__.py e l'adapter regole.leggi).
La superficie di attacco classica, di conseguenza, è quasi nulla — e sarebbe disonesto scrivere qui il modello di SECURITY.md che si trova ovunque, con la matrice delle versioni supportate e la promessa di una patch entro 90 giorni.
La vulnerabilità vera di questo progetto è il calcolo sbagliato. Questo codice ripartisce denaro fra i soci di una comunità energetica: un errore in una formula, in un arrotondamento o nell'interpretazione di una regola GSE non fa crashare nulla e non lascia tracce nei log. Sposta soldi da un socio a un altro, mese dopo mese, e nessuno se ne accorge finché qualcuno non rifà i conti a mano. È questo che chiediamo di segnalare, ed è questo che trattiamo con l'urgenza che altrove si riserva a una RCE.
Il 7 agosto 2026 una verifica pagina per pagina sul PDF ufficiale delle Regole Operative ha
trovato che scomponi_eccedentario calcolava la quota eccedentaria come
(rapporto − soglia) / rapporto, cioè come frazione di energia condivisa oltre la soglia,
mentre le Regole Operative (pag. 42) la definiscono come differenza in punti percentuali
fra il rapporto energia condivisa / energia immessa e il valore soglia:
% E_ACI,ecc,j,n = max[0 ; (E_ACI,j,n / E_immessa,j,n * 100)% − valore soglia]
Con rapporto 0,60 e soglia 0,55 la norma dà il 5% del contributo, la vecchia formula ne dava l'8,33%. La sovrastima arrivava al +79% a rapporto 0,56, cioè proprio appena sopra la soglia, dove ricadono le configurazioni realistiche, e restava dell'11% anche a rapporto 0,90. L'importo eccedentario è quello che va destinato ai soli consumatori diversi dalle imprese e alle finalità sociali: l'errore toglieva denaro agli altri membri per darlo a quella categoria, in silenzio, con l'invariante contabile "la somma delle quote è il totale" perfettamente soddisfatto per tutto il tempo.
Tre test "risolti a mano" pinnavano i valori sbagliati, perché erano stati derivati dalla
stessa lettura errata della formula. Nessun test è fallito. Nessuna eccezione è stata
sollevata. La correzione, e i tre test ricalcolati, sono al punto 2 di
docs/ROADMAP.md.
Un secondo caso, dello stesso genere, è documentato nelle guardie di
ripartizione._valida_eccedentario: passare la soglia come 55 invece di 0.55 —
percentuale invece di frazione, il refuso più naturale del mondo — azzerava l'importo
eccedentario senza sollevare nulla e senza produrre numeri assurdi. Semplicemente non
pagava i consumatori non-imprese. Oggi solleva ValueError con un messaggio che spiega il
perché, ma è stato scoperto per fortuna, non da un test.
Sono i benvenuti, e sono trattati come priorità:
- Errori di formula: la funzione non implementa quello che la norma prescrive.
- Errori di interpretazione normativa: la formula è implementata correttamente ma la lettura del testo GSE, ARERA o MASE è discutibile o sbagliata.
- Errori di arrotondamento: centesimi che si creano o si perdono, invarianti di somma che non chiudono, incoerenze fra intestazione e tabella di un rendiconto.
- Parametri normativi obsoleti o errati in
tariffe.py: soglie, tariffe base, cap, correttivi zonali, valore TIAD. Le Regole Operative sono già state aggiornate tre volte. - Casi limite che producono un risultato silenziosamente sbagliato: valori scambiati fra argomenti, unità di misura confuse, percentuali contro frazioni, tipi che attraversano una guardia che credevi li fermasse.
- Regole marcate
[verificato]indocs/FORMULE.mdche verificate non sono. Quella marcatura è una promessa, e va contestata se è falsa.
Sono benvenuti, con meno urgenza, anche i problemi tradizionali: input malformati che fanno
sollevare un'eccezione non gestita o consumare risorse in modo irragionevole nei due punti che
leggono file scritti da qualcun altro: l'adapter regole.leggi, che interpreta lo statuto in
TOML, e mock.carica, che interpreta i CSV delle misure ed è il codice destinato a diventare
l'adapter dell'export GSE reale.
Per evitare a entrambi di perdere tempo:
- Non c'è nulla da esfiltrare. Nessuna credenziale, nessun token, nessun segreto in
questo repository, e nessun dato personale: i dati sono generati da
mock.pycon un seme fisso, e i nomi dei membri sono etichette inventate. - Non c'è nulla da compromettere in esecuzione. Nessun server, nessuna porta, nessuna
deserializzazione di dati remoti, nessun
eval, nessuna chiamata a subprocess. - Nessuna dipendenza di runtime, quindi nessun avviso di CVE transitivo.
pytestserve solo a chi sviluppa e non entra in nulla che venga distribuito. - La demo scrive su disco, di proposito:
python -m cer_motorecrea./data/nella directory corrente. È documentato, è la sua cartella usa-e-getta ed è ignorata da git. regole.leggilegge un file TOML indicato dall'utente, contomllibdella libreria standard. Un file di statuto è un documento che chi lo esegue ha scritto o approvato: non è input ostile, e il modello non prevede di eseguire statuti di terzi non fidati.- Il fatto che il motore non sia certificato dal GSE non è una vulnerabilità, è lo stato dichiarato del progetto. Vedi la sezione qui sotto.
- Le scelte marcate
[modellazione]indocs/FORMULE.md— per esempio il criterio di attribuzione dell'energia condivisa ai singoli impianti, che la norma non prescrive — non sono errori. Discutibili sì, e la discussione è benvenuta come issue: ma sono scelte documentate, non bug.
Apri una issue pubblica su https://github.com/nqwrc/cer-motore/issues. È il canale giusto per praticamente tutto quello che riguarda questo progetto: un errore di calcolo in un motore open source non è un segreto da custodire finché non c'è una patch, è un avviso che chiunque lo stia usando ha bisogno di leggere subito, perché nel frattempo sta producendo rendiconti.
Se ritieni davvero che una segnalazione non debba essere pubblica — per esempio perché sai che una CER sta già ripartendo denaro con la formula sbagliata e vuoi darle il tempo di fermarsi — usa la segnalazione privata di GitHub: scheda Security del repository, pulsante Report a vulnerability. È attiva.
Non c'è comunque alcun obbligo di riservatezza a senso unico: se preferisci pubblicare la tua analisi per conto tuo, fallo pure. E se il canale privato non funzionasse, apri una issue che dica soltanto che hai qualcosa da segnalare, senza i dettagli.
Una segnalazione utile su un errore di calcolo contiene tre cose. Con queste, il problema si riproduce e si chiude; senza, diventa una conversazione lunga.
- La formula. Quella implementata e quella che secondo te è corretta, scritte per esteso. Se il disaccordo è sull'interpretazione, di' esattamente dove le due letture divergono.
- La fonte, con il numero di pagina. Documento (Regole Operative CACER, DM CACER
414/2023, delibera ARERA, ...), paragrafo e pagina. Per le Regole Operative si cita il
numero stampato a piè di pagina, non quello dell'indice del lettore PDF: i due sono
sfasati di 1 (l'Appendice B è a pag. stampata 160 = pag. 161 del lettore). Il PDF è
pubblico e interamente estraibile: link e hash sha256 in fondo a
docs/FORMULE.md. Una citazione verbatim vale più di una parafrasi. - Un caso numerico che diverge. Gli input esatti, il risultato che dà il motore, il
risultato che dovrebbe dare, e il calcolo passo per passo che porta al secondo. Idealmente
nella forma di un test: guarda
test_scomponi_eccedentario_insiemi_caso_a_manointests/test_ripartizione.pyper il formato che usiamo. Non derivare il valore atteso dall'output del codice — è esattamente il modo in cui l'errore del 7 agosto 2026 è sopravvissuto ai suoi test.
Se hai solo uno dei tre pezzi, segnala comunque: un dubbio circostanziato su una formula è più utile del silenzio. Ma dillo, così sappiamo cosa manca.
Va detto senza giri di parole, perché è la premessa di tutto il resto:
cer-motoreè uno spike su dati mock, in fase pre-v0.1, e non va usato per riparti reali.
Il formato dell'export GSE reale dall'area clienti non è ancora stato osservato: i dati in
ingresso sono generati da mock.py secondo un'assunzione documentata in
docs/MOCK-GSE.md. Le formule sono verificate verbatim sui documenti
ufficiali, ma la verifica del vincolo eccedentario è applicata al periodo di calcolo mentre
il GSE la esegue a conguaglio su base annuale. Nel cumulo con il contributo in conto
capitale la partizione dell'energia esente dal fattore F è implementata, con due avvertenze
che restano in capo al chiamante (docs/FORMULE.md, sezione 2-bis): il
criterio pro-quota oraria con cui l'energia condivisa si attribuisce ai punti di prelievo è
una scelta di modellazione dichiarata, non una prescrizione normativa; e la classificazione
dei POD nelle cinque categorie esenti è un fatto giuridico sul titolare, non deducibile
dalle misure. La terza — la completezza dell'elenco dei prelievi — non è più affidata alla
sola buona fede del chiamante: dal 22 agosto 2026 un perimetro troncato che porti l'energia
condivisa di un'ora sopra il prelievo totale della stessa ora è rifiutato, perché la norma
lo rende impossibile (EC = min(E_immessa; E_prelevata), sezione 1). Sui dati mock il
silenzio valeva, a seconda del punto di prelievo mancante, dal 5% di tariffa premio mai
decurtata al 13% tolto a chi la norma esentava. Non è una verifica della completezza,
che il motore non può fare: un troncamento che non porti mai l'energia condivisa oraria
sopra il prelievo totale della stessa ora passa ancora in silenzio. Il perimetro resta
una responsabilità del chiamante; quello che è cambiato è che l'errore più grosso ora si
manifesta invece di restare muto.
Punti aperti aggiornati in docs/ROADMAP.md.
Il progetto non è approvato, validato o certificato dal GSE, da ARERA o da alcuna autorità: è un'implementazione indipendente, pubblicata perché sia possibile controllarla. Chi lo usa per prendere decisioni che riguardano denaro di altre persone lo fa sotto la propria responsabilità, come dice la licenza MIT, e dovrebbe rifare i conti a mano prima.
Una sola: main. Non ci sono ancora release pubblicate e non esistono branch di
manutenzione. Una correzione arriva su main e viene annotata nel
CHANGELOG.md; se sposta denaro, l'entità dello spostamento viene dichiarata
lì con un numero, non con un aggettivo.