1:"$Sreact.fragment" 2:I[5379,["915","static/chunks/915-e75e15b01ec6c60e.js","619","static/chunks/619-ba102abea3e3d0e4.js","284","static/chunks/284-7b00ddceae1508d2.js","177","static/chunks/app/layout-f47d2482c80930e1.js"],"ThemeProvider"] 3:I[120,["915","static/chunks/915-e75e15b01ec6c60e.js","619","static/chunks/619-ba102abea3e3d0e4.js","284","static/chunks/284-7b00ddceae1508d2.js","177","static/chunks/app/layout-f47d2482c80930e1.js"],"Header"] 4:I[9766,[],""] 5:I[8924,[],""] 6:I[6613,["915","static/chunks/915-e75e15b01ec6c60e.js","619","static/chunks/619-ba102abea3e3d0e4.js","284","static/chunks/284-7b00ddceae1508d2.js","177","static/chunks/app/layout-f47d2482c80930e1.js"],"Footer"] 7:I[7989,[],"ClientSegmentRoot"] 8:I[2086,["915","static/chunks/915-e75e15b01ec6c60e.js","284","static/chunks/284-7b00ddceae1508d2.js","584","static/chunks/app/blog/layout-56162451cf26531f.js"],"default"] a:I[2401,["915","static/chunks/915-e75e15b01ec6c60e.js","619","static/chunks/619-ba102abea3e3d0e4.js","935","static/chunks/app/blog/shai-Hulud-npm-worm-supply-chain-attack/page-8e554d49cb7075d5.js"],"BackToBlog"] 27:I[7150,[],""] :HL["/cv/_next/static/media/4cf2300e9c8272f7-s.p.woff2","font",{"crossOrigin":"","type":"font/woff2"}] :HL["/cv/_next/static/media/93f479601ee12b01-s.p.woff2","font",{"crossOrigin":"","type":"font/woff2"}] :HL["/cv/_next/static/css/d893c1b9853c3059.css","style"] 0:{"P":null,"b":"wIBcpDzxA6-tV9uMd1RSd","p":"/cv","c":["","blog","shai-Hulud-npm-worm-supply-chain-attack"],"i":false,"f":[[["",{"children":["blog",{"children":["shai-Hulud-npm-worm-supply-chain-attack",{"children":["__PAGE__",{}]}]}]},"$undefined","$undefined",true],["",["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/cv/_next/static/css/d893c1b9853c3059.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}]],["$","html",null,{"lang":"en","suppressHydrationWarning":true,"children":["$","body",null,{"className":"__variable_e0e7f9 __variable_49b421 bg-white tracking-tight antialiased dark:bg-zinc-950","children":["$","$L2",null,{"enableSystem":true,"attribute":"class","storageKey":"theme","defaultTheme":"system","children":["$","div",null,{"className":"flex min-h-screen w-full flex-col font-[family-name:var(--font-inter-tight)]","children":["$","div",null,{"className":"relative mx-auto w-full max-w-screen-sm flex-1 px-4 pt-20","children":[["$","$L3",null,{}],["$","$L4",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L5",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]],"forbidden":"$undefined","unauthorized":"$undefined"}],["$","$L6",null,{}]]}]}]}]}]}]]}],{"children":["blog",["$","$1","c",{"children":[null,["$","$L7",null,{"Component":"$8","slots":{"children":["$","$L4",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L5",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","forbidden":"$undefined","unauthorized":"$undefined"}]},"params":{},"promise":"$@9"}]]}],{"children":["shai-Hulud-npm-worm-supply-chain-attack",["$","$1","c",{"children":[null,["$","$L4",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L5",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","forbidden":"$undefined","unauthorized":"$undefined"}]]}],{"children":["__PAGE__",["$","$1","c",{"children":[[["$","$La",null,{}],"\n",["$","figure",null,{"children":[["$","img",null,{"src":"/cv/images/shai-hulud.png","alt":"Shai-Hulud npm worm attack illustration from Wiz.io","className":"rounded-xl"}],["$","figcaption",null,{"className":"text-center","children":"Wiz Research – Shai-Hulud npm worm supply chain attack"}]]}],"\n",["$","h1",null,{"children":["Technical Brief: The ",["$","em",null,{"children":"Shai-Hulud"}]," NPM Worm/Supply-Chain Attack"]}],"\n",["$","hr",null,{}],"\n",["$","h2",null,{"children":"Incident Overview"}],"\n",["$","ul",null,{"children":["\n",["$","li",null,{"children":["On ",["$","strong",null,{"children":"15 September 2025"}],", multiple popular npm packages were hijacked by threat actors to deliver malicious post-install scripts that exfiltrate sensitive data. The campaign has been dubbed ",["$","em",null,{"children":"Shai-Hulud"}]," by ",["$","a",null,{"href":"https://www.wiz.io/blog/shai-hulud-npm-supply-chain-attack","children":"Wiz Research"}],"."]}],"\n",["$","li",null,{"children":["It is significant because besides theft, it behaves like a ",["$","strong",null,{"children":"self-propagating worm"}],": once a compromised package has access to npm/GitHub credentials, it uses them to poison downstream packages (",["$","a",null,{"href":"https://www.wiz.io/blog/shai-hulud-npm-supply-chain-attack","children":"Wiz"}],")."]}],"\n",["$","li",null,{"children":["This build-chain compromise is directly downstream of the August 2025 ",["$","em",null,{"children":"s1ngularity / Nx"}]," incident, which itself involved GitHub token theft extending into npm token theft and package poisoning (","$Lb",")."]}],"\n"]}],"\n","$Lc","\n","$Ld","\n","$Le","\n","$Lf","\n","$L10","\n","$L11","\n","$L12","\n","$L13","\n","$L14","\n","$L15","\n","$L16","\n","$L17","\n","$L18","\n","$L19","\n","$L1a","\n","$L1b","\n","$L1c","\n","$L1d","\n","$L1e","\n","$L1f","\n","$L20","\n","$L21","\n","$L22","\n","$L23","\n","$L24"],null,"$L25"]}],{},null,false]},null,false]},null,false]},null,false],"$L26",false]],"m":"$undefined","G":["$27",[]],"s":false,"S":true} 28:I[4431,[],"OutletBoundary"] 2a:I[5278,[],"AsyncMetadataOutlet"] 2c:I[4431,[],"ViewportBoundary"] 2e:I[4431,[],"MetadataBoundary"] 2f:"$Sreact.suspense" b:["$","a",null,{"href":"https://www.aikido.dev/blog/s1ngularity-nx-attackers-strike-again","children":"Aikido"}] c:["$","hr",null,{}] d:["$","h2",null,{"children":"Attack Mechanics & Payload Behavior"}] e:["$","table",null,{"children":[["$","thead",null,{"children":["$","tr",null,{"children":[["$","th",null,{"children":"Phase"}],["$","th",null,{"children":"What the attacker does"}]]}]}],["$","tbody",null,{"children":[["$","tr",null,{"children":[["$","td",null,{"children":["$","b",null,{"children":"Initial Compromise"}]}],["$","td",null,{"children":["$","p",null,{"children":["Maintainer accounts (npm / GitHub) are compromised, likely via phishing or token theft.\n",["$","a",null,{"href":"https://www.aikido.dev/blog/s1ngularity-nx-attackers-strike-again","children":"Aikido"}]]}]}]]}],["$","tr",null,{"children":[["$","td",null,{"children":["$","b",null,{"children":"Payload delivery"}]}],["$","td",null,{"children":["$","p",null,{"children":["Malicious versions of npm packages include ",["$","code",null,{"children":"postinstall"}]," hooks that launch a ",["$","code",null,{"children":"bundle.js"}],".\nIt leverages ",["$","b",null,{"children":"TruffleHog"}]," to scan secrets and cloud metadata.\n",["$","a",null,{"href":"https://www.darkreading.com/application-security/self-replicating-shai-hulud-worm-npm-packages","children":"DarkReading"}]]}]}]]}],["$","tr",null,{"children":[["$","td",null,{"children":["$","b",null,{"children":"Exfiltration"}]}],["$","td",null,{"children":["$","p",null,{"children":["Secrets are exfiltrated in two ways: (1) via public GitHub repos named ",["$","i",null,{"children":"Shai-Hulud"}]," with encoded data,\n(2) via malicious GitHub Actions workflows sending to ",["$","code",null,{"children":"webhook[.]site"}],".\n",["$","a",null,{"href":"https://www.wiz.io/blog/shai-hulud-npm-supply-chain-attack","children":"Wiz"}]]}]}]]}],["$","tr",null,{"children":[["$","td",null,{"children":["$","b",null,{"children":"Propagation (Worm behavior)"}]}],["$","td",null,{"children":["$","p",null,{"children":["With valid npm tokens, the worm republishes compromised packages with incremented versions.\nIt also manipulates GitHub repos, sometimes renaming them to ",["$","i",null,{"children":"-migration"}],".\n",["$","a",null,{"href":"https://www.wiz.io/blog/shai-hulud-npm-supply-chain-attack","children":"Wiz"}]]}]}]]}]]}]]}] f:["$","hr",null,{}] 10:["$","h2",null,{"children":"Impact & Scope"}] 11:["$","ul",null,{"children":["\n",["$","li",null,{"children":["The worm has compromised ",["$","strong",null,{"children":"dozens to 180+ packages"}]," (",["$","a",null,{"href":"https://www.mend.io/blog/npm-supply-chain-attack-packages-compromised-by-self-spreading-malware/","children":"Mend"}],")."]}],"\n",["$","li",null,{"children":["High-profile packages like ",["$","code",null,{"children":"@ctrl/tinycolor"}]," (millions of weekly downloads) were hit (",["$","a",null,{"href":"https://www.itnews.com.au/news/first-npm-worm-shai-hulud-released-in-supply-chain-attack-620344","children":"ITNews"}],")."]}],"\n",["$","li",null,{"children":["Secrets exposed include ",["$","strong",null,{"children":"GitHub tokens, npm tokens, API keys, AWS/GCP credentials"}]," (",["$","a",null,{"href":"https://www.wiz.io/blog/shai-hulud-npm-supply-chain-attack","children":"Wiz"}],")."]}],"\n"]}] 12:["$","hr",null,{}] 13:["$","h2",null,{"children":"Comparison with Previous Incidents"}] 14:["$","ul",null,{"children":["\n",["$","li",null,{"children":["The ",["$","em",null,{"children":"Nx / s1ngularity"}]," compromise in August 2025 was a precursor — token theft and package poisoning, now expanded with worm-style propagation (",["$","a",null,{"href":"https://www.wiz.io/blog/shai-hulud-npm-supply-chain-attack","children":"Wiz"}],")."]}],"\n",["$","li",null,{"children":["Earlier incidents (e.g. ",["$","em",null,{"children":"debug / chalk"}]," hijack, Sept 2025) lacked automated propagation (",["$","a",null,{"href":"https://www.wiz.io/blog/widespread-npm-supply-chain-attack-breaking-down-impact-scope-across-debug-chalk","children":"Wiz"}],")."]}],"\n"]}] 15:["$","hr",null,{}] 16:["$","h2",null,{"children":"Risks & Threat Model Implications"}] 17:["$","ol",null,{"children":["\n",["$","li",null,{"children":[["$","strong",null,{"children":"High blast radius"}]," – worm behavior magnifies downstream risk."]}],"\n",["$","li",null,{"children":[["$","strong",null,{"children":"Dev machine and CI/CD compromise"}]," – lifecycle scripts run in trusted contexts."]}],"\n",["$","li",null,{"children":[["$","strong",null,{"children":"Secrets leakage"}]," – both repo commits and workflow logs exposed (",["$","a",null,{"href":"https://www.wiz.io/blog/shai-hulud-npm-supply-chain-attack","children":"Wiz"}],")."]}],"\n",["$","li",null,{"children":[["$","strong",null,{"children":"Persistence & stealth"}]," – leverages legitimate tools like TruffleHog, masquerading as benign processes (",["$","a",null,{"href":"https://www.sysdig.com/blog/shai-hulud-the-novel-self-replicating-worm-infecting-hundreds-of-npm-packages","children":"Sysdig"}],")."]}],"\n"]}] 18:["$","hr",null,{}] 19:["$","h2",null,{"children":"Mitigation & Remediation Actions"}] 1a:["$","h3",null,{"children":"Immediate"}] 1b:["$","ul",null,{"children":["\n",["$","li",null,{"children":["Identify if malicious versions were installed → ",["$","code",null,{"children":"rm -rf node_modules && npm cache clean --force"}]," (",["$","a",null,{"href":"https://www.wiz.io/blog/shai-hulud-npm-supply-chain-attack","children":"Wiz"}],")."]}],"\n",["$","li",null,{"children":["Revoke and rotate all exposed tokens (GitHub, npm, cloud providers) (",["$","a",null,{"href":"https://www.wiz.io/blog/shai-hulud-npm-supply-chain-attack","children":"Wiz"}],")."]}],"\n",["$","li",null,{"children":["Audit GitHub repos for suspicious branches, repos, or injected workflows (",["$","a",null,{"href":"https://www.wiz.io/blog/shai-hulud-npm-supply-chain-attack","children":"Wiz"}],")."]}],"\n"]}] 1c:["$","h3",null,{"children":"Detection & Monitoring"}] 1d:["$","ul",null,{"children":["\n",["$","li",null,{"children":["Monitor CI/CD for suspicious lifecycle scripts (",["$","a",null,{"href":"https://www.sysdig.com/blog/shai-hulud-the-novel-self-replicating-worm-infecting-hundreds-of-npm-packages","children":"Sysdig"}],")."]}],"\n",["$","li",null,{"children":["Use IoCs from Wiz, ReversingLabs, GitGuardian (e.g. workflow names, exfil endpoints) (",["$","a",null,{"href":"https://blog.gitguardian.com/shai-hulud-a-persistent-secret-leaking-campaign/","children":"GitGuardian"}],")."]}],"\n",["$","li",null,{"children":["Review GitHub audit logs for unusual repo creation and workflow injection (",["$","a",null,{"href":"https://www.wiz.io/blog/shai-hulud-npm-supply-chain-attack","children":"Wiz"}],")."]}],"\n"]}] 1e:["$","h3",null,{"children":"Preventative"}] 1f:["$","ul",null,{"children":["\n",["$","li",null,{"children":"Enforce least privilege on tokens."}],"\n",["$","li",null,{"children":"Use lockfiles / shrinkwrap to avoid unexpected package versions."}],"\n",["$","li",null,{"children":"Scan for suspicious postinstall scripts."}],"\n",["$","li",null,{"children":"Maintain dependency allow-lists and enable MFA for maintainer accounts."}],"\n"]}] 20:["$","hr",null,{}] 21:["$","h2",null,{"children":"Conclusion"}] 22:["$","p",null,{"children":["The ",["$","em",null,{"children":"Shai-Hulud"}]," campaign marks an escalation in supply chain threats:"]}] 23:["$","ul",null,{"children":["\n",["$","li",null,{"children":["It merges ",["$","strong",null,{"children":"worm-style propagation"}],", ",["$","strong",null,{"children":"secret harvesting"}],", and ",["$","strong",null,{"children":"repo exposure"}],"."]}],"\n",["$","li",null,{"children":["Teams using npm should treat this as a ",["$","strong",null,{"children":"high-urgency incident"}]," — both to remediate if impacted and to harden their defenses for future attacks."]}],"\n",["$","li",null,{"children":["Prior incidents (like ",["$","em",null,{"children":"Nx / s1ngularity"}],") show this is not isolated, but part of an evolving attacker playbook."]}],"\n"]}] 24:["$","hr",null,{}] 25:["$","$L28",null,{"children":["$L29",["$","$L2a",null,{"promise":"$@2b"}]]}] 26:["$","$1","h",{"children":[null,[["$","$L2c",null,{"children":"$L2d"}],["$","meta",null,{"name":"next-size-adjust","content":""}]],["$","$L2e",null,{"children":["$","div",null,{"hidden":true,"children":["$","$2f",null,{"fallback":null,"children":"$L30"}]}]}]]}] 9:"$0:f:0:1:2:children:1:props:children:1:props:params" 2d:[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}],["$","meta","2",{"name":"theme-color","content":"#ffffff"}]] 29:null 31:I[622,[],"IconMark"] 2b:{"metadata":[["$","title","0",{"children":"Ryan Johnston - CV"}],["$","meta","1",{"name":"description","content":"Ryan Johnston - CV"}],["$","link","2",{"rel":"icon","href":"/cv/favicon.ico","type":"image/x-icon","sizes":"16x16"}],["$","$L31","3",{}]],"error":null,"digest":"$undefined"} 30:"$2b:metadata"