%26lt%3B%3Fphp%0A%2F%2A%2A%0A+%2A+Google+News+sitemap+%E2%80%94+https%3A%2F%2Fzbrandco.com%2Fnews-sitemap.xml%0A+%2A+Lists+posts+published+in+the+last+48+hours+%28Google+News+window%29.%0A+%2A%0A+%2A+%40package+ZBrandCo%0A+%2A%2F%0Aif+%28%21defined%28%26%23039%3BABSPATH%26%23039%3B%29%29+exit%3B%0A%0A%2F%2A+Route+%2B+one-time+rewrite+flush+%28runs+once+after+deploy%29.+%2A%2F%0Aadd_action%28%26%23039%3Binit%26%23039%3B%2C+function+%28%29+%7B%0A++++add_rewrite_rule%28%26%23039%3B%5Enews-sitemap%5C.xml%24%26%23039%3B%2C+%26%23039%3Bindex.php%3Fzb_news_sitemap%3D1%26%23039%3B%2C+%26%23039%3Btop%26%23039%3B%29%3B%0A++++add_rewrite_tag%28%26%23039%3B%25zb_news_sitemap%25%26%23039%3B%2C+%26%23039%3B1%26%23039%3B%29%3B%0A++++if+%28get_option%28%26%23039%3Bzb_news_sitemap_rev%26%23039%3B%29+%21%3D%3D+%26%23039%3B1%26%23039%3B%29+%7B%0A++++++++flush_rewrite_rules%28false%29%3B%0A++++++++update_option%28%26%23039%3Bzb_news_sitemap_rev%26%23039%3B%2C+%26%23039%3B1%26%23039%3B%29%3B%0A++++%7D%0A%7D%29%3B%0A%0A%2F%2A+Render.+Hook+parse_request+at+priority+0+%E2%80%94+BEFORE+AIOSEO%2C+which+otherwise+intercepts%0A+++%2Fnews-sitemap.xml+and+serves+an+empty+stub+%28its+Pro+News-Sitemap+addon+isn%26%23039%3Bt+installed%29.%0A+++Match+the+raw+request+path+so+we+win+regardless+of+rewrite-rule+resolution+order.+%2A%2F%0Aadd_action%28%26%23039%3Bparse_request%26%23039%3B%2C+function+%28%24wp%29+%7B%0A++++%24path+%3D+trim%28parse_url%28%24_SERVER%5B%26%23039%3BREQUEST_URI%26%23039%3B%5D+%3F%3F+%26%23039%3B%26%23039%3B%2C+PHP_URL_PATH%29+%3F%3A+%26%23039%3B%26%23039%3B%2C+%26%23039%3B%2F%26%23039%3B%29%3B%0A++++if+%28%24path+%21%3D%3D+%26%23039%3Bnews-sitemap.xml%26%23039%3B+%26amp%3B%26amp%3B+empty%28%24wp-%26gt%3Bquery_vars%5B%26%23039%3Bzb_news_sitemap%26%23039%3B%5D%29%29+return%3B%0A%0A++++%24pub_name+%3D+get_bloginfo%28%26%23039%3Bname%26%23039%3B%29%3B%0A++++%24lang+++++%3D+substr%28get_bloginfo%28%26%23039%3Blanguage%26%23039%3B%29%2C+0%2C+2%29+%3F%3A+%26%23039%3Ben%26%23039%3B%3B%0A%0A++++%24q+%3D+new+WP_Query%28%5B%0A++++++++%26%23039%3Bpost_type%26%23039%3B++++++%3D%26gt%3B+%26%23039%3Bpost%26%23039%3B%2C%0A++++++++%26%23039%3Bpost_status%26%23039%3B++++%3D%26gt%3B+%26%23039%3Bpublish%26%23039%3B%2C%0A++++++++%26%23039%3Bposts_per_page%26%23039%3B+%3D%26gt%3B+1000%2C%0A++++++++%26%23039%3Borderby%26%23039%3B++++++++%3D%26gt%3B+%26%23039%3Bdate%26%23039%3B%2C%0A++++++++%26%23039%3Border%26%23039%3B++++++++++%3D%26gt%3B+%26%23039%3BDESC%26%23039%3B%2C%0A++++++++%26%23039%3Bno_found_rows%26%23039%3B++%3D%26gt%3B+true%2C%0A++++++++%26%23039%3Bdate_query%26%23039%3B+++++%3D%26gt%3B+%5B%5B%26%23039%3Bafter%26%23039%3B+%3D%26gt%3B+%26%23039%3B48+hours+ago%26%23039%3B%2C+%26%23039%3Binclusive%26%23039%3B+%3D%26gt%3B+true%5D%5D%2C%0A++++%5D%29%3B%0A%0A++++if+%28%21headers_sent%28%29%29+header%28%26%23039%3BContent-Type%3A+application%2Fxml%3B+charset%3DUTF-8%26%23039%3B%29%3B%0A++++echo+%26%23039%3B%26lt%3B%3Fxml+version%3D%26quot%3B1.0%26quot%3B+encoding%3D%26quot%3BUTF-8%26quot%3B%3F%26gt%3B%26%23039%3B+.+%26quot%3B%5Cn%26quot%3B%3B%0A++++echo+%26%23039%3B%26lt%3Burlset+xmlns%3D%26quot%3Bhttp%3A%2F%2Fwww.sitemaps.org%2Fschemas%2Fsitemap%2F0.9%26quot%3B+%26%23039%3B%0A+++++++.+%26%23039%3Bxmlns%3Anews%3D%26quot%3Bhttp%3A%2F%2Fwww.google.com%2Fschemas%2Fsitemap-news%2F0.9%26quot%3B%26gt%3B%26%23039%3B+.+%26quot%3B%5Cn%26quot%3B%3B%0A%0A++++while+%28%24q-%26gt%3Bhave_posts%28%29%29+%7B%0A++++++++%24q-%26gt%3Bthe_post%28%29%3B%0A++++++++echo+%26quot%3B++%26lt%3Burl%26gt%3B%5Cn%26quot%3B%3B%0A++++++++echo+%26quot%3B++++%26lt%3Bloc%26gt%3B%26quot%3B+.+esc_url%28get_permalink%28%29%29+.+%26quot%3B%26lt%3B%2Floc%26gt%3B%5Cn%26quot%3B%3B%0A++++++++echo+%26quot%3B++++%26lt%3Bnews%3Anews%26gt%3B%5Cn%26quot%3B%3B%0A++++++++echo+%26quot%3B++++++%26lt%3Bnews%3Apublication%26gt%3B%5Cn%26quot%3B%3B%0A++++++++echo+%26quot%3B++++++++%26lt%3Bnews%3Aname%26gt%3B%26quot%3B+.+esc_html%28%24pub_name%29+.+%26quot%3B%26lt%3B%2Fnews%3Aname%26gt%3B%5Cn%26quot%3B%3B%0A++++++++echo+%26quot%3B++++++++%26lt%3Bnews%3Alanguage%26gt%3B%26quot%3B+.+esc_html%28%24lang%29+.+%26quot%3B%26lt%3B%2Fnews%3Alanguage%26gt%3B%5Cn%26quot%3B%3B%0A++++++++echo+%26quot%3B++++++%26lt%3B%2Fnews%3Apublication%26gt%3B%5Cn%26quot%3B%3B%0A++++++++echo+%26quot%3B++++++%26lt%3Bnews%3Apublication_date%26gt%3B%26quot%3B+.+esc_html%28get_the_date%28%26%23039%3Bc%26%23039%3B%29%29+.+%26quot%3B%26lt%3B%2Fnews%3Apublication_date%26gt%3B%5Cn%26quot%3B%3B%0A++++++++echo+%26quot%3B++++++%26lt%3Bnews%3Atitle%26gt%3B%26quot%3B+.+htmlspecialchars%28get_the_title%28%29%2C+ENT_NOQUOTES%2C+%26%23039%3BUTF-8%26%23039%3B%29+.+%26quot%3B%26lt%3B%2Fnews%3Atitle%26gt%3B%5Cn%26quot%3B%3B%0A++++++++echo+%26quot%3B++++%26lt%3B%2Fnews%3Anews%26gt%3B%5Cn%26quot%3B%3B%0A++++++++echo+%26quot%3B++%26lt%3B%2Furl%26gt%3B%5Cn%26quot%3B%3B%0A++++%7D%0A++++wp_reset_postdata%28%29%3B%0A++++echo+%26%23039%3B%26lt%3B%2Furlset%26gt%3B%26%23039%3B%3B%0A++++exit%3B%0A%7D%2C+1%29%3B%0A%0A%2F%2A+Advertise+it+to+crawlers.+Run+LAST+%28PHP_INT_MAX%29+so+it+appends+AFTER+AIOSEO%2C+which%0A+++otherwise+rebuilds+robots_txt+at+a+later+priority+and+drops+our+line.+Idempotent.+%2A%2F%0Aadd_filter%28%26%23039%3Brobots_txt%26%23039%3B%2C+function+%28%24output%29+%7B%0A++++%24line+%3D+%26quot%3BSitemap%3A+%26quot%3B+.+home_url%28%26%23039%3B%2Fnews-sitemap.xml%26%23039%3B%29%3B%0A++++if+%28strpos%28%24output%2C+%24line%29+%21%3D%3D+false%29+return+%24output%3B%0A++++return+rtrim%28%24output%2C+%26quot%3B%5Cn%26quot%3B%29+.+%26quot%3B%5Cn%26quot%3B+.+%24line+.+%26quot%3B%5Cn%26quot%3B%3B%0A%7D%2C+PHP_INT_MAX%2C+1%29%3B%0A%0A%2F%2A+Promote+single+posts%26%23039%3B+schema+%40type+from+Article%2FBlogPosting+to+NewsArticle+%E2%80%94+Google+News%0A+++strongly+prefers+NewsArticle.+AIOSEO+emits+the+default+%28BlogPosting%29+and+exposes+its+graph%0A+++via+the+aioseo_schema_output+filter%3B+we+rewrite+the+%40type+in+place+%28no+duplicate+schema%29.%0A+++No-op+if+the+filter+name+changes+in+a+future+AIOSEO+version+%28harmless%29.+%2A%2F%0Aadd_filter%28%26%23039%3Baioseo_schema_output%26%23039%3B%2C+function+%28%24schema%29+%7B%0A++++if+%28%21is_singular%28%26%23039%3Bpost%26%23039%3B%29+%7C%7C+%21is_array%28%24schema%29%29+return+%24schema%3B%0A++++%24nodes+%3D+%28%21empty%28%24schema%5B%26%23039%3B%40graph%26%23039%3B%5D%29+%26amp%3B%26amp%3B+is_array%28%24schema%5B%26%23039%3B%40graph%26%23039%3B%5D%29%29+%3F+%26%23039%3B%40graph%26%23039%3B+%3A+null%3B%0A++++%24list++%3D+%24nodes+%3F+%24schema%5B%26%23039%3B%40graph%26%23039%3B%5D+%3A+%24schema%3B%0A++++foreach+%28%24list+as+%24k+%3D%26gt%3B+%24node%29+%7B%0A++++++++if+%28is_array%28%24node%29+%26amp%3B%26amp%3B+%21empty%28%24node%5B%26%23039%3B%40type%26%23039%3B%5D%29%0A++++++++++++%26amp%3B%26amp%3B+in_array%28%24node%5B%26%23039%3B%40type%26%23039%3B%5D%2C+%5B%26%23039%3BBlogPosting%26%23039%3B%2C+%26%23039%3BArticle%26%23039%3B%5D%2C+true%29%29+%7B%0A++++++++++++%24list%5B%24k%5D%5B%26%23039%3B%40type%26%23039%3B%5D+%3D+%26%23039%3BNewsArticle%26%23039%3B%3B%0A++++++++%7D%0A++++%7D%0A++++if+%28%24nodes%29+%7B+%24schema%5B%26%23039%3B%40graph%26%23039%3B%5D+%3D+%24list%3B+%7D+else+%7B+%24schema+%3D+%24list%3B+%7D%0A++++return+%24schema%3B%0A%7D%2C+20%29%3B%0A%0A%2F%2A+Register+our+custom+news+sitemap+inside+AIOSEO%26%23039%3Bs+sitemap+index+so+crawlers+can%0A+++discover+it+from+%2Fsitemap.xml+instead+of+relying+on+robots.txt+alone.+%2A%2F%0Aadd_filter%28%26%23039%3Baioseo_sitemap_indexes%26%23039%3B%2C+function+%28%24indexes%29+%7B%0A++++%24q+%3D+new+WP_Query%28%5B%0A++++++++%26%23039%3Bpost_type%26%23039%3B++++++%3D%26gt%3B+%26%23039%3Bpost%26%23039%3B%2C%0A++++++++%26%23039%3Bpost_status%26%23039%3B++++%3D%26gt%3B+%26%23039%3Bpublish%26%23039%3B%2C%0A++++++++%26%23039%3Bposts_per_page%26%23039%3B+%3D%26gt%3B+1000%2C%0A++++++++%26%23039%3Borderby%26%23039%3B++++++++%3D%26gt%3B+%26%23039%3Bdate%26%23039%3B%2C%0A++++++++%26%23039%3Border%26%23039%3B++++++++++%3D%26gt%3B+%26%23039%3BDESC%26%23039%3B%2C%0A++++++++%26%23039%3Bno_found_rows%26%23039%3B++%3D%26gt%3B+true%2C%0A++++++++%26%23039%3Bdate_query%26%23039%3B+++++%3D%26gt%3B+%5B%5B%26%23039%3Bafter%26%23039%3B+%3D%26gt%3B+%26%23039%3B48+hours+ago%26%23039%3B%2C+%26%23039%3Binclusive%26%23039%3B+%3D%26gt%3B+true%5D%5D%2C%0A++++++++%26%23039%3Bfields%26%23039%3B+++++++++%3D%26gt%3B+%26%23039%3Bids%26%23039%3B%2C%0A++++%5D%29%3B%0A%0A++++%24lastmod+%3D+current_time%28%26%23039%3Bmysql%26%23039%3B%2C+1%29%3B%0A++++if+%28function_exists%28%26%23039%3Baioseo%26%23039%3B%29+%26amp%3B%26amp%3B+method_exists%28aioseo%28%29-%26gt%3Bhelpers%2C+%26%23039%3BdateTimeToIso8601%26%23039%3B%29%29+%7B%0A++++++++%24lastmod+%3D+aioseo%28%29-%26gt%3Bhelpers-%26gt%3BdateTimeToIso8601%28%24lastmod%29%3B%0A++++%7D+else+%7B%0A++++++++%24lastmod+%3D+gmdate%28%26%23039%3Bc%26%23039%3B%2C+strtotime%28%24lastmod%29%29%3B%0A++++%7D%0A%0A++++%24indexes%5B%5D+%3D+%5B%0A++++++++%26%23039%3Bloc%26%23039%3B+++++%3D%26gt%3B+home_url%28%26%23039%3B%2Fnews-sitemap.xml%26%23039%3B%29%2C%0A++++++++%26%23039%3Blastmod%26%23039%3B+%3D%26gt%3B+%24lastmod%2C%0A++++++++%26%23039%3Bcount%26%23039%3B+++%3D%26gt%3B+count%28%24q-%26gt%3Bposts%29%2C%0A++++%5D%3B%0A%0A++++return+%24indexes%3B%0A%7D%29%3B%0A%0A%2F%2A+Fix+AIOSEO+taxonomy+sitemap+lastmod%3A+fall+back+to+the+latest+associated+post%0A+++modification+date+when+the+term+itself+has+no+usable+modified+date.+This+avoids%0A+++the+SEO-negative+1970-01-01+epoch+default+that+appears+on+fresh+category%2Ftag+terms.+%2A%2F%0Aadd_filter%28%26%23039%3Baioseo_sitemap_entry_lastmod%26%23039%3B%2C+function+%28%24date%2C+%24type%2C+%24object%29+%7B%0A++++if+%28%21in_array%28%24type%2C+%5B%26%23039%3Bcategory%26%23039%3B%2C+%26%23039%3Bpost_tag%26%23039%3B%5D%2C+true%29%29+%7B%0A++++++++return+%24date%3B%0A++++%7D%0A%0A++++%24term+%3D+%24object%3B%0A++++if+%28%21%28%24term+instanceof+WP_Term%29+%7C%7C+empty%28%24term-%26gt%3Bterm_id%29%29+%7B%0A++++++++return+%24date%3B%0A++++%7D%0A%0A++++if+%28empty%28%24term-%26gt%3Bcount%29%29+%7B%0A++++++++return+null%3B%0A++++%7D%0A%0A++++%24ts+%3D+%21empty%28%24term-%26gt%3Bterm_modified%29+%3F+strtotime%28%24term-%26gt%3Bterm_modified%29+%3A+false%3B%0A++++if+%28%24ts+%26amp%3B%26amp%3B+%24date+%26amp%3B%26amp%3B+strtotime%28%24date%29+%26gt%3B%3D+strtotime%28%26%23039%3B2024-01-01+00%3A00%3A00%26%23039%3B%29%29+%7B%0A++++++++return+%24date%3B%0A++++%7D%0A%0A++++%24latest+%3D+%28new+WP_Query%28%5B%0A++++++++%26%23039%3Bpost_type%26%23039%3B++++++%3D%26gt%3B+%26%23039%3Bpost%26%23039%3B%2C%0A++++++++%26%23039%3Bpost_status%26%23039%3B++++%3D%26gt%3B+%26%23039%3Bpublish%26%23039%3B%2C%0A++++++++%26%23039%3Bposts_per_page%26%23039%3B+%3D%26gt%3B+1%2C%0A++++++++%26%23039%3Bno_found_rows%26%23039%3B++%3D%26gt%3B+true%2C%0A++++++++%26%23039%3Bfields%26%23039%3B+++++++++%3D%26gt%3B+%26%23039%3Bids%26%23039%3B%2C%0A++++++++%26%23039%3Borderby%26%23039%3B++++++++%3D%26gt%3B+%26%23039%3Bmodified%26%23039%3B%2C%0A++++++++%26%23039%3Border%26%23039%3B++++++++++%3D%26gt%3B+%26%23039%3BDESC%26%23039%3B%2C%0A++++++++%26%23039%3Btax_query%26%23039%3B++++++%3D%26gt%3B+%5B%5B%0A++++++++++++%26%23039%3Btaxonomy%26%23039%3B+%3D%26gt%3B+%24type%2C%0A++++++++++++%26%23039%3Bfield%26%23039%3B++++%3D%26gt%3B+%26%23039%3Bterm_id%26%23039%3B%2C%0A++++++++++++%26%23039%3Bterms%26%23039%3B++++%3D%26gt%3B+%5B%24term-%26gt%3Bterm_id%5D%2C%0A++++++++%5D%5D%2C%0A++++%5D%29%29-%26gt%3Bposts%5B0%5D+%3F%3F+null%3B%0A%0A++++if+%28%21empty%28%24latest%29%29+%7B%0A++++++++return+get_post_datetime%28%24latest%2C+%26%23039%3Bmodified%26%23039%3B%29-%26gt%3Bformat%28%26%23039%3Bc%26%23039%3B%29%3B%0A++++%7D%0A%0A++++return+%24date%3B%0A%7D%2C+20%2C+3%29%3B%0A%0A%2F%2A+Google+AdSense+%E2%80%94+verification+%2B+ad-serving+tag%2C+output+in+%26lt%3Bhead%26gt%3B+on+every+page.%0A+++%28Placed+in+this+stable+include+rather+than+functions.php+%2F+site-features.php+to+avoid%0A+++colliding+with+the+concurrently-edited+theme+files%3B+it+is+a+Google+head+integration.%29+%2A%2F%0Aadd_action%28%26%23039%3Bwp_head%26%23039%3B%2C+function+%28%29+%7B%0A++++echo+%26%23039%3B%26lt%3Bscript+async+src%3D%26quot%3Bhttps%3A%2F%2Fpagead2.googlesyndication.com%2Fpagead%2Fjs%2Fadsbygoogle.js%3Fclient%3Dca-pub-6533848202824497%26quot%3B+crossorigin%3D%26quot%3Banonymous%26quot%3B%26gt%3B%26lt%3B%2Fscript%26gt%3B%26%23039%3B+.+%26quot%3B%5Cn%26quot%3B%3B%0A%7D%2C+1%29%3B%0A 140+ Mastra npm Packages Hit in Supply Chain Attack - zBrandco
AI

140+ Mastra npm Packages Hit in Supply Chain Attack

140+ Mastra npm Packages Hit in Supply Chain Attack

Image: AI-generated editorial image

A supply chain attack on June 17, 2026, injected a malicious easy-day-js dependency into 140+ packages across the @mastra npm scope, deploying a postinstall dropper that steals cryptocurrency wallets, API keys, and CI/CD credentials from developer workstations Microsoft Security Blog. The attack, attributed with high confidence to the North Korean state-sponsored group Sapphire Sleet, exploited a dormant contributor account that retained publish access to the entire Mastra ecosystem Snyk.

The blast radius includes high-traffic packages such as @mastra/core (4 million monthly downloads) and mastra (1.5 million monthly downloads). Combined weekly downloads across all affected packages exceed 1.1 million, making this one of the most broadly impactful npm supply chain compromises of 2026 StepSecurity.

Mastra Npm Supply Chain Attack: How the Attack Unfolded

The attack chain required no zero-day vulnerability. Instead, the attacker compromised the npm account ehindero, a former Mastra contributor whose publish rights to the @mastra scope had never been revoked Snyk. npm does not expire scope publish permissions for inactive accounts, so a single stolen credential was sufficient to push updates to every package in the ecosystem.

On June 16, 2026, the attacker published a clean easy-day-js@1.11.21 as a bait package. This version was a byte-for-byte clone of the legitimate dayjs library (57 million weekly downloads), including matching metadata, repository URL, and author fields StepSecurity. The attacker then injected "easy-day-js": "^1.11.21" into the package.json files of 140+ @mastra packages and republished them with easy-day-js tagged as latest.

The critical detail is npm’s SemVer resolution. When users ran npm install, the caret range ^1.11.21 automatically resolved to easy-day-js@1.11.22, which the attacker uploaded on June 17 at 01:01 UTC with a malicious postinstall hook Microsoft Security Blog.

The Malicious Payload

The weaponized easy-day-js@1.11.22 contains a 4,572-byte obfuscated setup.cjs dropper that executes automatically during installation, even if the compromised package is never explicitly imported in application code Microsoft Security Blog. The dropper performs the following actions in sequence:

  1. Disables TLS certificate validation by setting NODE_TLS_REJECT_UNAUTHORIZED=0, allowing the payload to communicate with attacker infrastructure over a self-signed certificate on a raw IP address.
  2. Writes marker files to ~/.pkg_history and ~/.pkg_logs to track infected hosts.
  3. Downloads a second-stage payload from https://23.254.164[.]92:8000/update/49890878.
  4. Writes the payload to the system temporary directory and executes it as a detached, window-hidden Node.js process.
  5. Self-deletes to reduce forensic traces.

The second-stage payload is an obfuscated cross-platform tool that targets cryptocurrency wallets and developer credentials. It reads Chrome, Brave, and Edge browser profiles to extract data from MetaMask, Phantom, Solflare, Coinbase Wallet, OKX, and Keplr extensions. It also collects system metadata, LLM API keys, cloud provider credentials, database connection strings, and CI/CD tokens StepSecurity.

Scope and Impact

The attacker republished 142 packages across the Mastra ecosystem in a tightly automated sequence between 01:12 and 02:39 UTC on June 17 Snyk. Key compromised packages include:

  • @mastra/core@1.42.1 — core framework engine (4M monthly downloads)
  • @mastra/memory@1.20.4 — memory management layer
  • @mastra/server@2.1.1 — runtime server component
  • @mastra/deployer@1.42.1 — deployment tooling
  • mastra@1.13.1 — top-level framework package
  • @mastra/rag@2.2.2 — retrieval-augmented generation integrations
  • @mastra/mcp@1.10.1 — Model Context Protocol integrations

The Mastra team responded within hours, revoking the attacker’s publish access, removing the malicious easy-day-js package from npm, and forward-rolling clean versions for all affected packages Snyk. Source code in the Mastra GitHub repository was not modified; the malicious dependency was injected only into published npm tarballs.

What Developers Should Do

If you installed any @mastra package, or any package that depends on @mastra, on or after June 17, 2026, treat your environment as potentially compromised StepSecurity.

Immediate remediation steps include:

  • Audit your package-lock.json and npm-shrinkwrap.json for easy-day-js entries.
  • Delete node_modules and reinstall from a clean lockfile.
  • Rotate all credentials exposed on the affected machine: LLM API keys, GitHub tokens, AWS keys, npm tokens, and cryptocurrency wallet seed phrases.
  • Review system persistence mechanisms, including scheduled tasks, launch agents, and Node.js background processes.

Bottom Line

The Mastra supply chain attack was not a sophisticated zero-day exploit. It was a credential hygiene failure: a dormant npm account retained publish access for months, and the attacker used it to inject a single malicious dependency across an entire package scope. The postinstall hook converted every installation command into an automatic compromise vector, and the payload went after the most sensitive assets developers keep on their workstations.

This incident reinforces a rule that grows more important every year: scope publish permissions should expire when contributors become inactive. Until npm or registry operators enforce that standard, every open source project with a broad publish scope is one dormant account away from a similar event.

[^1]: Microsoft Security Blog — https://www.microsoft.com/en-us/security/blog/2026/06/17/postinstall-payload-inside-mastra-npm-supply-chain-compromise/.
[^2]: Snyk — https://snyk.io/blog/a-forgotten-contributor-account-compromised-the-entire-mastra-npm-package-scope/.
[^3]: StepSecurity — https://www.stepsecurity.io/blog/mastra-npm-packages-compromised-using-easy-day-js.

We may earn commission from affiliate links at no extra cost to you. Last updated: Jun 26, 2026.
Aira

Founding Editor and Publisher of ZBrandCo, covering artificial intelligence, open-source software, and the developer tools people actually use. Signal over hype: every story starts from a primary source and explains why it matters. ZBrandCo runs no paid reviews and no affiliate links. Tips and corrections: editorial@zbrandco.com.