diff --git a/CLAUDE.md b/CLAUDE.md index bf6e078..5a8815a 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -59,11 +59,11 @@ curl -H "Authorization: Bearer $(cat ~/.coolify-token)" \ ## Tests -- `npm test` (vitest) — 299 cas +- `npm test` (vitest) — 301 cas - `__tests__/findings.test.js` — `/defenseurs/findings` (14 cas : auth, validation, filtres severity/category, asymetrie INFO, scan clean vs no_data, JSON corrompu) - `__tests__/state.test.js` — `/defenseurs/state` + non-regression `/defenseurs` apres extraction de `serveJsonFile()` (9 cas : corps identique octet pour octet sur un JSON non canonique, no_data, isolation des deux chemins, 401 sans token / mauvais token / token d'ingestion, 404 sur POST) - `__tests__/health.test.js` — module `metrics.js` + `/health` (6 cas : exports, payload champ pour champ, garde de latence) - - `__tests__/auth.test.js` — filet de securite auth sur les routes de lecture (65 cas : matrice route x mode d'echec, en-tetes malformes, quasi-rates du token sur les 6 routes de lecture, fail-closed, **2 cas `404 (not 401)` qui figent l'ordre routage -> auth**, **un espion sur `timingSafeEqual`** qui exige deux empreintes de 32 octets a chaque requete, et **des epingles sur le source** : aucune egalite contre une chaine `Bearer`, un seul `tokenMatches()` et un seul `return null` dans `checkAuth()`, egalites limitees a une liste blanche, `tokenMatches()` qui hache les deux cotes sans test de longueur) + - `__tests__/auth.test.js` — filet de securite auth sur les routes de lecture (67 cas : matrice route x mode d'echec, en-tetes malformes (dont le separateur `Bearer` autre qu'un espace), quasi-rates du token sur les 6 routes de lecture, fail-closed, **2 cas `404 (not 401)` qui figent l'ordre routage -> auth**, **un espion sur `timingSafeEqual`** qui exige deux empreintes de 32 octets a chaque requete, et **des epingles sur le source** : aucune egalite contre une chaine `Bearer`, un seul `tokenMatches()` et un seul `return null` dans `checkAuth()`, egalites limitees a une liste blanche, `tokenMatches()` qui hache les deux cotes sans test de longueur) - `__tests__/hosts.test.js` — matrice exhaustive des endpoints postes (155 cas : regex de route comme controle anti-traversee, 401 avant 403, cloisonnement des deux tokens, 503 fail-closed, reconstruction par liste blanche, 413, `neverSeen`, fraicheur serveur sous horloge figee, degradation de lecture) - `__tests__/agent.test.js` — agent poste (50 cas : contrat de payload verifie contre `sanitizeSnapshot()`, une seule tentative sans rejeu, timeout, codes de sortie, wrapper shell, et **le token absent de toute sortie d'echec, fragments de 5 caracteres compris**) - Runtime reste 0-dep ; vitest en devDep uniquement diff --git a/__tests__/auth.test.js b/__tests__/auth.test.js index f785cf3..7173738 100644 --- a/__tests__/auth.test.js +++ b/__tests__/auth.test.js @@ -125,6 +125,10 @@ describe("auth gate — malformed Authorization header", () => { ["raw token without the Bearer scheme", TOKEN], ["lowercase scheme", `bearer ${TOKEN}`], ["scheme with no token", "Bearer"], + // The separator is exactly one space: `startsWith("Bearer")` followed by + // slice(7) would accept any byte in its place. + ["tab instead of the space", `Bearer\t${TOKEN}`], + ["any other byte instead of the space", `BearerX${TOKEN}`], ]; test.each(MALFORMED)("401 on GET /defenseurs — %s", async (_label, header) => {