astro.config.mjs Supply Chain Attack via Blockchain C2 - Real-time Open Source Software Supply Chain Security<br>Login Start for Free 1.5k
Back<br>Vet<br>Scan and govern your dependencies across every PR and build.
PMG<br>Block malicious packages at install-time, before they enter your codebase.
xbom<br>Generate AI-enriched BOMs using real code evidence, not just manifests.
GRYPH<br>Monitor every AI coding agent action across your projects and workflows.
BackDiscover & Monitor<br>SCA & SBOMScan dependencies, generate SBOMs, enforce policy.
AI Agent DiscoverySee every AI tool and SDK in your org.
AI Agent MonitoringAudit every action your AI agents take.
Protect<br>Developer SecurityBlock malicious packages at install-time.
CI/CD SecurityBlock malicious packages in your pipeline.
MCP ServerBlock threats inside your AI coding agent.
Agent APIThreat intelligence API for custom agents.
Threat IntelligenceReal-time malicious package verdicts.
Govern<br>Endpoint ProtectionPackage events & AI inventory in the cloud.
PlatformCentralized policies, dashboard, compliance.
Login Start for Free 1.5k
Back to Blog
astro.config.mjs Supply Chain Attack via Blockchain C2<br>Malware
SafeDep Team<br>• Jun 12, 2026 • 9 min read
Table of Contents
Open Source · Free<br>Protect your projects from malicious packages<br>PMG wraps your favorite package manager to block malicious packages at install time
Get PMG on GitHub
Pull request #206 against Egonex-AI/Understand-Anything (an open source code-to-knowledge-graph tool with 57,000+ GitHub stars) carries a build-time payload hidden in homepage/astro.config.mjs. Every invocation of astro build, astro dev, or astro preview from the affected branch runs the file as a Node.js module, and an obfuscated IIFE at the end fires automatically. The payload beacons one of three hardcoded C2 servers, exfiltrates a campaign marker, XOR-decrypts and evaluates a downloaded bot client, then independently resolves a second-stage command from a Tron blockchain address whose latest transaction encodes a BSC transaction hash carrying the active payload. Because the command relay uses only public blockchain RPC nodes, blocking the C2 IPs does not stop the second stage.<br>The deceptive PR<br>The PR title reads fix(dashboard): filter Path Finder "To" dropdown to reachable nodes (#188). The description documents a BFS reachability fix, a shared useMemo adjacency map, a useEffect for clearing stale targets, and a test plan citing 395 assertions across an 80-node generated graph. None of that appears in the diff.
The actual changed files are two:<br>.gitignore: adds branch_structure.json, temp_auto_push.bat, and temp_interactive_push.bat<br>homepage/astro.config.mjs: inserts createRequire preamble and a large obfuscated payload<br>The .gitignore additions suppress three Windows batch scripts from git status. The .bat filenames point to automated push tooling, consistent with the attacker running a Windows-based workflow to generate and submit PRs across multiple targets. The payload in astro.config.mjs is appended after several hundred characters of horizontal whitespace on the same line as the closing });. GitHub’s diff renderer treats that line as complete. A reviewer scrolling vertically through the diff sees nothing suspicious.
The malicious file is pinned at AsimRaza10/Understand-Anything @ 8d30be36. (Scroll right in the code section)<br>The attack was publicly disclosed in issue #432, filed by a downstream user who reviewed the PR diff statically and never checked out or built the branch. The issue title reads: “Security: PR #206 injects an obfuscated executable payload into homepage/astro.config.mjs (do not merge).”<br>This PR fits the V4 vector in SafeDep’s malicious pull request threat model: a contributor-class attacker submits a plausible-looking change to a public repository, targeting the build pipeline rather than the dependency graph. The threat model covers the full taxonomy; this post documents one live instance in detail.<br>Why astro.config.mjs is an attack surface<br>Astro evaluates astro.config.mjs as a live Node.js module at the start of every dev, build, and preview run. The file executes before any user code, before any import graph resolves, with full access to the process environment and filesystem. This is the same execution context as a postinstall script, but the trigger is broader. A pnpm install happens once per environment setup. astro dev runs continuously throughout a project’s lifetime, so every developer, every CI job, and every preview build on any branch carrying this file executes the payload.<br>There is no opt-in, no sandbox, and no way to run Astro builds without evaluating the config. This is the config-as-code attack surface in practice.<br>Recovering require in an ESM context<br>Astro configs are ES modules. The http and https Node.js builtins the payload needs are not available without explicit ESM imports, and adding visible imports would surface in the diff. The payload restores require through three added...