Backend: difetti minori dalla revisione del 21/09 #36

Closed
opened 2026-09-21 20:14:54 +00:00 by Adriano · 3 comments
Owner

GRAVITÀ: bassa

  1. src/backend/services/report_service.py:203,258,340,428 ; ai_service.py:28-35 ; production_export_service.py:157 — chiamate bloccanti (kaleido, WeasyPrint, pdfplumber) dentro handler async: un PDF ferma il worker. Manca asyncio.to_thread. PRESUNTO nei tempi.
  2. src/backend/api/routers/files.py:124-165 ; settings.py:94,118 — tipo del file deciso dal content_type del client; x.html dichiarato image/png è servito come text/html; logo ammette SVG; dimensione controllata dopo aver letto tutto in memoria. Path traversal escluso.
  3. src/backend/api/routers/measurements.py:277 — export CSV controlla has_role a mano e ignora is_admin: admin senza ruoli → 403.
  4. src/backend/api/routers/users.py:89-90 — {"roles": null} o {"is_admin": null} → 500. Un admin può togliersi is_admin senza ritorno via API.
  5. src/backend/services/recipe_service.py:425 — il task di default di update_current_version non ha task_type="drawing" (la riga :293 sì): diventa "note".
  6. src/backend/api/middleware/logging.py:37,45 — ip_address è il proxy; action String(100) con path lunghi fa fallire l'insert in silenzio; INSERT+commit a ogni richiesta, tabella senza limite.
  7. src/backend/services/ai_service.py:64-74 — KeyError/JSONDecodeError sulla risposta del modello → 400 str(e) invece di 502; timeout 60 s senza retry.
  8. src/backend/services/measurement_service.py:210 ; production_service.py:40 — datetime.now() naive locale contro utcnow() di auth_service.py:74: fusi mescolati nello stesso DB.
    VERIFICATO: lettura del codice da parte del revisore; non rieseguiti.

ORIGINE: revisione fable del 21/09/2026 su ramo V3.0.0, commit 8cc5e82. Documento completo: _output/2026-09-21-TieMeasureFlow-revisione.md su Wasabi.

GRAVITÀ: bassa 1. src/backend/services/report_service.py:203,258,340,428 ; ai_service.py:28-35 ; production_export_service.py:157 — chiamate bloccanti (kaleido, WeasyPrint, pdfplumber) dentro handler async: un PDF ferma il worker. Manca `asyncio.to_thread`. PRESUNTO nei tempi. 2. src/backend/api/routers/files.py:124-165 ; settings.py:94,118 — tipo del file deciso dal `content_type` del client; `x.html` dichiarato image/png è servito come text/html; logo ammette SVG; dimensione controllata dopo aver letto tutto in memoria. Path traversal escluso. 3. src/backend/api/routers/measurements.py:277 — export CSV controlla `has_role` a mano e ignora `is_admin`: admin senza ruoli → 403. 4. src/backend/api/routers/users.py:89-90 — `{"roles": null}` o `{"is_admin": null}` → 500. Un admin può togliersi `is_admin` senza ritorno via API. 5. src/backend/services/recipe_service.py:425 — il task di default di `update_current_version` non ha `task_type="drawing"` (la riga :293 sì): diventa "note". 6. src/backend/api/middleware/logging.py:37,45 — `ip_address` è il proxy; `action` String(100) con path lunghi fa fallire l'insert in silenzio; INSERT+commit a ogni richiesta, tabella senza limite. 7. src/backend/services/ai_service.py:64-74 — KeyError/JSONDecodeError sulla risposta del modello → 400 `str(e)` invece di 502; timeout 60 s senza retry. 8. src/backend/services/measurement_service.py:210 ; production_service.py:40 — `datetime.now()` naive locale contro `utcnow()` di auth_service.py:74: fusi mescolati nello stesso DB. VERIFICATO: lettura del codice da parte del revisore; non rieseguiti. ORIGINE: revisione fable del 21/09/2026 su ramo `V3.0.0`, commit 8cc5e82. Documento completo: `_output/2026-09-21-TieMeasureFlow-revisione.md` su Wasabi.
Adriano added the bug label 2026-09-21 20:14:54 +00:00
Adriano added the assegnata:jeeg label 2026-09-21 20:23:29 +00:00
Author
Owner

⚡ jeeg — preso in carico 2026-09-21T20:23:29Z
ramo: lavoro/jeeg · capacità: docker, gpu, systemd

⚡ jeeg — **preso in carico** 2026-09-21T20:23:29Z ramo: `lavoro/jeeg` · capacità: docker, gpu, systemd
Author
Owner

ANALISI (passo 1) — ramo lavoro/jeeg, worktree ~/lavoro/TieMeasureFlow su jeeg, lotto A3

MODIFICHE, una per punto dell'issue:

  1. src/backend/services/report_service.py, ai_service.py, production_export_service.py: avvolgi le chiamate bloccanti (pio.to_image, write_pdf, pdfplumber, scrittura file grande) in await asyncio.to_thread(...).
  2. src/backend/api/routers/files.py + settings.py (logo): verifica il tipo dai magic bytes (PNG, JPEG, GIF, WebP, PDF; per il logo anche SVG solo se già ammesso → in tal caso servilo con Content-Disposition: attachment o rifiutalo: scegli RIFIUTO di SVG) e vincola l'estensione al tipo rilevato; leggi a blocchi e interrompi oltre il limite di dimensione.
  3. src/backend/api/routers/measurements.py:277: usa la dipendenza require_role standard (ammette admin).
  4. src/backend/api/routers/users.py:89-90: roles/is_admin null → 422; un admin non può togliere is_admin a se stesso né all'ultimo admin → 409.
  5. src/backend/services/recipe_service.py:425: task_type="drawing" come alla riga :293.
  6. src/backend/api/middleware/logging.py: tronca action a 100 caratteri; ip_address dallo stesso _client_ip del rate limit (importa la funzione; il lotto A2 la modifica: usa la firma _client_ip(request)).
  7. src/backend/services/ai_service.py: KeyError/JSONDecodeError/risposta malformata del modello → eccezione dedicata che il router traduce in 502; il router (recipes.py parse-technical-sheet) va toccato solo per questa traduzione.
  8. NON uniformare i datetime in questa issue (richiede migrazione dati): scrivi nel rapporto MANCA.
    TEST: uno per punto 2, 3, 4, 5, 7.
    NON TOCCARE: rate_limit.py, auth_service.py, setup.py (lotto A2); tasks.py, measurement_service.py, production*.py (lotto A1).
ANALISI (passo 1) — ramo `lavoro/jeeg`, worktree `~/lavoro/TieMeasureFlow` su jeeg, lotto A3 MODIFICHE, una per punto dell'issue: 1. src/backend/services/report_service.py, ai_service.py, production_export_service.py: avvolgi le chiamate bloccanti (pio.to_image, write_pdf, pdfplumber, scrittura file grande) in `await asyncio.to_thread(...)`. 2. src/backend/api/routers/files.py + settings.py (logo): verifica il tipo dai magic bytes (PNG, JPEG, GIF, WebP, PDF; per il logo anche SVG solo se già ammesso → in tal caso servilo con `Content-Disposition: attachment` o rifiutalo: scegli RIFIUTO di SVG) e vincola l'estensione al tipo rilevato; leggi a blocchi e interrompi oltre il limite di dimensione. 3. src/backend/api/routers/measurements.py:277: usa la dipendenza `require_role` standard (ammette admin). 4. src/backend/api/routers/users.py:89-90: `roles`/`is_admin` null → 422; un admin non può togliere `is_admin` a se stesso né all'ultimo admin → 409. 5. src/backend/services/recipe_service.py:425: `task_type="drawing"` come alla riga :293. 6. src/backend/api/middleware/logging.py: tronca `action` a 100 caratteri; `ip_address` dallo stesso `_client_ip` del rate limit (importa la funzione; il lotto A2 la modifica: usa la firma `_client_ip(request)`). 7. src/backend/services/ai_service.py: KeyError/JSONDecodeError/risposta malformata del modello → eccezione dedicata che il router traduce in 502; il router (recipes.py parse-technical-sheet) va toccato solo per questa traduzione. 8. NON uniformare i datetime in questa issue (richiede migrazione dati): scrivi nel rapporto MANCA. TEST: uno per punto 2, 3, 4, 5, 7. NON TOCCARE: rate_limit.py, auth_service.py, setup.py (lotto A2); tasks.py, measurement_service.py, production*.py (lotto A1).
Author
Owner

Soluzione

ESITO: fatto
COSA: Punti 1-7 fatti: chiamate bloccanti in asyncio.to_thread; tipo file dai magic bytes con estensione vincolata, SVG rifiutato, lettura a blocchi; export CSV ammette admin; roles/is_admin null → 422 e ultimo admin protetto (409); task di default drawing; log accessi con action troncata e IP reale; errori del modello AI → 502. Punto 8 (datetime): issue nuova.
COMMIT (ramo lavoro/jeeg, unito su V3.0.0 a d483fa1): 805f41c; 3e99b64; 403c923; 5ca3dc1; 302903a; 1d5f245; 3f70f99
PROVA: uv run --extra server --extra client --extra dev pytest -q → 622 passed, 4 skipped (erano 391); visione su Python 3.13 → 16 passed (erano 8).

**Soluzione** ESITO: fatto COSA: Punti 1-7 fatti: chiamate bloccanti in `asyncio.to_thread`; tipo file dai magic bytes con estensione vincolata, SVG rifiutato, lettura a blocchi; export CSV ammette admin; roles/is_admin null → 422 e ultimo admin protetto (409); task di default `drawing`; log accessi con action troncata e IP reale; errori del modello AI → 502. Punto 8 (datetime): issue nuova. COMMIT (ramo lavoro/jeeg, unito su V3.0.0 a d483fa1): 805f41c; 3e99b64; 403c923; 5ca3dc1; 302903a; 1d5f245; 3f70f99 PROVA: `uv run --extra server --extra client --extra dev pytest -q` → 622 passed, 4 skipped (erano 391); visione su Python 3.13 → 16 passed (erano 8).
Adriano removed the assegnata:jeeg label 2026-09-21 21:56:44 +00:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: Adriano/TieMeasureFlow#36