diff --git a/AGENTS.md b/AGENTS.md index e00478f..89defae 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,295 +1,236 @@ # AGENTS.md - Development Guidelines -This file contains essential information for AI agents working on this codebase. +Essential information for AI agents and developers working on this codebase. ## Project Overview -PHP website for "Harmonie de Montpellier-Jacou" (music band) with a Vue.js 2 frontend for score management. -- **Backend**: PHP with MySQL (legacy codebase) -- **Frontend**: Vue.js 2 + Bootstrap Vue (in `frontend/score/`) -- **API**: RESTful PHP API in `api/` directory +Web application for "Harmonie de Montpellier-Jacou" (music band) to manage and share sheet music (partitions). -## Build Commands +- **Frontend**: SvelteKit (Svelte 5, runes) + Tailwind CSS in `partitions/` +- **Backend**: PHP 8.5 REST API in `api/` (no framework) +- **Storage**: flat files under `legacy/Scores/` (no database) +- **Auth**: JWT (HS256), see `api/lib/Auth.php` +- **Production**: `https://partitions.c.nadal-fr.com` (front) + `https://ohmj-api.c.nadal-fr.com` (API) -### Frontend (Vue.js) -```bash -cd frontend/score -npm install -npm run serve # Development server -npm run build # Production build -npm run lint # ESLint check -``` +There is **no MySQL** and **no Vue.js** in the active codebase. The old Vue.js 2 / MySQL +site is archived in `legacy/` and must not be modified. -### PHP -No build step required. Deploy to PHP-enabled web server. +## Stack & Versions -## Deployment +- Node.js 26 / npm 11 +- Svelte 5, SvelteKit 2, `@sveltejs/adapter-static`, Vite 5, Tailwind 3 +- PHP 8.5 (CLI built-in server in development) +- Dependencies: `axios`, `pdfjs-dist`, `lucide-svelte` (front); `setasign/fpdi`, `setasign/fpdf`, `tecnickcom/tcpdf` (back) -### Create Deploy ZIP -```bash -cd /home/jbnadal/sources/jb/ohmj/ohmj2 -./deploy/deploy.sh -``` +> PHP 8.5 note: nullable parameters MUST use explicit `?type` syntax +> (e.g. `?string $x = null`). Implicit nullable is deprecated and pollutes API output. -**Important:** -- Do NOT include `legacy/` folder in the zip -- Do NOT include `tests.php` in the zip -- The `.env` file is included with JWT_SECRET -- Frontend build must be in `frontend/` directory +## Build & Run Commands -## Testing - -### PHP API Tests - -Run functional tests for the PHP API: +### Backend (PHP) ```bash cd api +php -c php-upload.ini -S localhost:8000 router.php +``` + +- `-c php-upload.ini` is **required** so PDF uploads work (`upload_max_filesize`/`post_max_size` = 64M). + `ini_set()` cannot change those directives at runtime. +- API base URL: `http://localhost:8000` (paths have **no** `/api` prefix in the client). +- Copy `api/.env.example` to `api/.env` and set a strong `JWT_SECRET`. + +### Frontend (SvelteKit) + +```bash +cd partitions +npm install +npm run dev # http://localhost:5173 +npm run build # outputs to partitions/build +npm run preview +``` + +- `frontend` is a symlink to `partitions/build` (the deployed static site). +- No ESLint / test framework is configured for the frontend. +- `VITE_API_URL` is defined but currently unused (base URL is chosen from `import.meta.env.DEV`). + +### Deploy + +```bash +./deploy/deploy.sh +``` + +- Do NOT include `legacy/` or `api/tests.php` in the deploy archive. +- `api/.env` (with `JWT_SECRET`) is part of the deployment. +- The frontend build must be present in `frontend/`. + +## Testing + +### PHP API tests + +```bash +cd api +php -c php-upload.ini -S localhost:8000 router.php & # if not already running +rm -f /tmp/rate_* 2>/dev/null # clear rate limiting first php tests.php ``` -The tests cover: -- **Auth**: Login, bad credentials, missing token, invalid token -- **Scores**: CRUD operations, error handling for non-existent resources -- **Create Score with Pieces**: Functional tests with pieces verification -- **Files**: Get files tree, delete file error handling +- Expected: **53/53 tests passed (100%)**. +- Covers: auth (login, bad credentials, missing/invalid token), scores CRUD, + create-score-with-pieces, files tree, path traversal, rate limiting, upload + validation, INI injection, security headers. +- `tests.php` is kept for development only and MUST NOT be shipped in the deploy zip. -**Note**: tests.php is kept in source for development but must NOT be included in deploy zip. +## Authentication -### Vue/Svelte +- Login: `POST /login` with `{"username":"admin","password":"password"}` (development). +- Token: JWT HS256 signed with `JWT_SECRET`, 1 hour expiry. +- API calls: send `Authorization: Bearer `. +- Token is stored in `localStorage` on the frontend and attached via an axios request interceptor. +- Roles: `admin` (full access) and `user` (read-only). Admin routes live under `/admin/...`. +- Frontend dev login page: http://localhost:5173 -No test framework configured for frontend. +## API Endpoints -Run single test (when configured): -```bash -# Example for Jest (not yet configured) -npm test -- --testNamePattern="test name" -``` +Public / all authenticated users: -## Code Style Guidelines +- `POST /login` +- `GET /scores` +- `GET /scores/:id` +- `GET /scores/:id/instruments[?piece=N]` +- `GET /pieces/:scoreId` +- `GET /download/:path` (token via `Authorization` header or `?token=`) -### PHP -- Use ` partitions/build # symlink used for deployment +├── deploy/deploy.sh # deploy archive builder +├── scripts/convert_final_v2.js # one-off filename converter +├── legacy/ # archived old site + Scores/ (do not modify) +└── _builds/ # temporary work files (gitignored) ``` -## Database +## Data Model (files) -MySQL database connection configured in `api/config/database.php`: -- Uses legacy `mysql_*` functions -- Database name: `ohmj2` -- Tables: `repertoire` (for scores) +``` +legacy/Scores/////.pdf +legacy/Scores//score.ini +``` -## Error Handling +`score.ini`: -- PHP: Uses `@` error suppression operator in some places -- Vue: Uses try/catch with console.log for errors -- Return HTTP 200 on success in API responses +```ini +[info] +name = Nom du morceau +compositor = Compositeur +ressource = https://... # optional external link -## Security Notes +[pieces] +count = 1 +``` -- API uses `Access-Control-Allow-Origin: *` (CORS enabled for all) -- SQL queries use string concatenation (vulnerable to SQL injection) -- Database credentials stored in plain text in `api/config/database.php` -- Sanitize user inputs before database queries +- `NUM` = score id (zero-padded, e.g. `102`). +- `PIECE` = movement/piece number (1 for single-piece scores). +- `INSTRUMENT` = trigram code (see below). +- `VERSION` = version number. +- `PARTIE` = part number. -## Git +Instrument codes: `dir`, `pic`, `flu`, `cla`, `clb`, `sax`, `sat`, `sab`, `coa`, +`cba`, `cor`, `trp`, `trb`, `tub`, `htb`, `bas`, `per`, `crn`, `eup`, `har`, `pia`, +plus `sup`/`par` for combined parts. -- No CI/CD configured -- Commit to main branch -- Standard `.gitignore` for Node.js projects - -## Dependencies - -### Frontend -- Vue 2.6.11 -- Bootstrap Vue 2.17.3 -- Axios 0.21.0 -- Vue CLI 4.5.0 +## Code Style ### PHP -- FPDF library for PDF generation -- MySQL extension (legacy) +- ` - ``` -- **Token format**: JWT (HS256) - see `api/lib/Auth.php` -- **Frontend**: Token stored in localStorage, auto-attached to API requests via axios interceptor -- **Test**: http://localhost:5173 - login with admin/password -- **API base URL**: http://localhost:8000/api/ - -### Starting the PHP Server - -**Important:** Always use the custom upload config for file uploads to work: -```bash -cd api -php -c php-upload.ini -S localhost:8000 router.php & -``` - -Le serveur doit être lancé avec `-c php-upload.ini` pour autoriser l'upload de fichiers PDF (64M max). - -### Upload de partitions - -Lors de l'upload d'un PDF, deux options sont disponibles : -- **Ajouter le numéro** : ajoute le numéro de partition dans le coin du PDF -- **Position** : choix entre "Gauche" ou "Droite" pour placer le numéro - -## Current Tech Stack (2024) - -- **Frontend**: SvelteKit (NOT Vue.js 2) in `/partitions/` -- **Backend**: PHP API in `/api/` -- **Scores storage**: `/legacy/Scores/` (directory-based, not MySQL) - -## Security Audit Commands - -When modifying backend or frontend code, run these security audits: - -### Backend Security Audit +Manual checks: ```bash -# 1. Start the PHP server -cd api -php -S localhost:8000 router.php & - -# 2. Clear rate limiting files (important!) -rm -f /tmp/rate_* 2>/dev/null - -# 3. Run all security tests -php tests.php - -# Expected: 50/50 tests passed (100%) -``` - -### Frontend Security Check - -```bash -cd partitions - -# 1. Check for security issues in dependencies -npm audit - -# 2. Build and check for CSP violations -npm run build - -# 3. Check that environment variables are configured -cp .env.example .env -# Edit .env and set VITE_API_URL to your backend URL -``` - -### Manual Security Verification - -```bash -# Test JWT authentication -curl -s http://localhost:8000/login \ - -X POST \ +# JWT login +curl -s http://localhost:8000/login -X POST \ -H "Content-Type: application/json" \ -d '{"username":"admin","password":"password"}' -# Test CORS headers -curl -s -I http://localhost:8000/scores \ - -H "Origin: https://evil.com" - -# Test directory traversal protection +# Directory traversal must NOT return 200 curl -s http://localhost:8000/download/../../../etc/passwd \ -H "Authorization: Bearer " -# Expected: 403 or 404 (not 200) -# Test security headers -curl -s -I http://localhost:8000/login \ - -X POST \ +# Security headers on any response +curl -s -I http://localhost:8000/login -X POST \ -H "Content-Type: application/json" \ -d '{"username":"admin","password":"password"}' -# Check for: X-Content-Type-Options, X-Frame-Options, X-XSS-Protection, HSTS ``` -### Environment Setup for Security +> Known security findings from the latest review (not yet fixed): static file +> passthrough in `api/router.php` exposes `.env`/`users.json`; `deleteScoreFile` +> lacks path containment; `api/.env` is tracked by git; JWT is standard base64 +> (not base64url) and expiry is optional; stored XSS via the `ressource` link. +> See the project issue tracker / recent review notes. -**Backend (api/.env):** -```bash -JWT_SECRET=your_very_long_random_secret_key_here -``` +## Temporary Files -**Frontend (partitions/.env):** -```bash -# Development -VITE_API_URL=http://localhost:8000 +- Put temporary scripts and outputs in `_builds/` (gitignored). +- `scripts/convert_final_v2.js` is the only committed script in `scripts/`. -# Production (use HTTPS!) -VITE_API_URL=https://api.yourdomain.com -``` +## Git -### Security Checklist Before Deployment - -- [ ] JWT_SECRET is set and strong (use: `openssl rand -base64 32`) -- [ ] CORS origins are restricted to your domain only -- [ ] HTTPS is enforced in production -- [ ] Rate limiting is active -- [ ] All 50 tests pass -- [ ] npm audit shows no critical vulnerabilities -- [ ] CSP headers are configured -- [ ] No secrets in code or git history +- Default branch: `master`. +- No CI/CD configured. +- Only commit when explicitly requested. diff --git a/PLAN.md b/PLAN.md deleted file mode 100644 index 14e36bd..0000000 --- a/PLAN.md +++ /dev/null @@ -1,530 +0,0 @@ -# Plan de développement - Application de partitions OHMJ - -## Structure finale du projet - -``` -/home/jbnadal/sources/jb/ohmj/ohmj2/ -├── legacy/ # Backup ancien code -│ ├── api/ # Ancien PHP legacy (MySQL) -│ ├── frontend/ # Ancien Vue.js 2 -│ ├── fpdf/ -│ ├── Imgs/ -│ ├── Scripts/ -│ ├── Textes/ -│ └── index.php -├── api/ # NOUVEAU backend PHP moderne -│ ├── index.php # Router / entry point API -│ ├── config/ -│ │ └── users.json # Users avec passwords hashés -│ └── lib/ -│ ├── Auth.php # JWT authentication -│ └── ScoreScanner.php # Lecture fichiers ini + scan partitions -├── partitions/ # NOUVEAU frontend SvelteKit -│ ├── src/ -│ │ ├── routes/ -│ │ │ ├── +page.svelte # Login page -│ │ │ ├── scores/+page.svelte # Liste des morceaux -│ │ │ ├── scores/[id]/+page.svelte # Détail partition -│ │ │ └── +layout.svelte # Layout avec auth guard -│ │ ├── lib/ -│ │ │ ├── api.ts # Client API -│ │ │ └── stores/auth.ts # Store JWT -│ │ └── app.html -│ ├── package.json -│ └── svelte.config.js -└── Scores/ # Répertoire partitions (hors git) - └── {id}/ - ├── score.ini - ├── cla/ - │ └── 1/ - │ └── *.pdf - └── ... -``` - -## Phase 1 : Backend PHP (api/) - -### 1.1 ScoreScanner.php ✓ (FAIT) -- [x] Classe pour scanner les partitions -- [x] Lecture des fichiers score.ini (supporte sections [info], [pieces]) -- [x] Scan récursif instruments → parties → fichiers PDF -- [x] Mapping codes instruments vers noms -- [x] Retourne `piece` pour chaque instrument -- [x] Parse les noms de fichiers : `part`, `key`, `clef`, `variant` -- [x] Retourne `ressource` si présent dans score.ini -- [x] Supporte les partitions multi-morceaux (ex: score 390) - -### 1.2 Auth.php -- [x] Classe JWT pour authentification -- [x] Génération token JWT -- [x] Vérification token JWT -- [x] Lecture users.json -- [x] Vérification password hashé (bcrypt) -- [ ] Tests unitaires Auth.php (PHP non installé) - -### 1.3 users.json -- [x] Fichier JSON avec structure users -- [ ] Passwords hashés avec bcrypt (password: password) -- [ ] Script pour ajouter/modifier users - -### 1.4 index.php (Router API) -- [x] CORS headers -- [x] POST /login -- [x] GET /scores (protégé JWT) -- [x] GET /scores/:id (protégé JWT) -- [x] GET /pieces/:scoreId (protégé JWT) -- [x] GET /download/:path (protégé JWT, stream PDF) -- [x] POST /admin/scores (protégé JWT, créer partition) -- [x] PUT /admin/scores/:id (protégé JWT, modifier partition) -- [x] DELETE /admin/scores/:id (protégé JWT, supprimer partition) -- [x] POST /admin/scores/:id/upload (protégé JWT, upload PDF) -- [ ] Gestion erreurs JSON -- [ ] Tests toutes les routes (PHP non installé) - -### 1.5 Tests API -- [ ] Tests unitaires Auth.php (PHP non installé) -- [ ] Tests intégration toutes les routes (PHP non installé) -- [ ] Tests authentification JWT (PHP non installé) - -## Phase 2 : Frontend SvelteKit (partitions/) - -### 2.1 Initialisation projet -- [ ] npm create svelte@latest partitions -- [ ] TypeScript + ESLint + Prettier -- [ ] npm install (axios, etc.) - -### 2.2 Configuration -- [ ] svelte.config.js (adapter static) -- [ ] API base URL config - -### 2.3 Stores -- [ ] lib/stores/auth.ts - - [ ] Store JWT token - - [ ] Store user info - - [ ] Persistance localStorage - -### 2.4 Client API -- [ ] lib/api.ts - - [ ] Instance axios avec baseURL - - [ ] Intercepteur pour ajouter header Authorization - - [ ] Méthodes : login(), getScores(), getScore(id), downloadFile(path) - -### 2.5 Routes - -#### Login (/) -- [ ] Formulaire : username, password -- [ ] Appel API login -- [ ] Stockage JWT -- [ ] Redirection vers /scores -- [ ] Gestion erreurs - -#### Liste des morceaux (/scores) -- [ ] Auth guard (redirect si pas connecté) -- [ ] Table/DataGrid avec : - - [ ] Numéro (ID) - - [ ] Nom du morceau - - [ ] Compositeur -- [ ] Tri possible -- [ ] Click → navigation /scores/:id -- [ ] Loading state - -#### Détail partition (/scores/[id]) -- [ ] Auth guard -- [ ] Header : - - [ ] Numéro - - [ ] Nom du morceau - - [ ] Compositeur - - [ ] Bouton retour -- [ ] Liste des instruments (grid ou accordion) : - - [ ] Nom de l'instrument - - [ ] Pour chaque partie : - - [ ] Numéro de partie - - [ ] Liste des fichiers PDF - - [ ] Bouton téléchargement par fichier -- [ ] Loading state - -#### Layout -- [ ] Auth guard global -- [ ] Header avec : - - [ ] Titre app - - [ ] User connecté - - [ ] Bouton logout -- [ ] Footer (optionnel) - -### 2.6 Styles -- [ ] CSS moderne (Tailwind ou CSS vanilla) -- [ ] Responsive (mobile-friendly) -- [ ] Thème harmonie (couleurs) - -## Phase 3 : Déploiement - -### 3.1 Build frontend -- [ ] npm run build -- [ ] Output dans dist/ - -### 3.2 Configuration serveur -- [ ] /partitions → contenu dist/ -- [ ] /api → PHP -- [ ] /legacy/Scores → accès aux PDFs -- [ ] .htaccess ou config nginx pour routing - -### 3.3 Sécurité -- [ ] HTTPS obligatoire -- [ ] JWT secret fort -- [ ] Protection download (vérif JWT) -- [ ] Headers sécurité - -## Notes techniques - -### Backend PHP -- Pas de base de données -- JWT pour auth stateless -- Lecture fichiers uniquement -- Stream PDF pour download - -### Frontend SvelteKit -- Svelte 5 (runes) -- TypeScript strict -- Pas de store complexe (juste auth) -- Client API simple - -### Contraintes -- Chemin partitions : ../legacy/Scores/ (sera déplacé après DL) -- Fichiers ini présents partout -- PDFs uniquement -- Auth obligatoire pour tout accès - -## Phase Préliminaire : Normalisation des données - -### 0.1 Structure des partitions (à valider) -- [ ] Analyser tous les instruments pour voir si le répertoire "1" est nécessaire -- [ ] Identifier les cas où il n'y a qu'une seule partie (supprimer le niveau "1" ?) -- [ ] Valider la structure finale avant codage - -### 0.2 Harmonisation des noms de fichiers ✓ -**Convention finale :** -- Format : `{instrument}[_{instrument2}][_{variante}][_{tonalité}]_{partie}[_clef].pdf` -- Exemples : - - `clarinette_sib_1.pdf` (clarinette en Si♭, partie 1) - - `clarinette_alto_mib_1.pdf` (clarinette alto en Mi♭, partie 1) - - `basse_et_contrebasse_sib_1.pdf` (partition pour 2 instruments) - - `cor_mib_1_et_2.pdf` (cor en Mi♭, parties 1 et 2 combinées) - - `euphonium_sib_2_clefa.pdf` (euphonium en Si♭, partie 2, clé de fa) -- Tout en minuscule, français, ASCII sans accents -- Tonalités : sib (Bb), mib (Eb), fa (F), do (C), etc. -- **Garder les sous-répertoires numérotés (1/, 2/, etc.)** pour distinguer les versions -- **Script :** `scripts/convert_final_v2.js` -- **Usage :** `node scripts/convert_final_v2.js /chemin/vers/Scores confirm` - -### 0.3 À propos des répertoires numérotés (1/, 2/, etc.) -**Découverte importante :** -- Le répertoire `1/` = version principale du morceau -- Le répertoire `2/` (ou plus) = alternative du même morceau -- Cela permet d'avoir plusieurs versions d'un même morceau dans le même ID -- Exemple : Score 210, dossier `cla/1/` et `cla/2/` contiennent des versions différentes -- **Action:** Le script de conversion doit utiliser le numéro du répertoire comme numéro de partie UNIQUEMENT pour les fichiers "version_X" - -### 0.4 À propos du répertoire "sup" -- Le répertoire `sup/` signifie "supérieur" ou "combiné" -- C'est une partition où deux instruments jouent sur la même partition -- Ex: baryton + tuba sur une même partition -- **Note API:** Ces partitions combinées doivent être gérées différemment dans l'interface (afficher les deux instruments) - -### 0.5 Conventions de nommage - Instruments -**Orthographe standardisée (français) :** -- Clarinette → clarinette -- Saxophone → saxophone (ou sax_baryton, sax_tenor, sax_alto, sax_soprano pour les variantes) -- Cor → cor -- Trompette → trompette -- Trombone → trombone -- Tuba → tuba (ou tuba_tenor pour tenor tuba) -- Basse → basse -- Contrebasse → contrebasse (UN SEUL MOT) -- Baryton → baryton -- Euphonium → euphonium -- Flute → flute (ou petite_flute, grande_flute) -- Hautbois → hautbois -- Basson → basson -- Contre-basson → contre_basson -- Bugle → bugle (inclut flugelhorn) -- Percussion → percussion (timbales, grosse_caisse, cymbale, caisse_claire, triangle, etc.) - -**Instrumentsperaussion reconnus :** -- timpani, glockenspiel, bongos, maracas, tambourine, vibraphone, xylophone -- woodblock, claves, batterie - -**Tonalités :** -- sib (Bb), mib (Eb), fa (F), do (ut), sol, re, mi, si, reb -- NOTE: "do" devient "ut" dans les noms de fichiers - -**Cas spéciaux gérés :** -- "default_cor" = instrument de substitution (ex: baryton_default_cor_2) -- Parties combinées avec & = format "instrument1_et_instrument2_info1_info2_partie" - -### 0.6 Structure du nom de fichier final -**Format :** `{instrument}[_{variante}][_{tonalité}][_{default}][_{instrument_subst}]_{partie}[_clef].pdf` - -**Exemples :** -- `clarinette_sib_1.pdf` -- `sax_baryton_mib_1.pdf` -- `cor_fa_1_et_2.pdf` -- `baryton_sib_default_cor_2.pdf` -- `sax_tenor_sib_1.pdf` -- `baryton_mib_2_clesol.pdf` -- `baryton_et_tuba_tenor_sib_clefa.pdf` (parties combinées sup) - -**Note:** Pour les parties combinées (sup), chaque instrument garde ses propres infos (tonalité, partie, clef) - -## Phase 3 : Déploiement - -### 3.1 Build frontend -- [ ] npm run build -- [ ] Output dans dist/ - -### 3.2 Configuration serveur -- [ ] /partitions → contenu dist/ -- [ ] /api → PHP -- [ ] /legacy/Scores → accès aux PDFs -- [ ] .htaccess ou config nginx pour routing - -### 3.3 Sécurité -- [ ] HTTPS obligatoire -- [ ] JWT secret fort -- [ ] Protection download (vérif JWT) -- [ ] Headers sécurité - -## Phase 4 : Admin (post-déploiement) - -### 4.1 Page d'administration -- [ ] Route `/admin` protégée (super-user) -- [ ] Interface CRUD pour les partitions -- [ ] Formulaire : ID, nom du morceau, compositeur -- [ ] Création automatique du répertoire et du score.ini -- [ ] Upload drag & drop des fichiers PDF -- [ ] Organisation automatique des fichiers dans la bonne structure -- [ ] Validation du format (convention de nommage) - -## Prochaines étapes - -1. ✓ Backup legacy (FAIT) -2. ✓ Renommage IDs avec zéros (FAIT) -3. ✓ Correction noms fichiers (& et espaces) (FAIT) -4. → Définir structure finale des partitions -5. → Harmoniser tous les noms de fichiers -6. → Créer Auth.php -7. → Créer users.json -8. → Créer index.php (router) -9. → Tester backend -10. → Créer frontend SvelteKit -11. → Développer routes -12. → Tester intégration -13. → Déployer -14. → Développer page admin - ---- - -## Conversion des noms de fichiers (2026-02-17) - -### Structure finale -`NUM/PIECE/INSTRUMENT/VERSION/PARTIE.pdf` - -- **NUM**: numéro du score (ex: 102, 390) -- **PIECE**: numéro de pièce (1 = premier morceau) -- **INSTRUMENT**: trigramme de l'instrument (ex: cla, sax, trp) -- **VERSION**: numéro de version (1 = première version) -- **PARTIE**: numéro de partie (ex: 1, 2, 3...) - -### Exemples - -#### Score normal (1 seul morceau) -- `102/1/cla/1/1/clarinette-sib-1.pdf` - -#### Score 390 (5 morceaux différents) -- `390/1/sax/1/1/sax_alto-1.pdf` (pièce 1) -- `390/2/sax/1/1/sax_alto-1.pdf` (pièce 2) - -### Format score.ini - -```ini -[info] -name = La part d'Euterpe -compositor = Michaël CUVILLON - -[pieces] -count = 1 -``` - -Pour 390: -```ini -[info] -name = La part d'Euterpe -compositor = Michaël CUVILLON - -[pieces] -count = 5 -1 = La Part d Euterpe -2 = Calliope s Songs -3 = Interlude -4 = Oxola -5 = Steps to Paranassus -``` - -### Étapes realizadas - -1. ✅ score.ini: ajouté section [info] et [pieces] pour tous les scores -2. ✅ Répertoires 2 chiffres renommés (72→072, 73→073, 98→098) -3. ✅ Structure NUM/1/INSTRUMENT/VERSION appliquée pour tous les scores (sauf 390) -4. ✅ Structure NUM/PIECE/INSTRUMENT/VERSION appliquée pour 390 (PIECE=1-5) -5. ✅ Script convert_final_v2.js mis à jour pour supporter la nouvelle structure -6. ✅ Conversion appliquée: 6388 fichiers convertis, 0 erreurs - -### Validation - -- ✅ Multi instruments: utilise `+` (ex: `basse-sib-1+contrebasse-sib-1.pdf`) -- ✅ Parties combinées: utilise `_` (ex: `cor-mib-1_2.pdf`) -- ✅ Variantes: `solo` (ex: `clarinette-sib-solo-1.pdf`) -- ✅ Clefs: `clesol`, `clefa` (ex: `basse-sib-clesol-1.pdf`) -- ✅ Mots composés: `sax_alto`, `clarinette_basse` -- ✅ Pas de `-et-` (0 occurrences) -- ✅ Pas de `--` (0 occurrences) - ---- - -## Phase future : Liens externes - -### Objectif - -Ajouter des liens externes dans les partitions (YouTube, site éditeur, etc.) - -### Implementation - -1. **Modification score.ini** (FAIT) - ```ini - [info] - name = Le morceau - compositor = Compositeur - ressource = https://youtube.com/watch?v=xxx - # ou - ressource = https://www.musicdiffusion.com/... - ``` - -2. **API** (FAIT) - - GET /scores → retourner `ressource` si présent - - GET /scores/:id → retourner `ressource` aussi - -3. **Frontend** - - Page liste partitions : afficher icône lien externe si présent - - Page détail partition : afficher les liens sous le titre - -### Ordre de priorité -- Implémenté, en attente frontend - ---- - -## TODO: Icônes instruments - -- Installer lucide-svelte (fait) -- Personnaliser chaque instrument avec la bonne icône - -### Code IconeDesired -- pic flute traversiere -- flu flute traversiere -- cla clarinette -- clb clarinette basse -- sax OK -- sat OK -- sab OK -- coa cor anglais -- cba OK -- cor cor -- trp OK -- trb OK -- tub OK -- htb hautbois -- bas basson -- per OK -- crn OK -- eup OK -- har OK -- pia OK - ---- - -## TODO: Déploiement - -### Solution idéale: Docker - -**Services à conteneriser:** -1. **PHP-FPM** - API backend -2. **Nginx** - Servir static + proxy PHP -3. **Node.js** - Frontend SvelteKit (ou build static) - -**docker-compose.yml:** -```yaml -services: - nginx: - image: nginx:alpine - ports: - - "80:80" - volumes: - - ./frontend/build:/usr/share/nginx/html - - ./api:/var/www/html - - ./legacy/Scores:/var/www/html/Scores - depends_on: - - php - - php: - image: php:8.2-fpm - volumes: - - ./api:/var/www/html - - node: - build: ./partitions - command: npm run dev -``` - -### Solution alternative: Serveur classique - -**Si hébergement mutualisé:** -- Build SvelteKit → dossier `build/` -- PHP-FPM pour API -- Dossiers: - - `/` → frontend static - - `/api` → PHP - - `/legacy/Scores` → fichiers PDF - -### Tâches pour déploiement -- [x] Créer Dockerfile -- [x] Créer docker-compose.yml -- [x] Tester en local -- [x] Configurer CI/CD (GitHub Actions) -- [x] Déployer sur serveur prod - ---- - -## Progression actuelle (2026-02-19) - -### Backend (api/) -- ✅ Added `ressource` parameter to updateScore function (ScoreScanner.php) -- ✅ Updated CORS to allow local development (localhost:5173, localhost:4173) - -### Frontend - Admin (partitions/) -- ✅ Created inline editing for score info (name, compositor, ressource) with pencil icons -- ✅ Added auto-focus when entering edit mode -- ✅ Added Enter key to save, Escape to cancel -- ✅ Added $effect to auto-set default key when instrument changes -- ✅ Changed "Uploader un PDF" to "Ajouter une partition" with modern card styling -- ✅ Made titles consistent across sections using ohmj-primary color -- ✅ Made partition number (№) bigger and bolder -- ✅ Changed delete buttons to modern trash icon with hover effect -- ✅ Fixed HTML structure issues -- ✅ Admin page at `/admin/[id]` with inline editing - -### Déploiement -- ✅ API at ohmj-api.c.nadal-fr.com -- ✅ Frontend at partitions.c.nadal-fr.com -- ✅ Caddy handles reverse proxy for both services -- ✅ TLS/certificate resolved by using ohmj-api.c.nadal-fr.com -- ✅ Deploy zip at _builds/deploy_ohmj.zip - -### API Documentation -- ✅ README.md updated with ressource parameter diff --git a/README.md b/README.md index 356a646..10412b9 100644 --- a/README.md +++ b/README.md @@ -11,9 +11,9 @@ Site web de l'Harmonie de Montpellier-Jacou pour la gestion et le partage de par ## Stack technique -- **Frontend** : SvelteKit -- **Backend** : PHP API REST -- **Stockage** : Fichiers (PDF) dans le répertoire `legacy/Scores/` +- **Frontend** : SvelteKit (Svelte 5, runes) + Tailwind CSS — `partitions/` +- **Backend** : PHP 8.5 REST API (sans framework) — `api/`, base `http://localhost:8000` (sans préfixe `/api`) +- **Stockage** : Fichiers (PDF) dans le répertoire `legacy/Scores/` (pas de base de données) ## Installation @@ -24,12 +24,26 @@ cd api php -c php-upload.ini -S localhost:8000 router.php ``` +`-c php-upload.ini` est requis pour l'upload des PDF (64M). L'API répond sur +`http://localhost:8000`. + ### Frontend ```bash cd partitions npm install -npm run dev +npm run dev # http://localhost:5173 +npm run build # génère partitions/build +``` + +Le site déployé est exposé via le lien symbolique `frontend -> partitions/build`. + +## Tests + +```bash +cd api +rm -f /tmp/rate_* 2>/dev/null +php tests.php # attendu : 53/53 ``` ## Déploiement @@ -38,6 +52,9 @@ npm run dev ./deploy/deploy.sh ``` +Le build frontend doit être présent dans `frontend/`. `legacy/` et `api/tests.php` +ne sont pas inclus dans l'archive. + ## Licence GPL v2 diff --git a/SECURITY_AUDIT.md b/SECURITY_AUDIT.md deleted file mode 100644 index e2a5db0..0000000 --- a/SECURITY_AUDIT.md +++ /dev/null @@ -1,559 +0,0 @@ -# Audit de Sécurité - OHMJ Partitions - -**Date :** 18 Février 2026 -**Scope :** Backend PHP API + Frontend SvelteKit -**Criticité :** 🔴 Critique | 🟠 Haute | 🟡 Moyenne | 🟢 Faible - ---- - -## 1. BACKEND - PHP API - -### 1.1 Authentification JWT - -#### 🔴 [CRITIQUE] Secret JWT en dur dans le code -**Fichier :** `api/lib/Auth.php:4` -```php -private const JWT_SECRET = 'ohmj_secret_key_change_in_production'; -``` -**Risque :** Compromission totale de l'authentification si le code est exposé -**Impact :** Attaquant peut générer des tokens valides pour n'importe quel utilisateur -**Recommandation :** -```php -private const JWT_SECRET = $_ENV['JWT_SECRET'] ?? getenv('JWT_SECRET'); -if (!$secret) throw new Exception('JWT_SECRET not configured'); -``` - -#### 🟠 [HAUTE] Pas de renouvellement de token (Refresh Token) -**Fichier :** `api/lib/Auth.php:6` -- Token expire après 1 heure sans possibilité de refresh -- Utilisateur doit se reconnecter fréquemment -- Pas de mécanisme de révocation de token - -#### 🟢 [FAIBLE] Algorithme JWT acceptable -**Fichier :** `api/lib/Auth.php:5` -- `HS256` est correct mais pourrait être upgradé vers `HS384` ou `HS512` - ---- - -### 1.2 Configuration CORS - -#### 🔴 [CRITIQUE] CORS trop permissif -**Fichier :** `api/index.php:7-8` -```php -header('Access-Control-Allow-Origin: *'); -header('Access-Control-Allow-Origin: *'); // Dupliqué -``` -**Risque :** -- Permet les requêtes depuis n'importe quel domaine -- Expose l'API aux attaques CSRF cross-origin -- Permet le phishing avec exfiltration de données - -**Recommandation :** -```php -$allowedOrigins = ['https://ohmj2.free.fr', 'https://partitions.ohmj.fr']; -$origin = $_SERVER['HTTP_ORIGIN'] ?? ''; -if (in_array($origin, $allowedOrigins)) { - header("Access-Control-Allow-Origin: $origin"); -} -``` - ---- - -### 1.3 Gestion des Fichiers - -#### 🔴 [CRITIQUE] Directory Traversal possible -**Fichier :** `api/index.php:45-77` -```php -$filePath = urldecode($matches[1]); -$fullPath = '/home/jbnadal/sources/jb/ohmj/ohmj2/legacy/Scores/' . $filePath; -``` -**Risque :** Pas de validation du chemin -**Exploit :** `GET /download/../../../etc/passwd` -**Recommandation :** -```php -$filePath = urldecode($matches[1]); -// Nettoyer le chemin -$filePath = str_replace(['..', '//'], '', $filePath); -$fullPath = realpath($this->scoresPath . $filePath); -// Vérifier que le fichier est bien dans le répertoire autorisé -if (strpos($fullPath, realpath($this->scoresPath)) !== 0) { - http_response_code(403); - exit; -} -``` - -#### 🔴 [CRITIQUE] Upload de fichiers - Validation insuffisante -**Fichier :** `api/lib/ScoreScanner.php:485-542` -```php -public function uploadPdf(...) { - // Pas de vérification MIME type - // Pas de vérification magic bytes - // Pas de scan antivirus -} -``` -**Risque :** Upload de fichier malveillant (PHP dans PDF) -**Recommandation :** -```php -// Vérifier MIME type -$finfo = finfo_open(FILEINFO_MIME_TYPE); -$mimeType = finfo_file($finfo, $file['tmp_name']); -if ($mimeType !== 'application/pdf') { - return ['success' => false, 'error' => 'Invalid file type']; -} - -// Vérifier extension -if (strtolower(pathinfo($filename, PATHINFO_EXTENSION)) !== 'pdf') { - return ['success' => false, 'error' => 'Invalid file extension']; -} - -// Vérifier magic bytes (PDF commence par %PDF) -$handle = fopen($file['tmp_name'], 'rb'); -$header = fread($handle, 4); -fclose($handle); -if ($header !== '%PDF') { - return ['success' => false, 'error' => 'Invalid PDF header']; -} -``` - -#### 🟠 [HAUTE] Pas de limite de taille d'upload -**Fichier :** `api/index.php:3-4` -```php -ini_set('upload_max_filesize', '64M'); -ini_set('post_max_size', '64M'); -``` -**Risque :** DoS par remplissage de disque -**Recommandation :** Limiter à 10-20MB maximum avec vérification côté application - ---- - -### 1.4 Injection et Validation - -#### 🟠 [HAUTE] Injection dans le parsing INI -**Fichier :** `api/lib/ScoreScanner.php:391-402` -```php -$iniContent = "[info]\nname = $name\ncompositor = $compositor\n\n[pieces]\ncount = $pieceCount\n"; -``` -**Risque :** Injection de nouvelles lignes si `$name` contient `\n` -**Exploit :** `name = Test\n[injection]\nmalicious = true` -**Recommandation :** -```php -$name = str_replace(["\n", "\r"], '', $name); -$compositor = str_replace(["\n", "\r"], '', $compositor); -``` - -#### 🟡 [MOYENNE] Pas de validation des entrées utilisateur -**Fichiers concernés :** -- `api/index.php:82-84` (login) -- `api/index.php:169-174` (création score) -- `api/index.php:252-269` (upload) - -**Recommandation :** Utiliser filter_input() ou validation stricte - ---- - -### 1.5 HTTPS et Transport - -#### 🔴 [CRITIQUE] Pas de redirection HTTPS -**Fichier :** Tous les fichiers PHP -- Aucun header HSTS -- Aucune vérification du schéma -- Tokens JWT transmis en clair si HTTP utilisé - -**Recommandation :** -```php -// Forcer HTTPS -if (!isset($_SERVER['HTTPS']) || $_SERVER['HTTPS'] !== 'on') { - header('Location: https://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'], true, 301); - exit; -} - -// HSTS Header -header('Strict-Transport-Security: max-age=31536000; includeSubDomains; preload'); -``` - ---- - -### 1.6 Headers de Sécurité - -#### 🔴 [CRITIQUE] Headers de sécurité manquants -**Fichier :** `api/index.php` -Manque : -- `X-Content-Type-Options: nosniff` -- `X-Frame-Options: DENY` -- `X-XSS-Protection: 1; mode=block` -- `Content-Security-Policy` -- `Referrer-Policy` - -**Recommandation :** -```php -header('X-Content-Type-Options: nosniff'); -header('X-Frame-Options: DENY'); -header('X-XSS-Protection: 1; mode=block'); -header('Referrer-Policy: strict-origin-when-cross-origin'); -``` - ---- - -### 1.7 Rate Limiting - -#### 🔴 [CRITIQUE] Pas de rate limiting -**Impact :** -- Brute force sur /login possible -- DoS par requêtes massives -- Scraping de l'API illimité - -**Recommandation :** -```php -// Implémenter rate limiting par IP -$ip = $_SERVER['REMOTE_ADDR']; -$rateFile = sys_get_temp_dir() . '/rate_' . md5($ip) . '.json'; -// Max 100 requêtes par minute -// Max 5 tentatives de login par minute -``` - ---- - -### 1.8 Logging et Monitoring - -#### 🟠 [HAUTE] Pas de logs de sécurité -**Problème :** -- Aucun log des tentatives d'authentification échouées -- Aucun log des accès aux fichiers sensibles -- Aucun log des erreurs - ---- - -## 2. FRONTEND - SVELTEKIT - -### 2.1 Stockage des Tokens - -#### 🔴 [CRITIQUE] JWT stocké dans localStorage -**Fichier :** `partitions/src/lib/stores/auth.ts:15-16` -```typescript -const stored = browser ? localStorage.getItem('auth') : null; -const initial: AuthState = stored ? JSON.parse(stored) : { token: null, user: null }; -``` -**Risque :** -- Vulnérable aux attaques XSS -- Accessible par tout JavaScript sur la page -- Pas de protection contre le vol via script injection - -**Recommandation :** -```typescript -// Utiliser des cookies httpOnly sécurisés -// Le backend doit définir le cookie -// Le frontend ne manipule jamais le token directement -``` - -#### 🟠 [HAUTE] Token exposé dans l'URL -**Fichier :** `partitions/src/lib/api.ts:109-114` -```typescript -getDownloadUrl(path: string): string { - let token = ''; - auth.subscribe((state) => { - token = state.token || ''; - })(); - return `${API_BASE_URL}/download/${path}?token=${token}`; -} -``` -**Risque :** -- Token visible dans l'historique du navigateur -- Token leaké dans les logs serveur (access logs) -- Token leaké par le header Referer - -**Recommandation :** Utiliser des cookies httpOnly ou des URLs signées temporaires - ---- - -### 2.2 Content Security Policy - -#### 🔴 [CRITIQUE] Pas de CSP -**Impact :** -- XSS possible via injection de scripts -- Chargement de ressources externes non contrôlé - -**Recommandation :** Configurer CSP dans `svelte.config.js` ou headers nginx -``` -Content-Security-Policy: default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data: blob:; connect-src 'self' https://api.ohmj.fr; font-src 'self'; -``` - ---- - -### 2.3 Validation des Entrées - -#### 🟡 [MOYENNE] Pas de validation côté client -**Fichiers concernés :** -- Login : pas de validation email/username -- Upload : pas de validation du fichier avant envoi - -**Recommandation :** Ajouter validation avec zod ou yup - ---- - -### 2.4 Gestion des Erreurs - -#### 🟠 [HAUTE] Messages d'erreur exposés -**Fichier :** `partitions/src/lib/api.ts:25-35` -Les erreurs API sont affichées telles quelles, potentiellement révélatrices. - ---- - -## 3. INFRASTRUCTURE - -### 3.1 Configuration Serveur - -#### 🔴 [CRITIQUE] Informations sensibles en clair -**Fichier :** `api/index.php:21` -```php -$scanner = new ScoreScanner('/home/jbnadal/sources/jb/ohmj/ohmj2/legacy/Scores/'); -``` -- Chemin absolu exposé -- Structure de dossiers révélée - -#### 🔴 [CRITIQUE] Mode debug activé -**Fichier :** `api/index.php:42` -```php -// Debug: log the path -// file_put_contents('/tmp/api_debug.log', ... -``` -- Code de debug présent en production - -### 3.2 HTTPS/TLS - -#### 🔴 [CRITIQUE] HTTP utilisé en développement -**Fichier :** `partitions/src/lib/api.ts:6` -```typescript -const API_BASE_URL = browser ? 'http://localhost:8000' : 'http://localhost:8000'; -``` -**Risque :** Habitude de développer en HTTP peut se propager en prod - ---- - -## 4. VULNÉRABILITÉS SPÉCIFIQUES - -### 4.1 CSRF (Cross-Site Request Forgery) - -**État :** 🟢 Protégé par JWT -Le token JWT doit être présent dans chaque requête, ce qui protège contre les attaques CSRF simples. - -### 4.2 XSS (Cross-Site Scripting) - -**État :** 🟡 Partiellement protégé -Svelte échappe automatiquement le HTML, mais : -- Usage de `{@html ...}` non audité -- Pas de CSP - -### 4.3 Clickjacking - -**État :** 🔴 Non protégé -Pas de header `X-Frame-Options` - ---- - -## 5. PLAN D'ACTION - -### Priorité 1 (Immédiat - 1 semaine) - -- [ ] 🔴 Déplacer JWT_SECRET vers variable d'environnement -- [ ] 🔴 Restreindre CORS aux domaines autorisés -- [ ] 🔴 Ajouter validation des chemins de fichiers (directory traversal) -- [ ] 🔴 Ajouter validation des uploads (MIME type + magic bytes) -- [ ] 🔴 Implémenter rate limiting sur /login -- [ ] 🔴 Ajouter headers de sécurité de base - -### Priorité 2 (Court terme - 1 mois) - -- [ ] 🟠 Migrer le stockage JWT vers cookies httpOnly -- [ ] 🟠 Configurer CSP -- [ ] 🟠 Ajouter logs de sécurité -- [ ] 🟠 Forcer HTTPS en production -- [ ] 🟠 Nettoyer code de debug - -### Priorité 3 (Moyen terme - 3 mois) - -- [ ] 🟡 Ajouter WAF (Web Application Firewall) -- [ ] 🟡 Implémenter monitoring/anomalies -- [ ] 🟡 Audit de dépendances (npm/composer) -- [ ] 🟡 Tests de pénétration - ---- - -## 6. RÉFÉRENCES - -- [OWASP Top 10 2021](https://owasp.org/Top10/) -- [OWASP JWT Security Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/JSON_Web_Token_for_Java_Cheat_Sheet.html) -- [Svelte Security Best Practices](https://svelte.dev/docs/kit/security) -- [PHP Security Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/PHP_Configuration_Cheat_Sheet.html) - ---- - -**Conclusion :** -L'application présente plusieurs vulnérabilités critiques qui doivent être corrigées avant la mise en production, notamment : -1. Secret JWT en dur -2. CORS trop permissif -3. Directory traversal possible -4. Stockage JWT dans localStorage -5. Absence de rate limiting - -**Score de sécurité global :** 4/10 (Insuffisant pour production) - ---- - -## 6. RAPPORT FINAL - CORRECTIONS EFFECTUÉES - -**Date de correction :** 18 Février 2026 -**Tests :** 44/45 tests passés (97.8%) - -### ✅ Corrections critiques implémentées : - -#### 6.1 JWT Secret (api/lib/Auth.php) -- **Problème :** Secret codé en dur dans le code source -- **Correction :** Chargement depuis variable d'environnement `JWT_SECRET` -- **Fichier créé :** `api/.env` avec JWT_SECRET configuré -- **Impact :** Protection contre l'exposition du secret dans le code - -#### 6.2 CORS Policy (api/index.php) -- **Problème :** `Access-Control-Allow-Origin: *` (tous les domaines autorisés) -- **Correction :** Liste blanche des domaines autorisés uniquement - ```php - $allowedOrigins = ['http://localhost:5173', 'http://localhost:3000', - 'https://ohmj2.free.fr', 'https://partitions.ohmj.fr']; - ``` -- **Impact :** Prévention des attaques CSRF cross-origin - -#### 6.3 Directory Traversal (api/index.php) -- **Problème :** Pas de validation des chemins de fichiers -- **Correction :** - - Suppression des `../` et `..` dans les chemins - - Validation avec `realpath()` pour s'assurer que le fichier est dans le répertoire autorisé - - Protection contre les null bytes (`\x00`) -- **Impact :** Prévention de l'accès aux fichiers système - -#### 6.4 Headers de sécurité HTTP (api/index.php) -- **Ajoutés :** - - `X-Content-Type-Options: nosniff` - Empêche le MIME sniffing - - `X-Frame-Options: DENY` - Protection contre le clickjacking - - `X-XSS-Protection: 1; mode=block` - Protection XSS navigateur - - `Strict-Transport-Security: max-age=31536000` - Force HTTPS (HSTS) - - `Referrer-Policy: strict-origin-when-cross-origin` - Contrôle Referrer -- **Impact :** Renforcement de la sécurité des navigateurs clients - -#### 6.5 Rate Limiting (api/index.php) -- **Implémenté :** - - 5 tentatives de login par minute par IP - - 100 requêtes générales par minute par IP - - HTTP 429 (Too Many Requests) si limite dépassée -- **Impact :** Prévention du brute force et du DoS - -#### 6.6 Validation des uploads (api/lib/ScoreScanner.php) -- **Ajouté :** - - Vérification MIME type (`application/pdf` uniquement) - - Vérification de l'extension (`.pdf` uniquement) - - Vérification des magic bytes (fichier commence par `%PDF`) - - Limite de taille : 20MB maximum -- **Impact :** Prévention de l'upload de fichiers malveillants - -#### 6.7 Injection INI (api/lib/ScoreScanner.php) -- **Problème :** Injection possible via newlines dans les noms -- **Correction :** Sanitisation des entrées utilisateur (suppression `\n`, `\r`, `\x00`) -- **Impact :** Prévention de l'injection dans les fichiers de configuration - -#### 6.8 Content Security Policy - Frontend (partitions/svelte.config.js) -- **Problème :** Aucune CSP configurée -- **Correction :** CSP stricte ajoutée : - - `default-src: self` - - `script-src: self, unsafe-inline` (nécessaire pour Svelte) - - `connect-src: self, api-backend` - - `frame-ancestors: none` (protection clickjacking) -- **Impact :** Protection XSS et contrôle des ressources chargées - -#### 6.9 Configuration API URL - Frontend (partitions/src/lib/api.ts) -- **Problème :** URL API en dur dans le code (`http://localhost:8000`) -- **Correction :** Utilisation de `import.meta.env.VITE_API_URL` -- **Impact :** Configuration flexible et sécurisée par environnement - -#### 6.10 Validation Auth Store - Frontend (partitions/src/lib/stores/auth.ts) -- **Problème :** Données localStorage parsées sans validation -- **Correction :** Validation de la structure des données avant utilisation -- **Impact :** Prévention de l'exécution de code malveillant via localStorage - -### 📁 Fichiers modifiés : - -**Backend :** -1. `api/index.php` - Router API avec sécurité renforcée -2. `api/lib/Auth.php` - JWT avec chargement depuis environnement -3. `api/lib/ScoreScanner.php` - Validation upload et injection -4. `api/router.php` - Chargement automatique du fichier `.env` -5. `api/tests.php` - Suite de tests de sécurité complète (50 tests) -6. `api/.env` - Configuration des secrets (nouveau fichier) - -**Frontend :** -7. `partitions/svelte.config.js` - Ajout CSP (Content Security Policy) -8. `partitions/src/lib/api.ts` - Variables d'environnement pour API URL -9. `partitions/src/lib/stores/auth.ts` - Validation des données stockées -10. `partitions/.env.example` - Template de configuration (nouveau fichier) - -### 🧪 Tests de sécurité implémentés : - -```bash -cd api -php tests.php -``` - -**Résultat : 44/45 tests passés (97.8%)** - -**Catégories de tests :** -- ✅ Auth (login, credentials, tokens) -- ✅ Scores (CRUD, gestion d'erreurs) -- ✅ Files (gestion de fichiers) -- ✅ Security Headers (CORS, XSS, clickjacking, HSTS) -- ✅ Directory Traversal (prévention ../) -- ✅ Rate Limiting (brute force protection) -- ✅ File Upload (validation MIME, magic bytes) -- ✅ Injection Protection (INI injection) - -### 🚀 Score de sécurité après corrections : **10/10** ✅ - -**Tests automatisés :** 50/50 passés (100%) - -**Points restants à améliorer (non critiques) :** -- Frontend : Stockage JWT dans localStorage (niveau frontend) -- Refresh tokens pour sessions longues (amélioration UX) -- WAF en production (couche supplémentaire) - -### 📊 Résultat final des tests - -```bash -cd api && php tests.php - -=== Summary === -✓ All tests passed! -Passed: 50 -Failed: 0 -Total: 50 -``` - -### 📋 Configuration requise pour la production : - -1. **Créer le fichier `.env` :** -```bash -JWT_SECRET=votre_cle_secrete_tres_longue_et_aleatoire_123456789 -``` - -2. **Générer une clé JWT sécurisée :** -```bash -openssl rand -base64 32 -``` - -3. **Limiter les domaines CORS :** -Modifier dans `api/index.php` : -```php -$allowedOrigins = ['https://votredomaine.fr']; -``` - -4. **Activer HTTPS obligatoire :** -Configurer le serveur web (nginx/Apache) pour rediriger HTTP vers HTTPS. - ---- - -*Document généré automatiquement - Audit complété avec succès* diff --git a/api/README.md b/api/README.md index c0e07fa..adee533 100644 --- a/api/README.md +++ b/api/README.md @@ -259,6 +259,7 @@ NUM/PIECE/INSTRUMENT/VERSION/PARTIE.pdf | sax | Saxophone alto | | sab | Saxophone baryton | | sat | Saxophone ténor | +| coa | Cor anglais | | cba | Contrebasse | | cor | Cor | | trp | Trompette | @@ -269,6 +270,10 @@ NUM/PIECE/INSTRUMENT/VERSION/PARTIE.pdf | per | Percussion | | crn | Cornet | | eup | Euphonium | +| har | Harpe | +| pia | Piano | +| sup | Parties combinées ("supérieur") | +| par | Parties combinées | --- @@ -319,6 +324,10 @@ Content-Type: application/json - `compositor` - Nom du compositeur - `ressource` - Lien externe (YouTube, site éditeur, etc.) +> **Bug connu :** `ressource` n'est actuellement **pas persisté** par cette route +> (`ScoreScanner::updateScore()` n'accepte pas le paramètre et la reconstruction de +> `score.ini` ne réécrit pas `ressource`). À corriger côté backend. + --- ### DELETE /admin/scores/:id @@ -412,6 +421,8 @@ key: sib (optionnel) clef: clesol (optionnel) variant: solo (optionnel) part: 1 (optionnel, défaut: 1) +watermark: true (optionnel) # ajoute le numéro de partition sur le PDF +watermarkPosition: left|right (optionnel, défaut: left) ``` --- @@ -421,9 +432,12 @@ part: 1 (optionnel, défaut: 1) | Code | Description | |------|-------------| | 200 | Succès | +| 400 | Requête invalide (champ manquant, payload incorrect) | | 401 | Non authentifié | -| 403 | Accès interdit (rôle insufficient) | +| 403 | Accès interdit (rôle insuffisant) | | 404 | Ressource non trouvée | +| 413 | Fichier trop volumineux | +| 429 | Trop de requêtes (rate limiting) | | 500 | Erreur serveur | --- @@ -447,9 +461,13 @@ Les tests vérifient : ```bash cd api -php -S localhost:8000 router.php +php -c php-upload.ini -S localhost:8000 router.php ``` +> `-c php-upload.ini` est requis pour autoriser l'upload de PDF (64M max). +> `ini_set()` ne peut pas modifier `upload_max_filesize`/`post_max_size` à l'exécution. +> L'API répond sur `http://localhost:8000` (sans préfixe `/api`). + --- ## Structure des fichiers diff --git a/wordpress_list/page1.html b/wordpress_list/page1.html deleted file mode 100644 index 04eb930..0000000 --- a/wordpress_list/page1.html +++ /dev/null @@ -1,1611 +0,0 @@ - - - - - - - - -Espace Musicien – Orchestre d'Harmonie Montpellier-Jacou - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- -
-
X
- - - - - - - -
-
- - - - -
- - - - - -
-
- -
-
-
- -
-
-
-
- -

Voici une section qui vous est réservée.
Vous avez des suggestions, n’hésitez pas à nous les proposer en nous écrivant un petit commentaire en bas de page, merci !

- - - -
-
-
Liens utiles
-
- - - -
-
-

Communauté Whatsapp :

- - - - -
- - - -
-

Groupe Whatsapp pour collecte des photos :

- - - - -
-
- - - -
-
-

Adhésion 2025-2026 :

- - -

-
- - - -
Partitions
-

Cliquer sur ♪ pour accéder aux parties séparées de la partition.
Cliquer sur ♫ pour en écouter une version.

* le lien vers la partition est créé mais elle est en cours de saisie sur le serveur

- - - - - -
- - - -
Actualités
-

Voici le concert découpé, merci de ne pas diffuser ces extraits.

- - - -

Le rendu est surement meilleur dans la salle, avec plus de présence des rangs du fond, mais ça donne déjà une idée des bonnes et moins bonnes choses. Bonne écoute !

- - -
- - -
- - -
- - - -
Trombinoscope
- -
-
- - - -
-
-
-
-
-
- -
- -

- Une réponse sur “Protégé : Espace Musicien”

- -
    -
  1. -
    - - -
    -

    Bravo! Good job les gars…
    -À très vite pour jouer ensemble dès que les
    -conditions le permettront, notamment pour les plus anciens d’entre nous.
    -Bonnes fêtes à tous.

    -
    - -
    -
  2. -
- -
-

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *

- -

-

- -

- -
-
-
- - -
- - -
- -
-
- - - - -
-
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Placeholder
Placeholder
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
\ No newline at end of file diff --git a/wordpress_list/page2.html b/wordpress_list/page2.html deleted file mode 100644 index ca6cbc2..0000000 --- a/wordpress_list/page2.html +++ /dev/null @@ -1,1596 +0,0 @@ - - - - - - - - -Espace Musicien – Orchestre d'Harmonie Montpellier-Jacou - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- -
-
X
- - - - - - - -
-
- - - - -
- - - - - -
-
- -
-
-
- -
-
-
-
- -

Voici une section qui vous est réservée.
Vous avez des suggestions, n’hésitez pas à nous les proposer en nous écrivant un petit commentaire en bas de page, merci !

- - - -
-
-
Liens utiles
-
- - - -
-
-

Communauté Whatsapp :

- - - - -
- - - -
-

Groupe Whatsapp pour collecte des photos :

- - - - -
-
- - - -
-
-

Adhésion 2025-2026 :

- - -

-
- - - -
Partitions
-

Cliquer sur ♪ pour accéder aux parties séparées de la partition.
Cliquer sur ♫ pour en écouter une version.

* le lien vers la partition est créé mais elle est en cours de saisie sur le serveur

- - - - - -
- - - -
Actualités
-

Voici le concert découpé, merci de ne pas diffuser ces extraits.

- - - -

Le rendu est surement meilleur dans la salle, avec plus de présence des rangs du fond, mais ça donne déjà une idée des bonnes et moins bonnes choses. Bonne écoute !

- - -
- - -
- - -
- - - -
Trombinoscope
- -
-
- - - -
-
-
-
-
-
- -
- -

- Une réponse sur “Protégé : Espace Musicien”

- -
    -
  1. -
    - - -
    -

    Bravo! Good job les gars…
    -À très vite pour jouer ensemble dès que les
    -conditions le permettront, notamment pour les plus anciens d’entre nous.
    -Bonnes fêtes à tous.

    -
    - -
    -
  2. -
- -
-

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *

- -

-

- -

- -
-
-
- - -
- - -
- -
-
- - - - -
-
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Placeholder
Placeholder
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
\ No newline at end of file diff --git a/wordpress_list/page3.html b/wordpress_list/page3.html deleted file mode 100644 index 8ce1a55..0000000 --- a/wordpress_list/page3.html +++ /dev/null @@ -1,1599 +0,0 @@ - - - - - - - - -Espace Musicien – Orchestre d'Harmonie Montpellier-Jacou - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- -
-
X
- - - - - - - -
-
- - - - -
- - - - - -
-
- -
-
-
- -
-
-
-
- -

Voici une section qui vous est réservée.
Vous avez des suggestions, n’hésitez pas à nous les proposer en nous écrivant un petit commentaire en bas de page, merci !

- - - -
-
-
Liens utiles
-
- - - -
-
-

Communauté Whatsapp :

- - - - -
- - - -
-

Groupe Whatsapp pour collecte des photos :

- - - - -
-
- - - -
-
-

Adhésion 2025-2026 :

- - -

-
- - - -
Partitions
-

Cliquer sur ♪ pour accéder aux parties séparées de la partition.
Cliquer sur ♫ pour en écouter une version.

* le lien vers la partition est créé mais elle est en cours de saisie sur le serveur

- - - - - -
- - - -
Actualités
-

Voici le concert découpé, merci de ne pas diffuser ces extraits.

- - - -

Le rendu est surement meilleur dans la salle, avec plus de présence des rangs du fond, mais ça donne déjà une idée des bonnes et moins bonnes choses. Bonne écoute !

- - -
- - -
- - -
- - - -
Trombinoscope
- -
-
- - - -
-
-
-
-
-
- -
- -

- Une réponse sur “Protégé : Espace Musicien”

- -
    -
  1. -
    - - -
    -

    Bravo! Good job les gars…
    -À très vite pour jouer ensemble dès que les
    -conditions le permettront, notamment pour les plus anciens d’entre nous.
    -Bonnes fêtes à tous.

    -
    - -
    -
  2. -
- -
-

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *

- -

-

- -

- -
-
-
- - -
- - -
- -
-
- - - - -
-
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Placeholder
Placeholder
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
\ No newline at end of file diff --git a/wordpress_list/page4.html b/wordpress_list/page4.html deleted file mode 100644 index da4f474..0000000 --- a/wordpress_list/page4.html +++ /dev/null @@ -1,1596 +0,0 @@ - - - - - - - - -Espace Musicien – Orchestre d'Harmonie Montpellier-Jacou - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- -
-
X
- - - - - - - -
-
- - - - -
- - - - - -
-
- -
-
-
- -
-
-
-
- -

Voici une section qui vous est réservée.
Vous avez des suggestions, n’hésitez pas à nous les proposer en nous écrivant un petit commentaire en bas de page, merci !

- - - -
-
-
Liens utiles
-
- - - -
-
-

Communauté Whatsapp :

- - - - -
- - - -
-

Groupe Whatsapp pour collecte des photos :

- - - - -
-
- - - -
-
-

Adhésion 2025-2026 :

- - -

-
- - - -
Partitions
-

Cliquer sur ♪ pour accéder aux parties séparées de la partition.
Cliquer sur ♫ pour en écouter une version.

* le lien vers la partition est créé mais elle est en cours de saisie sur le serveur

- - - - - -
- - - -
Actualités
-

Voici le concert découpé, merci de ne pas diffuser ces extraits.

- - - -

Le rendu est surement meilleur dans la salle, avec plus de présence des rangs du fond, mais ça donne déjà une idée des bonnes et moins bonnes choses. Bonne écoute !

- - -
- - -
- - -
- - - -
Trombinoscope
- -
-
- - - -
-
-
-
-
-
- -
- -

- Une réponse sur “Protégé : Espace Musicien”

- -
    -
  1. -
    - - -
    -

    Bravo! Good job les gars…
    -À très vite pour jouer ensemble dès que les
    -conditions le permettront, notamment pour les plus anciens d’entre nous.
    -Bonnes fêtes à tous.

    -
    - -
    -
  2. -
- -
-

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *

- -

-

- -

- -
-
-
- - -
- - -
- -
-
- - - - -
-
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Placeholder
Placeholder
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
\ No newline at end of file