[{"data":1,"prerenderedAt":553},["ShallowReactive",2],{"blog-stories/openclaw-malicious-packages":3},{"id":4,"title":5,"body":6,"category":528,"date":529,"dateModified":530,"description":531,"draft":532,"extension":533,"faq":534,"featured":532,"headerVariant":528,"image":530,"keywords":539,"meta":540,"navigation":541,"ogDescription":542,"ogTitle":530,"path":543,"readTime":544,"schemaOrg":545,"schemaType":546,"seo":547,"sitemap":548,"stem":549,"tags":550,"twitterCard":551,"__hash__":552},"blog/blog/stories/openclaw-malicious-packages.md","OpenClaw's 900 Malicious npm Packages: What Vibe Coders Need to Know",{"type":7,"value":8,"toc":506},"minimark",[9,21,28,31,36,39,44,47,84,88,91,96,114,120,124,127,137,141,144,147,151,157,164,168,174,177,186,190,196,200,203,207,214,234,238,260,263,267,273,281,288,292,299,310,314,317,349,352,356,363,379,382,406,410,413,416,420,426,430,433,439,442,470,494,502],[10,11,12],"tldr",{},[13,14,15,16,20],"p",{},"In 2025, the OpenClaw campaign published roughly 900 malicious npm packages using typosquatting and dependency confusion. Payloads included credential stealers, reverse shells, and data exfiltration. This is especially dangerous for vibe coders because AI coding tools suggest packages by name, not by security audit. When Cursor or Claude says ",[17,18,19],"code",{},"npm install some-package",", it has no idea if that package is legitimate.",[13,22,23,24,27],{},"You have probably run ",[17,25,26],{},"npm install"," thousands of times without thinking about it. It is a reflex, not a decision. Type the package name, hit enter, move on.",[13,29,30],{},"That reflex is exactly what the OpenClaw attackers exploited.",[32,33,35],"h2",{"id":34},"what-happened","What Happened",[13,37,38],{},"Over several months in 2025, a threat actor group published approximately 900 malicious packages to the npm registry. The campaign was methodical. Each package was designed to look like a legitimate, popular package, with names just slightly different from the real thing.",[40,41],"stat-box",{"label":42,"number":43},"Malicious packages published to npm","~900",[13,45,46],{},"The packages were not empty shells. They contained functional code that appeared to do what the name suggested. But buried in postinstall scripts or obfuscated helper files, they also ran payloads that:",[48,49,50,62,68,74],"ul",{},[51,52,53,57,58,61],"li",{},[54,55,56],"strong",{},"Stole credentials"," from environment variables, ",[17,59,60],{},".env"," files, and cloud provider configs",[51,63,64,67],{},[54,65,66],{},"Installed reverse shells"," giving attackers persistent access to developer machines",[51,69,70,73],{},[54,71,72],{},"Exfiltrated data"," including SSH keys, AWS credentials, and database connection strings",[51,75,76,79,80,83],{},[54,77,78],{},"Modified other packages"," in ",[17,81,82],{},"node_modules"," to maintain persistence even after the malicious package was removed",[32,85,87],{"id":86},"how-the-attack-worked","How the Attack Worked",[13,89,90],{},"OpenClaw used two primary techniques that are well-known but still devastatingly effective.",[92,93,95],"h3",{"id":94},"typosquatting","Typosquatting",[13,97,98,99,102,103,106,107,102,110,113],{},"The attackers registered package names that were slight misspellings of popular packages. Think ",[17,100,101],{},"expresss"," instead of ",[17,104,105],{},"express",", or ",[17,108,109],{},"lodahs",[17,111,112],{},"lodash",". A single character difference that is easy to miss, especially when you are typing fast or copying a suggestion from an AI tool.",[115,116,117],"warning-box",{},[13,118,119],{},"Typosquatting works because developers trust their muscle memory. When an AI tool suggests a package name, you trust it even more, because you assume the AI knows what it is talking about.",[92,121,123],{"id":122},"dependency-confusion","Dependency Confusion",[13,125,126],{},"Some packages targeted internal company package names. npm's default resolution prefers public registry packages over private ones if the version number is higher. By publishing a public package with the same name as a private internal package but with a higher version number, the attacker's code gets installed instead.",[128,129,130],"danger-box",{},[13,131,132,133,136],{},"If your company uses private npm packages without a scoped namespace (@company/package-name), you are vulnerable to dependency confusion attacks right now. Run ",[17,134,135],{},"npm config get registry"," to verify your registry configuration.",[32,138,140],{"id":139},"why-this-hits-vibe-coders-harder","Why This Hits Vibe Coders Harder",[13,142,143],{},"Traditional developers have workflows that provide some friction. Code reviews. Dependency approval processes. Senior developers who recognize unfamiliar package names.",[13,145,146],{},"Vibe coders often do not have these guardrails.",[92,148,150],{"id":149},"ai-tools-suggest-packages-by-name-not-by-reputation","AI Tools Suggest Packages by Name, Not by Reputation",[13,152,153,154,156],{},"When Cursor, Bolt, or Claude suggests ",[17,155,19],{},", it is drawing on training data about package names. It has no real-time knowledge of whether that package is still maintained, whether the author is trustworthy, or whether the package was published yesterday by an attacker.",[13,158,159,160,163],{},"The AI cannot run ",[17,161,162],{},"npm audit",". It cannot check download counts. It cannot verify the package author's identity. It just knows the name fits the context.",[92,165,167],{"id":166},"the-speed-of-vibe-coding-removes-friction","The Speed of Vibe Coding Removes Friction",[13,169,170,171,173],{},"When you are building fast, iterating with AI, and shipping in a weekend, every ",[17,172,26],{}," feels like a step toward the finish line. Stopping to vet a package breaks your flow. So you do not stop.",[13,175,176],{},"That is the exact behavior supply chain attackers count on.",[178,179,180],"tip-box",{},[13,181,182,183,185],{},"Treat every ",[17,184,26],{}," suggested by an AI tool the same way you would treat a link in an email from a stranger. Verify before you click.",[92,187,189],{"id":188},"postinstall-scripts-run-automatically","Postinstall Scripts Run Automatically",[13,191,192,193,195],{},"By default, npm runs lifecycle scripts (preinstall, install, postinstall) automatically when you install a package. A malicious postinstall script can execute arbitrary code on your machine the moment you run ",[17,194,26],{},". You do not even need to import the package in your code.",[32,197,199],{"id":198},"how-to-protect-yourself","How to Protect Yourself",[13,201,202],{},"The good news: defending against supply chain attacks does not require exotic tools. It requires discipline and a few configuration changes.",[92,204,206],{"id":205},"_1-always-check-before-you-install","1. Always Check Before You Install",[13,208,209,210,213],{},"Before running ",[17,211,212],{},"npm install anything-new",":",[48,215,216,222,228],{},[51,217,218,221],{},[54,219,220],{},"Search for it on npmjs.com."," Check the download count, publish history, and linked GitHub repo.",[51,223,224,227],{},[54,225,226],{},"Look at the publisher."," A package published by a new account with no other packages is suspicious.",[51,229,230,233],{},[54,231,232],{},"Compare the name carefully."," If an AI suggested it, double-check the spelling against the official docs.",[92,235,237],{"id":236},"_2-use-npm-audit-regularly","2. Use npm audit Regularly",[239,240,245],"pre",{"className":241,"code":242,"language":243,"meta":244,"style":244},"language-bash shiki shiki-themes github-light github-dark","npm audit\n","bash","",[17,246,247],{"__ignoreMap":244},[248,249,252,256],"span",{"class":250,"line":251},"line",1,[248,253,255],{"class":254},"sScJk","npm",[248,257,259],{"class":258},"sZZnC"," audit\n",[13,261,262],{},"This checks your dependency tree against the npm advisory database. Run it after every install and as part of your CI pipeline.",[92,264,266],{"id":265},"_3-disable-automatic-script-execution","3. Disable Automatic Script Execution",[13,268,269,270,213],{},"Add this to your ",[17,271,272],{},".npmrc",[239,274,279],{"className":275,"code":277,"language":278},[276],"language-text","ignore-scripts=true\n","text",[17,280,277],{"__ignoreMap":244},[13,282,283,284,287],{},"This prevents postinstall scripts from running automatically. You will need to manually run scripts for packages that require them (like ",[17,285,286],{},"node-gyp"," builds), but it eliminates the most dangerous attack vector.",[92,289,291],{"id":290},"_4-lock-your-dependencies","4. Lock Your Dependencies",[13,293,294,295,298],{},"Always commit your ",[17,296,297],{},"package-lock.json",". This ensures that:",[48,300,301,304,307],{},[51,302,303],{},"Everyone on your team installs the exact same versions",[51,305,306],{},"A compromised new version of a dependency does not silently slip in",[51,308,309],{},"You can diff changes to your dependency tree in code review",[92,311,313],{"id":312},"_5-use-scoped-packages-for-internal-code","5. Use Scoped Packages for Internal Code",[13,315,316],{},"If your company publishes internal packages, always use a scoped namespace:",[239,318,322],{"className":319,"code":320,"language":321,"meta":244,"style":244},"language-json shiki shiki-themes github-light github-dark","{\n  \"name\": \"@yourcompany/internal-utils\"\n}\n","json",[17,323,324,330,343],{"__ignoreMap":244},[248,325,326],{"class":250,"line":251},[248,327,329],{"class":328},"sVt8B","{\n",[248,331,333,337,340],{"class":250,"line":332},2,[248,334,336],{"class":335},"sj4cs","  \"name\"",[248,338,339],{"class":328},": ",[248,341,342],{"class":258},"\"@yourcompany/internal-utils\"\n",[248,344,346],{"class":250,"line":345},3,[248,347,348],{"class":328},"}\n",[13,350,351],{},"Scoped packages cannot be hijacked through dependency confusion.",[92,353,355],{"id":354},"_6-review-what-ai-installs","6. Review What AI Installs",[13,357,358,359,362],{},"After an AI coding session, review your ",[17,360,361],{},"package.json"," diff:",[239,364,366],{"className":241,"code":365,"language":243,"meta":244,"style":244},"git diff package.json\n",[17,367,368],{"__ignoreMap":244},[248,369,370,373,376],{"class":250,"line":251},[248,371,372],{"class":254},"git",[248,374,375],{"class":258}," diff",[248,377,378],{"class":258}," package.json\n",[13,380,381],{},"Look for packages you did not explicitly ask for. AI tools sometimes add dependencies you did not request, and those are the ones most likely to be wrong.",[383,384,386,403],"lesson-box",{"title":385},"The 30-Second Package Vetting Checklist",[48,387,388,391,394,397,400],{},[51,389,390],{},"Does it have more than 1,000 weekly downloads?",[51,392,393],{},"Has it been published for more than 6 months?",[51,395,396],{},"Does the publisher have other well-known packages?",[51,398,399],{},"Is there a linked, active GitHub repository?",[51,401,402],{},"Does the name match exactly what the official docs recommend?",[13,404,405],{},"If any answer is \"no,\" investigate further before installing.",[32,407,409],{"id":408},"this-is-not-theoretical","This Is Not Theoretical",[13,411,412],{},"Real developers got hit by OpenClaw. Credentials were stolen. Machines were compromised. Production databases were accessed through exfiltrated connection strings.",[13,414,415],{},"The npm registry removed the malicious packages after they were reported, but removal does not help the developers who already installed them. npm has no mechanism to remotely uninstall a package from your machine or notify you that something you installed was later flagged as malicious.",[40,417],{"label":418,"number":419},"Notifications npm sends you when a package you installed gets flagged as malware","0",[13,421,422,423,425],{},"This means the responsibility is entirely on you. Nobody is watching your ",[17,424,82],{}," folder except you.",[32,427,429],{"id":428},"the-broader-problem","The Broader Problem",[13,431,432],{},"OpenClaw was not the first npm supply chain attack, and it will not be the last. The npm registry has over 2 million packages. Anyone can publish a package in minutes. There is no review process, no code signing requirement, and minimal identity verification for publishers.",[13,434,435,436,438],{},"AI coding tools are accelerating the problem. They make it faster to build, which means more packages get installed with less scrutiny. The attack surface grows with every ",[17,437,26],{}," that goes unvetted.",[13,440,441],{},"The solution is not to stop using npm or to stop using AI tools. It is to add a verification step to your workflow. Five minutes of vetting can prevent weeks of incident response.",[443,444,445,452,458,464],"faq-section",{},[446,447,449],"faq-item",{"question":448},"What was the OpenClaw campaign?",[13,450,451],{},"OpenClaw was a sustained supply chain attack in 2025 where threat actors published approximately 900 malicious npm packages. These packages used typosquatting and dependency confusion to trick developers into installing them. Payloads included credential stealers, reverse shells, and data exfiltration scripts.",[446,453,455],{"question":454},"Can AI coding tools install malicious packages?",[13,456,457],{},"Yes. AI coding tools suggest packages based on name patterns and training data, not security audits. If a malicious package has a name similar to a popular one, an AI tool might suggest it. The AI has no way to verify whether a package is legitimate or malicious at the time of suggestion.",[446,459,461],{"question":460},"How do I check if I installed a malicious npm package?",[13,462,463],{},"Run npm audit to check for known vulnerabilities. Review your package-lock.json for unfamiliar packages. Check download counts and publisher history on npmjs.com. Look for packages with very low download counts, recent publish dates, and no GitHub repository linked.",[446,465,467],{"question":466},"What should I do if I find a malicious package in my project?",[13,468,469],{},"Remove it immediately with npm uninstall. Rotate any credentials or API keys that were accessible to the package. Check your system for unauthorized SSH keys or new user accounts. Review outbound network connections. If the package had postinstall scripts, assume your environment is compromised and rotate all secrets.",[471,472,473,479,484,489],"related-articles",{},[474,475],"related-card",{"description":476,"href":477,"title":478},"Understanding dependency vulnerabilities","/blog/vulnerabilities/vulnerable-dependencies","Vulnerable Dependencies",[474,480],{"description":481,"href":482,"title":483},"How to vet dependencies before installing","/blog/best-practices/third-party","Third-Party Security Best Practices",[474,485],{"description":486,"href":487,"title":488},"What to check after AI writes your code","/blog/checklists/ai-generated-code-checklist","AI-Generated Code Checklist",[474,490],{"description":491,"href":492,"title":493},"Common patterns from scanning vibe-coded apps","/blog/stories/100-scans-lessons","What I Learned Scanning 100 Projects",[495,496,499],"cta-box",{"href":497,"label":498},"/","Scan Your Project Now",[13,500,501],{},"OpenClaw proved that npm install is a trust decision. Find out what your AI-suggested dependencies are actually doing.",[503,504,505],"style",{},"html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}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 .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}",{"title":244,"searchDepth":332,"depth":332,"links":507},[508,509,513,518,526,527],{"id":34,"depth":332,"text":35},{"id":86,"depth":332,"text":87,"children":510},[511,512],{"id":94,"depth":345,"text":95},{"id":122,"depth":345,"text":123},{"id":139,"depth":332,"text":140,"children":514},[515,516,517],{"id":149,"depth":345,"text":150},{"id":166,"depth":345,"text":167},{"id":188,"depth":345,"text":189},{"id":198,"depth":332,"text":199,"children":519},[520,521,522,523,524,525],{"id":205,"depth":345,"text":206},{"id":236,"depth":345,"text":237},{"id":265,"depth":345,"text":266},{"id":290,"depth":345,"text":291},{"id":312,"depth":345,"text":313},{"id":354,"depth":345,"text":355},{"id":408,"depth":332,"text":409},{"id":428,"depth":332,"text":429},"stories","2026-03-13",null,"The OpenClaw campaign published roughly 900 malicious npm packages designed to steal credentials and install backdoors. Here's why vibe coders are especially at risk and how to protect yourself.",false,"md",[535,536,537,538],{"question":448,"answer":451},{"question":454,"answer":457},{"question":460,"answer":463},{"question":466,"answer":469},"openclaw malicious packages, npm supply chain attack, malicious npm packages 2025, vibe coding supply chain risk, npm security",{},true,"900 malicious npm packages targeted developers in 2025. AI coding tools made it worse.","/blog/stories/openclaw-malicious-packages","8 min read","[object Object]","BlogPosting",{"title":5,"description":531},{"loc":543},"blog/stories/openclaw-malicious-packages",[],"summary_large_image","ATP1OnTGmmllvbU4_t2CzjaKV63lkLy8xwiJukJquBo",1775843921341]