<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Sourabh Katti</title>
    <description>The latest articles on DEV Community by Sourabh Katti (@sourabh_katti_f87f3875e01).</description>
    <link>https://dev.to/sourabh_katti_f87f3875e01</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3640048%2F01cb599a-582e-4600-8fa8-f9941f45be91.jpg</url>
      <title>DEV Community: Sourabh Katti</title>
      <link>https://dev.to/sourabh_katti_f87f3875e01</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/sourabh_katti_f87f3875e01"/>
    <language>en</language>
    <item>
      <title>How I Built an AI Password Automation Tool with browser-use</title>
      <dc:creator>Sourabh Katti</dc:creator>
      <pubDate>Sat, 27 Dec 2025 02:48:53 +0000</pubDate>
      <link>https://dev.to/sourabh_katti_f87f3875e01/how-i-built-an-ai-password-automation-tool-with-browser-use-4ib1</link>
      <guid>https://dev.to/sourabh_katti_f87f3875e01/how-i-built-an-ai-password-automation-tool-with-browser-use-4ib1</guid>
      <description>&lt;p&gt;# How I Built an AI Password Automation Tool with browser-use&lt;/p&gt;

&lt;p&gt;## The Problem&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Breach notifications require changing 50+ passwords&lt;/li&gt;
&lt;li&gt;Manual process takes 4-8 hours&lt;/li&gt;
&lt;li&gt;Most people just... don't do it&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;## The Solution: AI Browser Automation&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;browser-use library (89.1% WebVoyager benchmark)&lt;/li&gt;
&lt;li&gt;Vision + action: AI sees screen, clicks buttons, types text&lt;/li&gt;
&lt;li&gt;Works on sites it's never seen before&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;## Architecture&lt;br&gt;
  [Code snippets showing browser-use integration]&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Electron frontend&lt;/li&gt;
&lt;li&gt;Python backend&lt;/li&gt;
&lt;li&gt;Local execution for security&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;## Challenges Solved&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Anti-bot detection → Chrome profile integration&lt;/li&gt;
&lt;li&gt;2FA handling → Pause for human, resume&lt;/li&gt;
&lt;li&gt;Password security → Zero-knowledge, memory-only&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;## Results&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;89% automated success rate&lt;/li&gt;
&lt;li&gt;30 minutes vs 6 hours&lt;/li&gt;
&lt;li&gt;Zero passwords transmitted to cloud&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;## Try It&lt;br&gt;
  Built this into The Password App for macOS.&lt;br&gt;
  Free tier: 5 passwords/month&lt;br&gt;
  &lt;a href="https://thepassword.app" rel="noopener noreferrer"&gt;https://thepassword.app&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>showdev</category>
      <category>security</category>
      <category>automation</category>
    </item>
    <item>
      <title>Building AI browser agents that actually work: lessons from automating password changes</title>
      <dc:creator>Sourabh Katti</dc:creator>
      <pubDate>Tue, 02 Dec 2025 17:22:01 +0000</pubDate>
      <link>https://dev.to/sourabh_katti_f87f3875e01/building-ai-browser-agents-that-actually-work-lessons-from-automating-password-changes-237g</link>
      <guid>https://dev.to/sourabh_katti_f87f3875e01/building-ai-browser-agents-that-actually-work-lessons-from-automating-password-changes-237g</guid>
      <description>&lt;p&gt;Everyone talks about AI agents coding, writing, and chatting. But what about agents that actually &lt;em&gt;do things&lt;/em&gt; in the real world - like navigating websites, clicking buttons, and filling forms?&lt;/p&gt;

&lt;p&gt;I spent the last few months building an AI agent that automates password changes across websites. Here's what I learned about making browser automation agents that actually work in production.&lt;/p&gt;

&lt;h2&gt;
  
  
  The problem with traditional browser automation
&lt;/h2&gt;

&lt;p&gt;Selenium and Playwright are great for predictable workflows. But real websites are messy:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Login flows change without warning&lt;/li&gt;
&lt;li&gt;CAPTCHAs appear randomly&lt;/li&gt;
&lt;li&gt;Elements have dynamic IDs&lt;/li&gt;
&lt;li&gt;Modals pop up unexpectedly&lt;/li&gt;
&lt;li&gt;Sites detect and block automation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Traditional scripted automation breaks constantly. You end up playing whack-a-mole with selectors.&lt;/p&gt;

&lt;h2&gt;
  
  
  Enter AI browser agents
&lt;/h2&gt;

&lt;p&gt;The idea is simple: instead of scripting every click, let an LLM observe the page and decide what to do next. Tools like &lt;a href="https://github.com/browser-use/browser-use" rel="noopener noreferrer"&gt;browser-use&lt;/a&gt; make this surprisingly accessible.&lt;/p&gt;

&lt;p&gt;Here's the basic architecture:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;browser_use&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Agent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Browser&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;langchain_openai&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ChatOpenAI&lt;/span&gt;

&lt;span class="c1"&gt;# Initialize browser and LLM
&lt;/span&gt;&lt;span class="n"&gt;browser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Browser&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;llm&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;ChatOpenAI&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;gpt-4o-mini&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Create agent with a task
&lt;/span&gt;&lt;span class="n"&gt;agent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Agent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;task&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Navigate to example.com and click the login button&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;llm&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;llm&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;browser&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;browser&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Run it
&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;agent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The agent takes screenshots, converts them to an accessibility tree, and asks the LLM: "Given this page state and your goal, what should you do next?"&lt;/p&gt;

&lt;h2&gt;
  
  
  Five hard lessons from production
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Constrain the agent ruthlessly
&lt;/h3&gt;

&lt;p&gt;My first agents were too open-ended. I'd say "change the password on this site" and watch in horror as the agent:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Opened new tabs to search for help articles&lt;/li&gt;
&lt;li&gt;Clicked "Forgot Password" links&lt;/li&gt;
&lt;li&gt;Navigated to completely unrelated pages&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt; Add strict rules to your prompts:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;task&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
Change the password on example.com.

STRICT RULES:
- DO NOT open new tabs
- DO NOT use search engines
- DO NOT click &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Forgot Password&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;
- If stuck after 5 actions, STOP and report failure
&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Never expose sensitive data to the LLM
&lt;/h3&gt;

&lt;p&gt;This was my biggest security mistake. Early versions included passwords in the task prompt:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# DON'T DO THIS
&lt;/span&gt;&lt;span class="n"&gt;task&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Log in with password: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;actual_password&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That password now lives in your LLM provider's logs forever.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt; Use custom actions that inject credentials without exposing them to the model:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="nd"&gt;@browser.action&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Enter password in focused field&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;enter_password&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;credentials&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="c1"&gt;# Password passed through secure channel
&lt;/span&gt;    &lt;span class="c1"&gt;# LLM only sees action name, never the value
&lt;/span&gt;    &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;keyboard&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;credentials&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;password&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. The DOM is not your friend
&lt;/h3&gt;

&lt;p&gt;Screen readers and accessibility trees are your friends. They provide semantic meaning that raw HTML doesn't.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Instead of parsing HTML for buttons
&lt;/span&gt;&lt;span class="n"&gt;soup&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find_all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;button&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;class_&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;submit-btn-v2&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Use accessibility snapshots
&lt;/span&gt;&lt;span class="n"&gt;snapshot&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;accessibility&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;snapshot&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="c1"&gt;# Returns structured data: button "Submit", textbox "Email", etc.
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This makes your agent more robust to CSS class changes and layout shifts.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Implement aggressive timeouts
&lt;/h3&gt;

&lt;p&gt;AI agents can get stuck in loops. Without timeouts, they'll burn through your API budget clicking the same broken element forever.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;agent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Agent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;task&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;task&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;llm&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;llm&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;max_steps&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;15&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;           &lt;span class="c1"&gt;# Hard limit on actions
&lt;/span&gt;    &lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;120&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;            &lt;span class="c1"&gt;# Total time limit
&lt;/span&gt;    &lt;span class="n"&gt;step_timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;        &lt;span class="c1"&gt;# Per-action limit
&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  5. Log everything (but redact credentials)
&lt;/h3&gt;

&lt;p&gt;When things go wrong (and they will), you need visibility. But you also can't log passwords.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;log_action&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="c1"&gt;# Redact anything that looks sensitive
&lt;/span&gt;    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;password&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;lower&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
        &lt;span class="n"&gt;target&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;[REDACTED]&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Action: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; -&amp;gt; &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  When AI agents beat traditional automation
&lt;/h2&gt;

&lt;p&gt;AI browser agents shine when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Sites change frequently&lt;/strong&gt; - The agent adapts to new layouts&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Workflows vary by account&lt;/strong&gt; - Different users see different flows&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Edge cases are endless&lt;/strong&gt; - 2FA prompts, CAPTCHA, cookie banners&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;You'd rather not maintain selectors&lt;/strong&gt; - Let the AI figure it out&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;They struggle when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Speed matters&lt;/strong&gt; - LLM calls add latency&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cost matters&lt;/strong&gt; - Each action = API call&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reliability must be 100%&lt;/strong&gt; - AI agents are probabilistic&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The architecture that worked
&lt;/h2&gt;

&lt;p&gt;After many iterations, here's what works:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Playwright for browser control&lt;/strong&gt; - Fast, reliable, good DevTools integration&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GPT-4o-mini for decisions&lt;/strong&gt; - Good enough for navigation, much cheaper than GPT-4&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Accessibility tree for page state&lt;/strong&gt; - More stable than DOM parsing&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Custom actions for sensitive operations&lt;/strong&gt; - Keep credentials out of LLM context&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Aggressive constraints&lt;/strong&gt; - Limit what the agent can do&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Real-world results
&lt;/h2&gt;

&lt;p&gt;Building &lt;a href="https://thepassword.app" rel="noopener noreferrer"&gt;The Password App&lt;/a&gt;, I've now run thousands of password change operations. Success rates:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Simple sites (basic forms): ~95%&lt;/li&gt;
&lt;li&gt;Complex sites (multi-step, 2FA): ~70%&lt;/li&gt;
&lt;li&gt;Anti-bot protected sites: ~40%&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The 40% is brutal but honest. Sites with Cloudflare, DataDome, or aggressive bot detection still win most battles.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's next for AI browser agents
&lt;/h2&gt;

&lt;p&gt;We're still early. Current limitations:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;CAPTCHA&lt;/strong&gt; - The eternal enemy&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Bot detection&lt;/strong&gt; - Getting harder to evade&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cost&lt;/strong&gt; - $0.01-0.05 per operation adds up&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Latency&lt;/strong&gt; - 30-60 seconds for simple tasks&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But the trajectory is clear. As vision models improve and costs drop, AI browser agents will handle increasingly complex web tasks.&lt;/p&gt;




&lt;p&gt;If you're building AI browser automation, I'd love to hear what's working for you. Drop a comment or find me on &lt;a href="https://twitter.com/thepasswordapp" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Building something similar? &lt;a href="https://thepassword.app" rel="noopener noreferrer"&gt;The Password App&lt;/a&gt; automates password changes using the techniques described here. Free tier available.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>python</category>
      <category>automation</category>
      <category>showdev</category>
    </item>
    <item>
      <title>Why We Don't Use Browser Extensions: The Clickjacking Problem</title>
      <dc:creator>Sourabh Katti</dc:creator>
      <pubDate>Tue, 02 Dec 2025 01:33:14 +0000</pubDate>
      <link>https://dev.to/sourabh_katti_f87f3875e01/why-we-dont-use-browser-extensions-the-clickjacking-problem-379i</link>
      <guid>https://dev.to/sourabh_katti_f87f3875e01/why-we-dont-use-browser-extensions-the-clickjacking-problem-379i</guid>
      <description>&lt;p&gt;At DEF CON 2025, security researchers disclosed a clickjacking vulnerability affecting the browser extensions of 1Password, Bitwarden, and LastPass. The attack allows malicious websites to steal autofilled credentials by overlaying invisible elements on password fields.&lt;/p&gt;

&lt;p&gt;Here's what happened, why browser extensions are inherently vulnerable to this attack class, and why we chose a completely different architecture for The Password App.&lt;/p&gt;

&lt;h2&gt;
  
  
  The clickjacking attack explained
&lt;/h2&gt;

&lt;p&gt;Clickjacking works by placing an invisible iframe or element over a legitimate page. When you think you're clicking a "Submit" button, you're actually clicking a hidden element that performs a different action.&lt;/p&gt;

&lt;p&gt;For password managers, the attack flow looks like this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;You visit a malicious website that mimics a legitimate login page&lt;/li&gt;
&lt;li&gt;Your password manager's browser extension autofills your credentials&lt;/li&gt;
&lt;li&gt;An invisible iframe overlays the password field&lt;/li&gt;
&lt;li&gt;When you click "Login," the hidden iframe captures your autofilled credentials&lt;/li&gt;
&lt;li&gt;Your password is sent to the attacker's server&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The user sees nothing wrong. The page looks normal. The password manager worked as expected. But the credentials are now in the attacker's hands.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why browser extensions are vulnerable
&lt;/h2&gt;

&lt;p&gt;Browser extensions operate in a hostile environment by design. They must:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Trust the page DOM&lt;/strong&gt;: Extensions interact with webpage elements they don't control&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Autofill into foreign contexts&lt;/strong&gt;: Credentials are injected into third-party content&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Run in the browser's sandbox&lt;/strong&gt;: Limited ability to verify page authenticity&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;React to user actions&lt;/strong&gt;: Must respond when users interact with password fields&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This creates an inherent tension. The extension must be helpful (autofill passwords) while operating in an environment controlled by potentially malicious actors (the webpage).&lt;/p&gt;

&lt;h3&gt;
  
  
  The technical problem
&lt;/h3&gt;

&lt;p&gt;Password manager extensions typically:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Detect login forms by examining DOM elements&lt;/li&gt;
&lt;li&gt;Match the current URL against saved credentials&lt;/li&gt;
&lt;li&gt;Inject the password into the detected field&lt;/li&gt;
&lt;li&gt;Trust that the field is what it appears to be&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Step 4 is where clickjacking attacks succeed. The extension fills the "real" password field, but that field is actually visible through an invisible iframe that captures the input.&lt;/p&gt;

&lt;p&gt;Even sophisticated extensions with phishing detection can be fooled because:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The URL is legitimate (the attack works on real sites with injected malicious ads)&lt;/li&gt;
&lt;li&gt;The DOM appears correct (until you account for invisible overlays)&lt;/li&gt;
&lt;li&gt;User behavior is normal (clicking buttons they can see)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The 1Password, Bitwarden, LastPass disclosure
&lt;/h2&gt;

&lt;p&gt;The DEF CON research demonstrated attacks against all three major password manager extensions:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Extension&lt;/th&gt;
&lt;th&gt;Vulnerable&lt;/th&gt;
&lt;th&gt;Attack vector&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1Password&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Invisible iframe overlay&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Bitwarden&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;CSS opacity manipulation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;LastPass&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Z-index layering attack&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;At the time of this writing, none of the affected vendors have issued public statements about remediation timelines.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why we chose desktop-only
&lt;/h2&gt;

&lt;p&gt;When we built &lt;a href="https://thepassword.app" rel="noopener noreferrer"&gt;The Password App&lt;/a&gt;, we made a deliberate architectural decision: &lt;strong&gt;no browser extensions&lt;/strong&gt;. Here's why:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Controlled environment
&lt;/h3&gt;

&lt;p&gt;A desktop application runs in an environment we control. We're not fighting against malicious webpage code because we're not executing in the webpage context.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Browser extension: [Your Code] → [Webpage DOM] → [Unknown scripts]
Desktop app:       [Your Code] → [Our UI] → [Isolated browser instance]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. No autofill, no autofill attacks
&lt;/h3&gt;

&lt;p&gt;The Password App doesn't autofill passwords into webpages. Instead, our AI agent navigates to sites and fills credentials through a controlled browser instance that we manage.&lt;/p&gt;

&lt;p&gt;The key difference:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Extension approach&lt;/strong&gt;: Inject credentials into untrusted page context&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Our approach&lt;/strong&gt;: Control the entire browser session in isolation&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  3. Visual confirmation
&lt;/h3&gt;

&lt;p&gt;Because password changes happen in a visible browser window, you can see exactly what's happening. There's no invisible iframe attack possible when you're watching the AI navigate to the password change form.&lt;/p&gt;

&lt;h2&gt;
  
  
  How clickjacking fails against our architecture
&lt;/h2&gt;

&lt;p&gt;Let's walk through why a clickjacking attack can't steal credentials from The Password App:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 1: Attacker sets up malicious overlay&lt;/strong&gt;&lt;br&gt;
The attacker creates a page with hidden elements designed to capture password input.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 2: User initiates password change&lt;/strong&gt;&lt;br&gt;
You select an account in The Password App and click "Change Password."&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 3: Isolated browser opens&lt;/strong&gt;&lt;br&gt;
Our AI agent opens a fresh browser instance with no extensions. It navigates directly to the account settings URL—not a malicious page.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 4: Credentials never touch the webpage&lt;/strong&gt;&lt;br&gt;
The AI identifies the password field and calls our secure credential injection function. The password travels through a separate channel, never entering the DOM in a way that JavaScript can intercept.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 5: Even if compromised...&lt;/strong&gt;&lt;br&gt;
Even if a malicious script somehow ran in our controlled browser, it couldn't exfiltrate the password because:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Domain locking prevents navigation to attacker URLs&lt;/li&gt;
&lt;li&gt;The AI never has the password value in its context&lt;/li&gt;
&lt;li&gt;Network requests are restricted to the target domain&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Multiple defensive layers mean no single failure compromises credentials.&lt;/p&gt;

&lt;h2&gt;
  
  
  The broader problem with browser-based security
&lt;/h2&gt;

&lt;p&gt;This isn't just about clickjacking. Browser extensions face a constant stream of attack vectors:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Attack type&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;th&gt;Extension vulnerable?&lt;/th&gt;
&lt;th&gt;Desktop app vulnerable?&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Clickjacking&lt;/td&gt;
&lt;td&gt;Invisible overlays steal input&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DOM manipulation&lt;/td&gt;
&lt;td&gt;Malicious scripts modify forms&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No (isolated browser)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Extension compromise&lt;/td&gt;
&lt;td&gt;Malicious update pushed to extension&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No (local only)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cross-site scripting&lt;/td&gt;
&lt;td&gt;Attacker injects scripts into trusted sites&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No (domain locked)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Man-in-the-browser&lt;/td&gt;
&lt;td&gt;Malware modifies browser behavior&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Partially (isolated process)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The fundamental issue is that browser extensions must coexist with arbitrary webpage code. Every new web API, every browser update, every clever attacker creates new potential vulnerabilities.&lt;/p&gt;

&lt;h2&gt;
  
  
  What this means for users
&lt;/h2&gt;

&lt;p&gt;If you use a password manager with a browser extension, you should:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Keep extensions updated&lt;/strong&gt; - Vendors will likely patch this vulnerability&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Be cautious on unfamiliar sites&lt;/strong&gt; - Don't autofill on sites you don't fully trust&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Consider the attack surface&lt;/strong&gt; - Browser extensions add risk, even from trusted vendors&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Use 2FA everywhere&lt;/strong&gt; - A stolen password with 2FA is less damaging&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you're evaluating password security tools, ask:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Does this tool inject credentials into webpage contexts?&lt;/li&gt;
&lt;li&gt;What happens if a malicious script runs alongside the extension?&lt;/li&gt;
&lt;li&gt;How does the tool verify page authenticity before autofilling?&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Our security architecture
&lt;/h2&gt;

&lt;p&gt;The Password App takes a different approach entirely:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Zero-knowledge&lt;/strong&gt;: Passwords never leave your Mac. They exist in memory only during the change process.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Credential isolation&lt;/strong&gt;: The AI agent never sees actual password values. It calls functions like &lt;code&gt;enter_password()&lt;/code&gt; that retrieve and inject credentials through a separate secure channel.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Domain locking&lt;/strong&gt;: During password changes, the browser can only navigate to the target domain. Attempts to redirect elsewhere are blocked at the browser level.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;No browser extension&lt;/strong&gt;: We eliminated the attack surface entirely by not putting code into the browser context.&lt;/p&gt;

&lt;h2&gt;
  
  
  The bottom line
&lt;/h2&gt;

&lt;p&gt;Clickjacking against password manager extensions isn't a theoretical risk—it's a demonstrated attack that works against the most popular tools. The affected vendors will likely patch this specific vulnerability, but the underlying problem remains: browser extensions operate in a hostile environment.&lt;/p&gt;

&lt;p&gt;When we designed The Password App, we asked: "How do we handle credentials securely in a world where we can't trust the browser?" The answer wasn't to build a better browser extension. It was to avoid the browser extension model entirely.&lt;/p&gt;

&lt;p&gt;Desktop-only. Local-only. Zero-knowledge. No extension attack surface.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://thepassword.app/blog/why-we-dont-use-browser-extensions" rel="noopener noreferrer"&gt;thepassword.app/blog&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>security</category>
    </item>
    <item>
      <title>The Shai-Hulud Worm: How 500+ NPM Packages Became Credential-Stealing Malware</title>
      <dc:creator>Sourabh Katti</dc:creator>
      <pubDate>Tue, 02 Dec 2025 01:31:47 +0000</pubDate>
      <link>https://dev.to/sourabh_katti_f87f3875e01/the-shai-hulud-worm-how-500-npm-packages-became-credential-stealing-malware-2ad1</link>
      <guid>https://dev.to/sourabh_katti_f87f3875e01/the-shai-hulud-worm-how-500-npm-packages-became-credential-stealing-malware-2ad1</guid>
      <description>&lt;p&gt;In September 2025, security researchers discovered something unprecedented: a self-replicating worm spreading through the NPM package ecosystem, stealing developer credentials and automatically infecting more packages. Named "Shai-Hulud" after the giant sandworms from Dune, this attack represents a new era in supply chain security—and a warning about how quickly stolen credentials can cascade into catastrophe.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is Shai-Hulud?
&lt;/h2&gt;

&lt;p&gt;Shai-Hulud is the first successful worm attack in the NPM ecosystem. Unlike traditional malware that requires manual deployment, this worm spreads autonomously by stealing developer credentials and using them to infect additional packages.&lt;/p&gt;

&lt;p&gt;Here's what makes it terrifying:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Self-replicating&lt;/strong&gt;: When the malware finds NPM tokens on a compromised machine, it automatically publishes malicious versions of any packages that developer maintains&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Exponential spread&lt;/strong&gt;: Each new infection creates more infections, without any attacker involvement&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Credential harvesting&lt;/strong&gt;: The malware steals AWS, GCP, Azure credentials, GitHub tokens, SSH keys, and more&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Destructive fallback&lt;/strong&gt;: If exfiltration fails, the malware attempts to destroy the victim's entire home directory&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Timeline: How It Unfolded
&lt;/h2&gt;

&lt;h3&gt;
  
  
  September 2025: First Wave
&lt;/h3&gt;

&lt;p&gt;The initial campaign was discovered when security researchers noticed suspicious activity in popular NPM packages. The attack spread through phishing emails impersonating NPM, tricking developers into revealing their credentials.&lt;/p&gt;

&lt;p&gt;Key impacts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;500+ packages compromised&lt;/strong&gt; including packages associated with CrowdStrike&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a class="mentioned-user" href="https://dev.to/ctrl"&gt;@ctrl&lt;/a&gt;/tinycolor&lt;/strong&gt; with over 2 million weekly downloads was infected&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;40+ packages&lt;/strong&gt; across multiple maintainers compromised in the initial wave&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  November 2025: Shai-Hulud 2.0
&lt;/h3&gt;

&lt;p&gt;A more aggressive second wave emerged, with researchers from &lt;a href="https://unit42.paloaltonetworks.com/npm-supply-chain-attack/" rel="noopener noreferrer"&gt;Unit 42&lt;/a&gt; and &lt;a href="https://jfrog.com/blog/shai-hulud-npm-supply-chain-attack-new-compromised-packages-detected/" rel="noopener noreferrer"&gt;JFrog&lt;/a&gt; documenting the expanded attack:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;25,000+ malicious repositories&lt;/strong&gt; across approximately 350 unique GitHub users&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pre-installation execution&lt;/strong&gt;: Unlike the first wave (post-install), the new variant executes during pre-install, dramatically widening the attack surface&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Notable victims&lt;/strong&gt;: Packages from Zapier, ENS Domains, PostHog, and Postman were affected&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GitHub Actions backdoors&lt;/strong&gt;: The malware drops workflow files that serialize and exfiltrate secrets&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;According to &lt;a href="https://www.cisa.gov/news-events/alerts/2025/09/23/widespread-supply-chain-compromise-impacting-npm-ecosystem" rel="noopener noreferrer"&gt;CISA's alert&lt;/a&gt;, this attack affects "tens of thousands" of GitHub repositories.&lt;/p&gt;

&lt;h2&gt;
  
  
  How the Attack Works
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Step 1: Initial Compromise
&lt;/h3&gt;

&lt;p&gt;Developers receive phishing emails that appear to come from NPM, requesting MFA credential updates. Those who fall for the phishing attack inadvertently grant attackers access to their NPM accounts.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2: Package Infection
&lt;/h3&gt;

&lt;p&gt;The attacker publishes a malicious version of a package the compromised developer maintains. This version includes hidden code in the installation scripts.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3: Credential Harvesting
&lt;/h3&gt;

&lt;p&gt;When someone installs the infected package, the malware scans for:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Credential type&lt;/th&gt;
&lt;th&gt;Location scanned&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;NPM tokens&lt;/td&gt;
&lt;td&gt;.npmrc files&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GitHub tokens&lt;/td&gt;
&lt;td&gt;Environment variables, config files&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cloud credentials&lt;/td&gt;
&lt;td&gt;AWS, GCP, Azure credential files&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SSH keys&lt;/td&gt;
&lt;td&gt;~/.ssh directory&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The malware uses &lt;a href="https://github.com/trufflesecurity/trufflehog" rel="noopener noreferrer"&gt;TruffleHog&lt;/a&gt; to systematically find secrets across the filesystem.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 4: Autonomous Spreading
&lt;/h3&gt;

&lt;p&gt;Here's where Shai-Hulud becomes unique: the worm uses stolen NPM tokens to identify other packages the victim maintains, then publishes malicious versions of those packages too—all without any human attacker involvement.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Developer A gets phished → Package X infected → 
Developer B installs Package X → Package Y, Z infected (B's packages) → 
Developer C, D, E install Y or Z → More packages infected...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 5: The Dead Man's Switch
&lt;/h3&gt;

&lt;p&gt;If the malware's exfiltration channels are blocked, it triggers a destructive fallback that attempts to securely overwrite and delete all writable files in the user's home directory. This "scorched earth" approach ensures maximum damage even if the attack is partially blocked.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why This Matters for Everyone (Not Just Developers)
&lt;/h2&gt;

&lt;p&gt;You might think: "I'm not a developer. Why should I care about NPM packages?"&lt;/p&gt;

&lt;p&gt;Here's why: &lt;strong&gt;the credentials stolen in Shai-Hulud attacks are the same credentials used to access your data&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;When attackers steal:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;AWS credentials&lt;/strong&gt; → They can access databases containing user information&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GitHub tokens&lt;/strong&gt; → They can insert backdoors into software you use&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cloud API keys&lt;/strong&gt; → They can access infrastructure running applications you depend on&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The companies affected by Shai-Hulud—like Zapier, PostHog, and Postman—handle data for millions of users. A compromised developer credential can cascade into:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;User database breaches&lt;/li&gt;
&lt;li&gt;API key theft&lt;/li&gt;
&lt;li&gt;Payment information exposure&lt;/li&gt;
&lt;li&gt;Personal data leaks&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  How to Protect Yourself
&lt;/h2&gt;

&lt;h3&gt;
  
  
  If You're a Developer
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://www.cisa.gov/news-events/alerts/2025/09/23/widespread-supply-chain-compromise-impacting-npm-ecosystem" rel="noopener noreferrer"&gt;CISA's mitigation recommendations&lt;/a&gt; include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Immediately rotate all credentials&lt;/strong&gt;: NPM tokens, GitHub PATs, SSH keys, cloud API keys&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Audit dependencies&lt;/strong&gt;: Run &lt;code&gt;npm audit&lt;/code&gt; and examine your package-lock.json files&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Check for Shai-Hulud repositories&lt;/strong&gt;: Look for unfamiliar repositories in your GitHub account with "Shai-Hulud" in the description&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Enable MFA everywhere&lt;/strong&gt;: GitHub, NPM, and all cloud providers&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pin dependency versions&lt;/strong&gt;: Lock versions to known-safe releases prior to September 16, 2025&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  If You're a Regular User
&lt;/h3&gt;

&lt;p&gt;Even if you're not a developer, you should:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Use unique passwords for every account&lt;/strong&gt;: If one password is compromised, only one account is affected&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Enable 2FA on important accounts&lt;/strong&gt;: Email, banking, anything with sensitive data&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Check for breaches&lt;/strong&gt;: Visit &lt;a href="https://haveibeenpwned.com" rel="noopener noreferrer"&gt;Have I Been Pwned&lt;/a&gt; to see if your credentials have been exposed&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Change passwords on breached accounts&lt;/strong&gt;: Don't wait—attackers act quickly&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Password Rotation Problem
&lt;/h2&gt;

&lt;p&gt;Here's the uncomfortable truth: most people know they should change compromised passwords. Most people don't do it.&lt;/p&gt;

&lt;p&gt;Why? Because changing passwords manually is tedious. If you have 100+ accounts and 30 of them use a compromised password, that's hours of work navigating to each site, finding the password change form, generating a new password, and updating your password manager.&lt;/p&gt;

&lt;p&gt;This is exactly why we built &lt;a href="https://thepassword.app" rel="noopener noreferrer"&gt;The Password App&lt;/a&gt;: to automate the tedious part of credential hygiene so you actually get it done.&lt;/p&gt;

&lt;h2&gt;
  
  
  Lessons from Shai-Hulud
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Supply Chain Attacks Are Escalating
&lt;/h3&gt;

&lt;p&gt;Shai-Hulud represents a new sophistication in supply chain attacks. The self-replicating nature means a single successful phishing email can cascade into thousands of compromised packages—and millions of affected users downstream.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Credential Hygiene Matters More Than Ever
&lt;/h3&gt;

&lt;p&gt;Every compromised credential is a potential entry point. Whether it's a developer's NPM token or your Netflix password, reused or weak credentials multiply risk.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Automated Attacks Require Automated Defenses
&lt;/h3&gt;

&lt;p&gt;Attackers use automation to scale their efforts. Individual users and organizations need automation to keep up. Manually rotating 100 passwords after every breach isn't sustainable.&lt;/p&gt;

&lt;h2&gt;
  
  
  Take Action Today
&lt;/h2&gt;

&lt;p&gt;The Shai-Hulud worm is a wake-up call. Supply chain attacks are getting more sophisticated, and credential theft cascades further than ever before.&lt;/p&gt;

&lt;p&gt;You may not be able to control whether the software you use gets compromised. But you can control:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Your own credential hygiene&lt;/strong&gt;: Unique passwords, 2FA enabled&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Your response time&lt;/strong&gt;: Change compromised passwords quickly&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Your attack surface&lt;/strong&gt;: Fewer reused passwords means less blast radius&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Your credentials are only as secure as your weakest password. Make them all strong.&lt;/p&gt;




&lt;h2&gt;
  
  
  Sources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://unit42.paloaltonetworks.com/npm-supply-chain-attack/" rel="noopener noreferrer"&gt;Unit 42: Shai-Hulud Worm Compromises npm Ecosystem&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://jfrog.com/blog/shai-hulud-npm-supply-chain-attack-new-compromised-packages-detected/" rel="noopener noreferrer"&gt;JFrog: Shai-Hulud npm supply chain attack&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.cisa.gov/news-events/alerts/2025/09/23/widespread-supply-chain-compromise-impacting-npm-ecosystem" rel="noopener noreferrer"&gt;CISA: Widespread Supply Chain Compromise Impacting npm Ecosystem&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.truesec.com/hub/blog/500-npm-packages-compromised-in-ongoing-supply-chain-attack-shai-hulud" rel="noopener noreferrer"&gt;Truesec: 500+ npm Packages Compromised&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://thepassword.app/blog/shai-hulud-npm-attack-supply-chain-security" rel="noopener noreferrer"&gt;thepassword.app/blog&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>security</category>
      <category>npm</category>
      <category>supplychain</category>
      <category>cybersecurity</category>
    </item>
  </channel>
</rss>
