[{"data":1,"prerenderedAt":1167},["ShallowReactive",2],{"article-axios-supply-chain-attack-npm-security":3},{"id":4,"title":5,"body":6,"date":1154,"description":1155,"extension":1156,"meta":1157,"navigation":252,"path":1158,"readingTime":262,"seo":1159,"stem":1160,"tags":1161,"__hash__":1166},"articles/articles/axios-supply-chain-attack-npm-security.md","Axios 1.14.1 : anatomie d'une attaque supply chain npm en temps réel",{"type":7,"value":8,"toc":1128},"minimark",[9,31,38,43,46,56,59,65,68,74,77,88,92,99,162,181,185,197,290,294,301,306,357,361,438,461,469,472,526,539,546,550,554,578,582,613,617,642,650,661,728,731,737,741,802,823,827,830,926,943,948,954,958,961,967,970,974,977,989,998,1004,1008,1011,1017,1021,1113,1116,1124],[10,11,12,13,17,18,22,23,26,27,30],"p",{},"Le 31 mars 2026, un post sur X alerte la communauté : ",[14,15,16],"strong",{},"axios@1.14.1 est compromis",". Un attaquant a pris le contrôle du compte npm du mainteneur principal et publié deux versions malveillantes (",[19,20,21],"code",{},"1.14.1"," et ",[19,24,25],{},"0.30.4",") qui tirent ",[19,28,29],{},"plain-crypto-js@4.2.1"," — un paquet malveillant créé le jour même. Aucune des deux versions n'apparaît dans les tags GitHub officiels du projet.",[10,32,33,34,37],{},"Axios, c'est ",[14,35,36],{},"+100 millions de téléchargements par semaine",". L'un des paquets les plus utilisés de l'écosystème Node.js. Quand il tombe, c'est l'ensemble de la chaîne qui tremble.",[39,40,42],"h2",{"id":41},"le-principe-sans-jargon","Le principe, sans jargon",[10,44,45],{},"Imagine Bob et Alice. Les deux utilisent un Samsung Galaxy S22.",[47,48,53],"pre",{"className":49,"code":51,"language":52},[50],"language-text","Jour 1 — Tout va bien\n\n  Bob                          Alice\n  [Samsung S22]                [Samsung S22]\n  version: 14.0               version: 14.0\n  -> tout fonctionne           -> tout fonctionne\n","text",[19,54,51],{"__ignoreMap":55},"",[10,57,58],{},"Samsung publie une mise à jour. Bob et Alice ont activé les mises à jour automatiques.",[47,60,63],{"className":61,"code":62,"language":52},[50],"Jour 2 — Mise à jour classique\n\n  Samsung publie la version 14.1\n  (correctif de sécurité légitime)\n\n  Bob                          Alice\n  [Samsung S22]                [Samsung S22]\n  version: 14.0 -> 14.1       version: 14.0 -> 14.1\n  -> OK, tout va bien          -> OK, tout va bien\n",[19,64,62],{"__ignoreMap":55},[10,66,67],{},"Maintenant, un attaquant compromet le compte d'un développeur chez Samsung. Il publie une fausse mise à jour.",[47,69,72],{"className":70,"code":71,"language":52},[50],"Jour 3 — L'attaque\n\n  Attaquant publie la version 14.2\n  (contient du code qui envoie tes contacts\n   et tes photos à un serveur externe)\n\n  Bob                          Alice\n  [Samsung S22]                [Samsung S22]\n  config: \"accepter ^14.x\"     config: \"rester sur 14.1\"\n         |                            |\n         v                            v\n  version: 14.1 -> 14.2       version: 14.1 (pas de changement)\n  -> COMPROMIS                 -> PROTEGEE\n",[19,73,71],{"__ignoreMap":55},[10,75,76],{},"La seule différence : Alice a fixé sa version. Bob accepte \"toute version 14.x\".",[10,78,79,80,83,84,87],{},"Dans npm, c'est exactement la même chose. ",[19,81,82],{},"\"axios\": \"^1.13.6\""," = \"accepter toute version 1.x\". ",[19,85,86],{},"\"axios\": \"1.13.6\""," = \"rester sur 1.13.6, point\".",[39,89,91],{"id":90},"ce-qui-sest-passé","Ce qui s'est passé",[10,93,94,95,98],{},"L'attaque suit un schéma classique de ",[14,96,97],{},"dependency injection"," :",[100,101,102,109,125,135],"ol",{},[103,104,105,108],"li",{},[14,106,107],{},"Compromission du compte"," du mainteneur principal Axios sur npm — via un token npm longue durée compromis (le vecteur exact est encore sous investigation). Les autres mainteneurs n'ont pas pu révoquer l'accès car les permissions de l'attaquant dépassaient les leurs.",[103,110,111,112,115,116,115,119,122,123],{},"Publication de ",[19,113,114],{},"axios@1.14.1"," ",[14,117,118],{},"et",[19,120,121],{},"axios@0.30.4"," (les deux branches empoisonnées en 39 minutes) avec une nouvelle dépendance : ",[19,124,29],{},[103,126,127,130,131,134],{},[19,128,129],{},"plain-crypto-js"," est un paquet publié le jour même — nom volontairement proche de ",[19,132,133],{},"crypto-js"," (typosquatting) pour ne pas attirer l'attention",[103,136,137,138,141,142,144,145,147,148,151,152,144,155,158,159],{},"Tout projet qui résout ",[19,139,140],{},"axios"," vers ",[19,143,21],{}," ou ",[19,146,25],{}," (via ",[19,149,150],{},"npm install"," avec un range ",[19,153,154],{},"^1.x",[19,156,157],{},"^0.30.x",") ",[14,160,161],{},"exécute le code malveillant",[10,163,164,165,168,169,172,173,176,177,180],{},"C'est la même mécanique que l'attaque ",[19,166,167],{},"event-stream"," de 2018, ",[19,170,171],{},"ua-parser-js"," de 2021, et ",[19,174,175],{},"colors","/",[19,178,179],{},"faker"," de 2022. Le vecteur change, le pattern reste.",[39,182,184],{"id":183},"qui-est-impacté","Qui est impacté ?",[10,186,187,188,193,194,98],{},"Toute application qui a fait un ",[14,189,190,192],{},[19,191,150],{}," frais"," entre la publication de la version compromise et son retrait, avec un range permissif dans ",[19,195,196],{},"package.json",[47,198,202],{"className":199,"code":200,"language":201,"meta":55,"style":55},"language-json shiki shiki-themes github-light github-dark","// VULNERABLE — résout vers 1.14.1\n\"axios\": \"^1.7.9\"\n\"axios\": \"^1.0.0\"\n\"axios\": \">=1.0.0\"\n\n// PROTEGE — ne résout jamais vers 1.14.1\n\"axios\": \"1.14.0\"\n\"axios\": \"1.13.6\"\n\"axios\": \"~1.13.6\"\n","json",[19,203,204,213,227,237,247,254,260,270,280],{"__ignoreMap":55},[205,206,209],"span",{"class":207,"line":208},"line",1,[205,210,212],{"class":211},"sJ8bj","// VULNERABLE — résout vers 1.14.1\n",[205,214,216,220,224],{"class":207,"line":215},2,[205,217,219],{"class":218},"sZZnC","\"axios\"",[205,221,223],{"class":222},"sVt8B",": ",[205,225,226],{"class":218},"\"^1.7.9\"\n",[205,228,230,232,234],{"class":207,"line":229},3,[205,231,219],{"class":218},[205,233,223],{"class":222},[205,235,236],{"class":218},"\"^1.0.0\"\n",[205,238,240,242,244],{"class":207,"line":239},4,[205,241,219],{"class":218},[205,243,223],{"class":222},[205,245,246],{"class":218},"\">=1.0.0\"\n",[205,248,250],{"class":207,"line":249},5,[205,251,253],{"emptyLinePlaceholder":252},true,"\n",[205,255,257],{"class":207,"line":256},6,[205,258,259],{"class":211},"// PROTEGE — ne résout jamais vers 1.14.1\n",[205,261,263,265,267],{"class":207,"line":262},7,[205,264,219],{"class":218},[205,266,223],{"class":222},[205,268,269],{"class":218},"\"1.14.0\"\n",[205,271,273,275,277],{"class":207,"line":272},8,[205,274,219],{"class":218},[205,276,223],{"class":222},[205,278,279],{"class":218},"\"1.13.6\"\n",[205,281,283,285,287],{"class":207,"line":282},9,[205,284,219],{"class":218},[205,286,223],{"class":222},[205,288,289],{"class":218},"\"~1.13.6\"\n",[39,291,293],{"id":292},"pourquoi-le-lockfile-ne-suffit-pas-toujours","Pourquoi le lockfile ne suffit pas (toujours)",[10,295,296,297,300],{},"La première réaction est souvent : \"j'ai un lockfile, je suis protégé\". C'est ",[14,298,299],{},"partiellement vrai",".",[302,303,305],"h3",{"id":304},"quand-le-lockfile-te-protège","Quand le lockfile te protège",[307,308,309,322],"table",{},[310,311,312],"thead",{},[313,314,315,319],"tr",{},[316,317,318],"th",{},"Situation",[316,320,321],{},"Protégé ?",[323,324,325,337,347],"tbody",{},[313,326,327,334],{},[328,329,330,333],"td",{},[19,331,332],{},"npm ci"," (CI/CD)",[328,335,336],{},"Oui — toujours",[313,338,339,344],{},[328,340,341,343],{},[19,342,150],{}," sans modification",[328,345,346],{},"Oui",[313,348,349,354],{},[328,350,351,352],{},"Clone frais + ",[19,353,332],{},[328,355,356],{},"Oui — si committé",[302,358,360],{"id":359},"quand-il-ne-te-protège-pas","Quand il ne te protège PAS",[307,362,363,372],{},[310,364,365],{},[313,366,367,369],{},[316,368,318],{},[316,370,371],{},"Pourquoi",[323,373,374,391,401,412,420,428],{},[313,375,376,381],{},[328,377,378],{},[19,379,380],{},"npm install \u003Cnouveau-paquet>",[328,382,383,386,387,390],{},[14,384,385],{},"Re-résolution de l'arbre entier",". Les deps avec ",[19,388,389],{},"^"," peuvent sauter à une version plus récente",[313,392,393,398],{},[328,394,395],{},[19,396,397],{},"npm update",[328,399,400],{},"Conçu pour mettre à jour",[313,402,403,409],{},[328,404,405,406,408],{},"Modification manuelle de ",[19,407,196],{}," + install",[328,410,411],{},"Lockfile recalculé",[313,413,414,417],{},[328,415,416],{},"Suppression du lockfile",[328,418,419],{},"Plus de protection",[313,421,422,425],{},[328,423,424],{},"Lockfile non committé",[328,426,427],{},"Chaque dev génère le sien",[313,429,430,435],{},[328,431,432],{},[19,433,434],{},"npm install --no-package-lock",[328,436,437],{},"Ignore explicitement le lockfile",[10,439,440,443,444,447,448,451,452,454,455,458,459,300],{},[14,441,442],{},"Le piège classique"," : un dev ajoute ",[19,445,446],{},"npm install dayjs",". Sans le savoir, npm re-résout axios de ",[19,449,450],{},"1.13.6"," → ",[19,453,21],{},". Le lockfile est mis à jour, committé, et toute l'équipe hérite du code malveillant. Un simple ",[19,456,457],{},"git diff package-lock.json"," aurait révélé l'apparition suspecte de ",[19,460,129],{},[39,462,464,465,468],{"id":463},"le-champ-integrity-ta-dernière-ligne-de-défense","Le champ ",[19,466,467],{},"integrity"," : ta dernière ligne de défense",[10,470,471],{},"Dans le lockfile, chaque paquet a un champ souvent ignoré :",[47,473,475],{"className":199,"code":474,"language":201,"meta":55,"style":55},"\"node_modules/axios\": {\n  \"version\": \"1.13.6\",\n  \"resolved\": \"https://registry.npmjs.org/axios/-/axios-1.13.6.tgz\",\n  \"integrity\": \"sha512-ChTCHMouEe2kn713WHbQGcuYrr...\"\n}\n",[19,476,477,485,499,511,521],{"__ignoreMap":55},[205,478,479,482],{"class":207,"line":208},[205,480,481],{"class":218},"\"node_modules/axios\"",[205,483,484],{"class":222},": {\n",[205,486,487,491,493,496],{"class":207,"line":215},[205,488,490],{"class":489},"sj4cs","  \"version\"",[205,492,223],{"class":222},[205,494,495],{"class":218},"\"1.13.6\"",[205,497,498],{"class":222},",\n",[205,500,501,504,506,509],{"class":207,"line":229},[205,502,503],{"class":489},"  \"resolved\"",[205,505,223],{"class":222},[205,507,508],{"class":218},"\"https://registry.npmjs.org/axios/-/axios-1.13.6.tgz\"",[205,510,498],{"class":222},[205,512,513,516,518],{"class":207,"line":239},[205,514,515],{"class":489},"  \"integrity\"",[205,517,223],{"class":222},[205,519,520],{"class":218},"\"sha512-ChTCHMouEe2kn713WHbQGcuYrr...\"\n",[205,522,523],{"class":207,"line":249},[205,524,525],{"class":222},"}\n",[10,527,528,529,532,533,115,535,538],{},"Ce hash ",[14,530,531],{},"SHA-512"," est calculé sur le tarball au moment du premier install. Si quelqu'un modifie le contenu d'un paquet sur le registry sans changer la version, ",[19,534,332],{},[14,536,537],{},"refusera"," l'installation car le hash ne correspondra plus.",[10,540,541,542,545],{},"C'est une protection contre le ",[14,543,544],{},"registry tampering"," — pas contre la publication d'une nouvelle version malveillante (qui a son propre hash légitime).",[39,547,549],{"id":548},"la-méthodologie-de-défense-en-profondeur","La méthodologie de défense en profondeur",[302,551,553],{"id":552},"niveau-1-le-minimum-vital-tout-le-monde-devrait-faire-ça","Niveau 1 — Le minimum vital (tout le monde devrait faire ça)",[555,556,557,563,572],"ul",{},[103,558,559,562],{},[14,560,561],{},"Lockfile committé"," dans git — toujours",[103,564,565,569,570],{},[14,566,567],{},[19,568,332],{}," en CI/CD, jamais ",[19,571,150],{},[103,573,574,577],{},[14,575,576],{},"Relire le diff du lockfile"," dans chaque PR",[302,579,581],{"id":580},"niveau-2-équipe-sérieuse","Niveau 2 — Équipe sérieuse",[555,583,584,590,599],{},[103,585,586,589],{},[14,587,588],{},"Versions fixes"," pour les dépendances critiques (HTTP, auth, paiement)",[103,591,592,144,595,598],{},[14,593,594],{},"Renovate",[14,596,597],{},"Dependabot"," — PRs automatiques de bump, avec diff visible et review",[103,600,601,606,607,612],{},[14,602,603],{},[19,604,605],{},"npm audit"," / ",[14,608,609],{},[19,610,611],{},"pnpm audit"," dans le pipeline CI (en warning au minimum)",[302,614,616],{"id":615},"niveau-3-sécurité-renforcée","Niveau 3 — Sécurité renforcée",[555,618,619,628,636],{},[103,620,621,144,624,627],{},[14,622,623],{},"Socket.dev",[14,625,626],{},"Snyk"," — analyse statique du code des dépendances",[103,629,630,635],{},[14,631,632],{},[19,633,634],{},"npm audit signatures"," — vérifie les attestations de provenance (SLSA/Sigstore)",[103,637,638,641],{},[14,639,640],{},"Registry privé"," (Artifactory, Verdaccio) — proxy avec allowlist",[39,643,645,606,647,649],{"id":644},"npm-audit-pnpm-audit-utile-mais-insuffisant-seul",[19,646,605],{},[19,648,611],{}," : utile mais insuffisant seul",[10,651,652,653,656,657,660],{},"Ces commandes interrogent la ",[14,654,655],{},"GitHub Advisory Database"," et comparent chaque paquet + version de ton lockfile contre les ",[14,658,659],{},"CVE connues"," (vulnérabilités publiées).",[47,662,666],{"className":663,"code":664,"language":665,"meta":55,"style":55},"language-bash shiki shiki-themes github-light github-dark","# npm\nnpm audit              # toutes les deps\nnpm audit --omit=dev   # seulement les deps de production\n\n# pnpm\npnpm audit             # toutes les deps\npnpm audit --prod      # seulement les deps de production\n","bash",[19,667,668,673,685,697,701,706,716],{"__ignoreMap":55},[205,669,670],{"class":207,"line":208},[205,671,672],{"class":211},"# npm\n",[205,674,675,679,682],{"class":207,"line":215},[205,676,678],{"class":677},"sScJk","npm",[205,680,681],{"class":218}," audit",[205,683,684],{"class":211},"              # toutes les deps\n",[205,686,687,689,691,694],{"class":207,"line":229},[205,688,678],{"class":677},[205,690,681],{"class":218},[205,692,693],{"class":489}," --omit=dev",[205,695,696],{"class":211},"   # seulement les deps de production\n",[205,698,699],{"class":207,"line":239},[205,700,253],{"emptyLinePlaceholder":252},[205,702,703],{"class":207,"line":249},[205,704,705],{"class":211},"# pnpm\n",[205,707,708,711,713],{"class":207,"line":256},[205,709,710],{"class":677},"pnpm",[205,712,681],{"class":218},[205,714,715],{"class":211},"             # toutes les deps\n",[205,717,718,720,722,725],{"class":207,"line":262},[205,719,710],{"class":677},[205,721,681],{"class":218},[205,723,724],{"class":489}," --prod",[205,726,727],{"class":211},"      # seulement les deps de production\n",[10,729,730],{},"Exemple de sortie :",[47,732,735],{"className":733,"code":734,"language":52},[50],"┌─────────────────────┬──────────────────────────────────┐\n│ moderate            │ Prototype Pollution in lodash    │\n├─────────────────────┼──────────────────────────────────┤\n│ Package             │ lodash                           │\n│ Vulnerable versions │ \u003C4.17.21                         │\n│ Patched versions    │ >=4.17.21                        │\n│ More info           │ https://ghsa.io/GHSA-xxxx        │\n└─────────────────────┴──────────────────────────────────┘\n",[19,736,734],{"__ignoreMap":55},[302,738,740],{"id":739},"ce-que-ça-détecte-et-ce-que-ça-ne-détecte-pas","Ce que ça détecte — et ce que ça ne détecte pas",[307,742,743,754],{},[310,744,745],{},[313,746,747,749],{},[316,748],{},[316,750,751,753],{},[19,752,605],{}," détecte ?",[323,755,756,763,770,780,793],{},[313,757,758,761],{},[328,759,760],{},"CVE publiée sur une dépendance",[328,762,346],{},[313,764,765,768],{},[328,766,767],{},"Paquet avec une vulnérabilité connue et patchée",[328,769,346],{},[313,771,772,775],{},[328,773,774],{},"Attaque zero-day (paquet malveillant sans CVE encore)",[328,776,777],{},[14,778,779],{},"Non",[313,781,782,789],{},[328,783,784,785,788],{},"Code malveillant dans un ",[19,786,787],{},"postinstall"," script",[328,790,791],{},[14,792,779],{},[313,794,795,798],{},[328,796,797],{},"Typosquatting (paquet au nom similaire)",[328,799,800],{},[14,801,779],{},[10,803,804,807,808,810,811,815,816,819,820,822],{},[14,805,806],{},"Conclusion"," : ",[19,809,605],{}," détecte les vulnérabilités ",[812,813,814],"em",{},"après"," qu'elles ont été signalées. C'est une couche de défense utile mais ",[14,817,818],{},"réactive"," — elle n'aurait pas détecté l'attaque axios au moment de la publication. Pour la détection proactive, il faut des outils comme ",[14,821,623],{}," qui analysent le comportement du code (appels réseau, eval, accès filesystem).",[39,824,826],{"id":825},"pourquoi-cest-la-combinaison-des-trois-qui-fonctionne","Pourquoi c'est la combinaison des trois qui fonctionne",[10,828,829],{},"Aucune de ces couches ne suffit seule. Chacune couvre l'angle mort des deux autres :",[307,831,832,850],{},[310,833,834],{},[313,835,836,839,841,846],{},[316,837,838],{},"Scénario d'attaque",[316,840,588],{},[316,842,843,844],{},"Lockfile + ",[19,845,332],{},[316,847,848],{},[19,849,605],{},[323,851,852,869,891,908],{},[313,853,854,860,863,866],{},[328,855,856,859],{},[14,857,858],{},"Nouvelle version malveillante"," (axios@1.14.1)",[328,861,862],{},"Bloque — tu restes sur 1.13.6",[328,864,865],{},"Bloque — le lockfile fixe 1.13.6",[328,867,868],{},"Pas de CVE encore",[313,870,871,879,882,889],{},[328,872,873,878],{},[14,874,875,876],{},"Dev fait ",[19,877,446],{}," et axios saute à 1.14.1",[328,880,881],{},"Bloque — version fixée, npm ne peut pas bumper",[328,883,884,885,888],{},"Le lockfile est ",[14,886,887],{},"mis à jour"," silencieusement",[328,890,868],{},[313,892,893,899,902,905],{},[328,894,895,898],{},[14,896,897],{},"CVE connue"," sur une dep existante",[328,900,901],{},"Tu restes sur la version vulnérable",[328,903,904],{},"Idem",[328,906,907],{},"Détecte et alerte",[313,909,910,918,921,924],{},[328,911,912,915,916],{},[14,913,914],{},"Dev supprime le lockfile"," et fait ",[19,917,150],{},[328,919,920],{},"Bloque — version exacte, pas de range",[328,922,923],{},"Plus de lockfile",[328,925,868],{},[10,927,928,929,932,933,935,936,938,939,942],{},"La ",[14,930,931],{},"ligne 2"," est la plus importante : sans version fixée, un simple ",[19,934,446],{}," peut re-résoudre tout l'arbre de dépendances. Le lockfile est mis à jour, committé, et ",[19,937,332],{}," en CI n'y verra rien — il installera fidèlement le ",[812,940,941],{},"nouveau"," lockfile compromis.",[10,944,945],{},[14,946,947],{},"Les versions fixes sont la seule couche qui protège quand le lockfile est re-généré.",[47,949,952],{"className":950,"code":951,"language":52},[50],"Versions fixes  → empêche la résolution silencieuse vers une version malveillante\nnpm ci          → garantit que la CI utilise exactement le lockfile committé\nnpm audit       → rattrape les CVE connues sur les versions fixées\n",[19,953,951],{"__ignoreMap":55},[302,955,957],{"id":956},"le-sweet-spot-pour-une-petite-équipe","Le sweet spot pour une petite équipe",[10,959,960],{},"Si tu es founding engineer ou en petite équipe, voici le ratio effort/protection optimal :",[47,962,965],{"className":963,"code":964,"language":52},[50],"1. Lockfile committé                              → gratuit\n2. npm ci en CI                                   → 5 min de config\n3. Versions fixes sur deps réseau (axios, auth0)  → 2 min par dep\n4. Dependabot activé                              → 10 min one-shot\n5. Relire le diff lockfile dans les PRs           → discipline\n",[19,966,964],{"__ignoreMap":55},[10,968,969],{},"Cinq actions. Pas quinze. Et ça couvre l'attaque d'aujourd'hui.",[39,971,973],{"id":972},"la-vraie-leçon","La vraie leçon",[10,975,976],{},"L'outillage aide, mais la discipline fait la différence.",[10,978,979,980,982,983,985,986,988],{},"La plupart des développeurs impactés ont fait ",[19,981,150],{},", le lockfile s'est mis à jour silencieusement avec ",[19,984,114],{}," + ",[19,987,129],{},", et ils ont committé sans regarder le diff.",[10,990,991,994,995],{},[14,992,993],{},"Le réflexe à ancrer"," : chaque fois que le lockfile change, se poser la question — ",[812,996,997],{},"est-ce que je comprends pourquoi ces dépendances ont changé ?",[10,999,1000,1001,300],{},"Si un paquet inconnu apparaît dans le diff et que tu ne l'as pas ajouté explicitement : ",[14,1002,1003],{},"c'est un signal d'alerte",[39,1005,1007],{"id":1006},"checklist-incident-supply-chain","Checklist incident supply chain",[10,1009,1010],{},"Pour référence, voici la checklist à dérouler quand un paquet est signalé compromis :",[47,1012,1015],{"className":1013,"code":1014,"language":52},[50],"□ grep `plain-crypto-js`, `axios@1.14.1` et `axios@0.30.4` dans tous les lockfiles\n□ Vérifier la version résolue de la dep directe\n□ Vérifier node_modules/ si installé localement\n□ Si impacté : fixer la version, supprimer node_modules, npm ci\n□ Rotation des secrets/tokens du poste de dev\n□ Vérifier les images Docker déployées en production\n□ Monitorer les logs réseau pour des connexions suspectes\n",[19,1016,1014],{"__ignoreMap":55},[39,1018,1020],{"id":1019},"rappel-syntaxe-des-versions-npm","Rappel : syntaxe des versions npm",[307,1022,1023,1042],{},[310,1024,1025],{},[313,1026,1027,1030,1033],{},[316,1028,1029],{},"Syntaxe",[316,1031,1032],{},"Range autorisé",[316,1034,1035,1038,1039,1041],{},[19,1036,1037],{},"1.7.9"," résout vers ",[19,1040,21],{}," ?",[323,1043,1044,1055,1069,1085,1099],{},[313,1045,1046,1050,1053],{},[328,1047,1048],{},[19,1049,1037],{},[328,1051,1052],{},"Exacte",[328,1054,779],{},[313,1056,1057,1062,1067],{},[328,1058,1059],{},[19,1060,1061],{},"~1.7.9",[328,1063,1064],{},[19,1065,1066],{},"≥1.7.9 \u003C1.8.0",[328,1068,779],{},[313,1070,1071,1076,1081],{},[328,1072,1073],{},[19,1074,1075],{},"^1.7.9",[328,1077,1078],{},[19,1079,1080],{},"≥1.7.9 \u003C2.0.0",[328,1082,1083],{},[14,1084,346],{},[313,1086,1087,1092,1095],{},[328,1088,1089],{},[19,1090,1091],{},"*",[328,1093,1094],{},"Tout",[328,1096,1097],{},[14,1098,346],{},[313,1100,1101,1106,1109],{},[328,1102,1103],{},[19,1104,1105],{},">=1.0.0",[328,1107,1108],{},"Tout depuis 1.0.0",[328,1110,1111],{},[14,1112,346],{},[1114,1115],"hr",{},[10,1117,1118],{},[812,1119,1120,1121,1123],{},"L'écosystème npm transporte une dette structurelle : des registres mutables, des comptes mainteneurs sans 2FA obligatoire, et des millions de projets qui font confiance aveugle à ",[19,1122,389],{},". Chaque attaque supply chain est un rappel que la sécurité de ton application dépend de la discipline avec laquelle tu gères tes dépendances — pas seulement de la qualité de ton code.",[1125,1126,1127],"style",{},"html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}",{"title":55,"searchDepth":215,"depth":215,"links":1129},[1130,1131,1132,1133,1137,1139,1144,1148,1151,1152,1153],{"id":41,"depth":215,"text":42},{"id":90,"depth":215,"text":91},{"id":183,"depth":215,"text":184},{"id":292,"depth":215,"text":293,"children":1134},[1135,1136],{"id":304,"depth":229,"text":305},{"id":359,"depth":229,"text":360},{"id":463,"depth":215,"text":1138},"Le champ integrity : ta dernière ligne de défense",{"id":548,"depth":215,"text":549,"children":1140},[1141,1142,1143],{"id":552,"depth":229,"text":553},{"id":580,"depth":229,"text":581},{"id":615,"depth":229,"text":616},{"id":644,"depth":215,"text":1145,"children":1146},"npm audit / pnpm audit : utile mais insuffisant seul",[1147],{"id":739,"depth":229,"text":740},{"id":825,"depth":215,"text":826,"children":1149},[1150],{"id":956,"depth":229,"text":957},{"id":972,"depth":215,"text":973},{"id":1006,"depth":215,"text":1007},{"id":1019,"depth":215,"text":1020},"2026-03-31","Le 31 mars 2026, un attaquant a compromis le compte d'un mainteneur Axios pour injecter un paquet malveillant. Retour technique sur l'attaque, pourquoi le lockfile ne suffit pas toujours, et la méthodologie de défense en profondeur.","md",{},"/articles/axios-supply-chain-attack-npm-security",{"title":5,"description":1155},"articles/axios-supply-chain-attack-npm-security",[1162,678,1163,1164,1165],"Security","Supply Chain","DevOps","Node.js","ZRYV1Wh-SxwI-4iypk9EX91vNgyai3RcHkElf1nX9KQ",1776852839559]