Files
InsanityLab-web/.superpowers/sdd/task-15-report.md
T
Adriano b913a2bf90 registri dei piani in git (AI-OS#309)
Refs gitea:Adriano/AI-OS#310

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-06 10:36:26 +02:00

4.9 KiB

Task 15 — Area admin: editor Tiptap, API CRUD, upload immagini

Data: 2026-07-02 Commit: f0ad162f742179e5154dd0e011345b35f093f119

Il Task 15 introduce l'area di amministrazione del sito InsanityLab: layout admin dedicato, pagine di login/logout, lista articoli con eliminazione, form di creazione e modifica con editor Tiptap, API REST protette per il CRUD dei post, endpoint di upload immagini con validazione e route di serving dei file caricati con protezione contro il path traversal. La sanitizzazione dell'HTML in ingresso è centralizzata in src/lib/sanitize.ts (basata su sanitize-html) ed è coperta da test scritti prima dell'implementazione, secondo il flusso TDD richiesto.

File creati

  • tests/sanitize.test.ts
  • src/lib/sanitize.ts
  • src/lib/post-body.ts
  • src/layouts/Admin.astro
  • src/pages/admin/login.astro, src/pages/admin/logout.ts, src/pages/admin/index.astro, src/pages/admin/new.astro, src/pages/admin/edit/[id].astro
  • src/components/admin/PostForm.astro
  • src/pages/api/admin/posts/index.ts, src/pages/api/admin/posts/[id].ts, src/pages/api/admin/upload.ts
  • src/pages/uploads/[...path].ts

Esito test (npm test)

Test Files  6 passed (6)
     Tests  28 passed (28)

28/28 test superati: 25 preesistenti (auth, contact, posts, rate-limit, slug) più i 3 nuovi test di sanitize. Il ciclo TDD è stato rispettato: il test è stato creato per primo, la sua esecuzione è fallita con Cannot find module '../src/lib/sanitize', quindi è stata scritta l'implementazione e i test sono passati al primo tentativo.

Verifica end-to-end (dev server su porta 4321)

# Check Atteso Ottenuto Esito
1 GET /admin senza autenticazione 302 302 ✓
2 POST /api/admin/posts senza autenticazione 401 401 ✓
3 Login POST /admin/login (utente e2e dedicato) 302 + cookie 302, cookie session impostato ✓
4 POST nuovo articolo 201 201, {"id":16,"slug":"test-e2e-task-15"} ✓
5 GET /blog/test-e2e-task-15 200 200 ✓
6 PUT aggiornamento articolo 200 200, {"ok":true} ✓
7 Upload PNG (10x10, generato via Python) 200 200, {"url":"/uploads/1782949642171-10ccb41e.png"} ✓
8 GET file caricato 200 200, Content-Type: image/png ✓
9 Path traversal /uploads/..%2f..%2fpackage.json 404 404 ✓
10 DELETE articolo 200 200, {"ok":true} ✓

Nota sul check 3: il dev server risponde 403 ai POST privi di header Origin per via della protezione CSRF integrata di Astro (security.checkOrigin). Con Origin: http://localhost:4321 il login funziona correttamente. Un browser reale invia sempre l'header, quindi il comportamento è quello atteso.

Nota sul check 9: la variante --path-as-is /uploads/../package.json restituiva 200 in modalità dev, ma la risposta proveniva dal middleware statico di Vite (headers ETag e Cache-Control: no-cache, assenti nella nostra route), che in sviluppo espone i file di progetto dopo la normalizzazione del path. La verifica è stata ripetuta sul server di produzione (npm run build + node --env-file=.env ./dist/server/entry.mjs): tutte le varianti di traversal (../, %2e%2e/, ..%2f) e l'accesso diretto a /package.json restituiscono 404. La route [...path].ts è quindi sicura; l'esposizione riguarda esclusivamente l'ambiente di sviluppo locale e non il deploy.

Build

npm run build completata senza errori: 54 asset generati, sitemap creata, server bundle prodotto in dist/server.

Adattamenti al test sanitize

Nessuno. sanitize-html 2.17.5 preserva l'HTML consentito esattamente com'è, quindi l'asserzione toBe(html) del primo test è rimasta invariata. Le asserzioni su <script>, handler inline e schema javascript: non sono state toccate.

Concern e note

  1. Esposizione file in dev: come descritto sopra, il dev server Vite serve i file della root del progetto (incluso package.json) dopo normalizzazione del path. Non riguarda la produzione, ma è bene non esporre mai il dev server su rete pubblica.
  2. Utente e2e: per i check è stato creato un utente temporaneo e2etest tramite scripts/create-user.mjs, poi rimosso dal database (sessioni incluse) al termine. L'utente admin preesistente non è stato toccato.
  3. Pulizia: il PNG di test caricato in uploads/ è stato eliminato; l'articolo di prova è stato rimosso tramite l'endpoint DELETE (check 10).
  4. Dev server Astro 7 come demone: astro dev in questa versione gira come demone (astro dev stop per fermarlo). Durante la verifica è stato trovato un demone preesistente sulla porta 4323, fermato e riavviato sulla 4321; al termine dei check il demone è stato fermato.
  5. getEnv('UPLOADS_DIR', 'uploads'): il fallback relativo uploads dipende dalla working directory del processo; il .env definisce già UPLOADS_DIR=uploads. In produzione conviene valutare un path assoluto.