fix(security): resolve brace-expansion + @babel/core via version-scoped overrides #111

Merged
maximus merged 1 commit from issue-103-babel-brace-overrides into master 2026-08-12 18:14:18 +00:00
Owner

Fixes #103
Fixes #110

Changements

  • brace-expansion -> ^1.1.18 / ^2.1.4 / ^5.0.9 (version-scoped par majeur). DoS (expansion exponentielle + OOM). Debloque #103 : un override blanket cassait le bundler (5.x force sur minimatch@3/@9 attendant l'export par defaut -> TypeError), mais le version-scoping bumpe chaque majeur dans son majeur, preservant le contrat CJS (export defaut 1.x/2.x, nomme 5.x). Verifie : minimatch@3 resout (mm("src/foo.js","src/*.js") == true, l'appel qui plantait sous blanket).
  • @babel/core -> ^7.29.6 (resout 7.29.7). GHSA-4x5r-pxfx-6jf8 (arbitrary file read via sourceMappingURL, vuln <=7.29.0).

Build/dev-time uniquement, non bundle dans l'APK. npm audit : 14 -> 12. Smoke test 11/11. Le diff lock est en remplacements 1:1 (102/102) ; le volume vient des ~87 instances dedupees de @babel/core, pas d'un churn parasite.

Note technique

Meme mecanisme que la PR #109 (js-yaml) : les overrides version-scoped (pkg@^N.0.0) ciblent par version installee, contournant deux limites des overrides nestes -- la non-propagation a travers un parent overridde (js-yaml/@expo/cli) et l'ecrasement cross-major d'un blanket (brace-expansion/minimatch).

Ce qui reste (irreductible)

#108 image-size (HIGH) reste ouverte : aucun fix publie (derniere 2.0.2 encore vulnerable, <=2.0.2), bloque upstream. Les 12 HIGH restantes sont 100% sa cascade metro/react-native/expo. Plancher irreductible sans bump du SDK Expo. Re-verifie le 2026-08-12.

Fixes #103 Fixes #110 ## Changements - **brace-expansion** -> `^1.1.18` / `^2.1.4` / `^5.0.9` (version-scoped par majeur). DoS (expansion exponentielle + OOM). Debloque #103 : un override blanket cassait le bundler (5.x force sur minimatch@3/@9 attendant l'export par defaut -> `TypeError`), mais le **version-scoping bumpe chaque majeur dans son majeur**, preservant le contrat CJS (export defaut 1.x/2.x, nomme 5.x). Verifie : `minimatch@3` resout (`mm("src/foo.js","src/*.js") == true`, l'appel qui plantait sous blanket). - **@babel/core** -> `^7.29.6` (resout 7.29.7). GHSA-4x5r-pxfx-6jf8 (arbitrary file read via sourceMappingURL, vuln `<=7.29.0`). Build/dev-time uniquement, non bundle dans l'APK. **`npm audit` : 14 -> 12.** Smoke test 11/11. Le diff lock est en remplacements 1:1 (102/102) ; le volume vient des ~87 instances dedupees de `@babel/core`, pas d'un churn parasite. ## Note technique Meme mecanisme que la PR #109 (js-yaml) : les overrides **version-scoped** (`pkg@^N.0.0`) ciblent par version installee, contournant deux limites des overrides nestes -- la non-propagation a travers un parent overridde (js-yaml/@expo/cli) et l'ecrasement cross-major d'un blanket (brace-expansion/minimatch). ## Ce qui reste (irreductible) **#108 image-size** (HIGH) reste ouverte : aucun fix publie (derniere 2.0.2 encore vulnerable, `<=2.0.2`), bloque upstream. Les **12 HIGH restantes sont 100% sa cascade** metro/react-native/expo. Plancher irreductible sans bump du SDK Expo. Re-verifie le 2026-08-12.
maximus added 1 commit 2026-08-12 18:07:12 +00:00
- brace-expansion -> ^1.1.18 / ^2.1.4 / ^5.0.9 (version-scoped per major):
  DoS advisories (exponential-time expansion + unbounded OOM). Version-scoping
  bumps each major within its own major, preserving the CJS export contract
  (default export for 1.x/2.x, named for 5.x) that a blanket override would have
  broken. This is #103's original blocker, now resolved. Verified: minimatch@3
  glob resolution works (the exact call that TypeError'd under a blanket).
- @babel/core -> ^7.29.6 (resolves 7.29.7): GHSA-4x5r-pxfx-6jf8 arbitrary file
  read via sourceMappingURL (vulnerable <=7.29.0, ~87 deduped instances).

Both build/dev-time only, not bundled in the shipped APK. npm audit 14 -> 12;
the remaining 12 high are entirely the image-size upstream-locked cascade
(no published fix, tracked in #108). Smoke test 11/11, lock diff is 1:1 version
replacements (large count driven by @babel/core's deduped instances).

Fixes #103
Fixes #110

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
maximus added the
status:review
label 2026-08-12 18:07:13 +00:00
Author
Owner

Review — APPROVE

Override de sécurité propre et ciblé : 2 fichiers touchés (package.json + package-lock.json), diff lock strictement symétrique (102/102 lignes, 22 bumps de version, aucun nœud de package ajouté ou retiré). Le version-scoping de brace-expansion par majeur est correct et les bumps @babel/* sont internement cohérents.

Vérifié

  • package.json : les 4 overrides ajoutés correspondent exactement au diff lock. brace-expansion@^1.0.0→^1.1.18, @^2.0.0→^2.1.4, @^5.0.0→^5.0.9 ; @babel/core→^7.29.6 (non scopé — un seul majeur 7.x dans l'arbre, donc pas de risque de clobber cross-major).
  • Cohérence lock : chaque bump @babel/* respecte les ranges de ses parents (ex. @babel/core@7.29.7 exige code-frame ^7.29.7 → bumpé à 7.29.7 ; @babel/traverse@7.29.8 exige parser ^7.29.8 → parser à 7.29.8). Les 6 instances de brace-expansion (codegen, top-level, expo, react-native, rimraf, test-exclude) restent chacune dans leur majeur.
  • Scope : build/dev-time uniquement, rien de bundlé dans l'APK. Pas de secret, pas de code applicatif touché, pas de migration.
  • Issues liées : #103 (brace-expansion, HIGH) et #110 (@babel/core, LOW) existent, ouvertes, type:security. Fixes correct pour les deux.

Suggestions (non bloquantes)

  1. Test de régression pour l'invariant que la PR défend. Tout le récit de la PR = un override blanket cassait minimatch (TypeError, contrat d'export CJS). Or aucun garde-fou automatisé ne verrouille ça : pas de CI dans ce repo, et tests/smoke.test.cjs ne teste pas minimatch. Un futur contributeur qui « simplifie » les 3 lignes brace-expansion en un seul blanket réintroduirait la casse en silence. Encoder la vérif manuelle du body dans smoke.test.cjs (require('minimatch')('src/foo.js','src/*.js') === true) fermerait ce trou.
  2. Deux claims du body légèrement imprécises (le diff, lui, est propre) : « Smoke test 11/11 » — smoke.test.cjs compte en fait 13 check() ; « ~87 instances dedupees de @babel/core » — le diff réel = 22 bumps de version pour une seule instance de @babel/core. Le code prime sur la prose ; à aligner si tu veux.
  3. brace-expansion@5 passe à engines: node "20 || >=22" (18 retiré). Sans impact attendu : build-time, toolchain Expo 54 déjà Node 20+, et engines est advisory par défaut. À garder en tête si l'env EAS tournait encore sur Node 18.

Note

Pas de CI sur ce repo (.forgejo/workflows absent) : npm audit 14→12 et le smoke test ne sont validés que localement. Non introduit par cette PR — juste le contexte qui explique pourquoi les claims du body ne sont pas rejouées par une automatisation.

Même mécanisme que #109 (js-yaml), pattern éprouvé et vérifié empiriquement par le lock. RAS bloquant.

## Review — APPROVE Override de sécurité propre et ciblé : 2 fichiers touchés (`package.json` + `package-lock.json`), diff lock strictement symétrique (102/102 lignes, 22 bumps de version, **aucun nœud de package ajouté ou retiré**). Le version-scoping de `brace-expansion` par majeur est correct et les bumps `@babel/*` sont internement cohérents. ### Vérifié - **package.json** : les 4 overrides ajoutés correspondent exactement au diff lock. `brace-expansion@^1.0.0→^1.1.18`, `@^2.0.0→^2.1.4`, `@^5.0.0→^5.0.9` ; `@babel/core→^7.29.6` (non scopé — un seul majeur `7.x` dans l'arbre, donc pas de risque de clobber cross-major). - **Cohérence lock** : chaque bump `@babel/*` respecte les ranges de ses parents (ex. `@babel/core@7.29.7` exige `code-frame ^7.29.7` → bumpé à 7.29.7 ; `@babel/traverse@7.29.8` exige `parser ^7.29.8` → parser à 7.29.8). Les 6 instances de `brace-expansion` (codegen, top-level, expo, react-native, rimraf, test-exclude) restent chacune dans leur majeur. - **Scope** : build/dev-time uniquement, rien de bundlé dans l'APK. Pas de secret, pas de code applicatif touché, pas de migration. - **Issues liées** : #103 (brace-expansion, HIGH) et #110 (@babel/core, LOW) existent, ouvertes, `type:security`. `Fixes` correct pour les deux. ### Suggestions (non bloquantes) 1. **Test de régression pour l'invariant que la PR défend.** Tout le récit de la PR = un override *blanket* cassait minimatch (`TypeError`, contrat d'export CJS). Or aucun garde-fou automatisé ne verrouille ça : pas de CI dans ce repo, et `tests/smoke.test.cjs` ne teste pas minimatch. Un futur contributeur qui « simplifie » les 3 lignes `brace-expansion` en un seul blanket réintroduirait la casse en silence. Encoder la vérif manuelle du body dans `smoke.test.cjs` (`require('minimatch')('src/foo.js','src/*.js') === true`) fermerait ce trou. 2. **Deux claims du body légèrement imprécises** (le diff, lui, est propre) : « Smoke test 11/11 » — `smoke.test.cjs` compte en fait **13** `check()` ; « ~87 instances dedupees de @babel/core » — le diff réel = **22 bumps de version** pour une **seule** instance de `@babel/core`. Le code prime sur la prose ; à aligner si tu veux. 3. **`brace-expansion@5` passe à `engines: node "20 || >=22"`** (18 retiré). Sans impact attendu : build-time, toolchain Expo 54 déjà Node 20+, et `engines` est advisory par défaut. À garder en tête si l'env EAS tournait encore sur Node 18. ### Note Pas de CI sur ce repo (`.forgejo/workflows` absent) : `npm audit 14→12` et le smoke test ne sont validés que **localement**. Non introduit par cette PR — juste le contexte qui explique pourquoi les claims du body ne sont pas rejouées par une automatisation. Même mécanisme que #109 (js-yaml), pattern éprouvé et vérifié empiriquement par le lock. RAS bloquant.
maximus merged commit 96adf00495 into master 2026-08-12 18:14:18 +00:00
maximus deleted branch issue-103-babel-brace-overrides 2026-08-12 18:14:18 +00:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: maximus/simpl-liste#111
No description provided.