solidity-map on npm: same malware payload, third package name
GitHub's advisory database flags solidity-map 2.21.0 as malware that reuses the hardhat-roof and chai-foundry payload byte for byte, so name-based blocklists will keep missing the next rename.
Automated engineering analysis by the Khalid Aboubakr Engineering Desk, from the primary sources listed below. Not reviewed by a person before publication. About the desk
Bottom line
solidity-map 2.21.0 on npm is malware (GHSA-95qc-c4xp-j95w). There is no patched version: the only fix is removal. It reuses the hardhat-roof and chai-foundry payload byte for byte. Search lockfiles and node_modules for all three names and for the payload's file hashes, and treat any machine that installed or imported it as compromised.
What happened
GitHub's advisory database lists solidity-map on npm as malicious code under GHSA-95qc-c4xp-j95w. Only version 2.21.0 is affected, and no patched version is listed. GitHub states that the package is malware and therefore has no patched versions.
The advisory describes solidity-map as a new name for an existing campaign. It cites two earlier malware records: hardhat-roof (MAL-2026-17509) and chai-foundry (MAL-2026-13356). The package's index.js and lib/config.js are byte-identical to the payloads of those two packages. The file hashes in the solidity-map OSV record are stated to match the evidence in the hardhat-roof record.
The advisory says the package presents itself as an ordinary utility and that its code runs on import or install without the user knowing.
The earlier hardhat-roof analysis by Amazon Inspector found that the package copied the pino logger's documentation and licence files as cover. In that package, index.js calls require('./lib/config') at its top. That loads lib/config.js, a 4,373,384-byte file with obfuscator.io-style string-array obfuscation. Any import or require of the package therefore runs the obfuscated code at load time.
Why it matters
This advisory does not describe a flaw in a legitimate dependency with a version to upgrade to. It describes hostile code. If a project installed or imported solidity-map, the question is not whether to patch but what that code did on the machine that ran it. The evidence does not answer that.
The more useful signal is the pattern. One payload, with the same file hashes, has now been published under at least three names; solidity-map and hardhat-roof both carry version 2.21.0 (chai-foundry's version is not in the evidence):
hardhat-roofchai-foundrysolidity-map
The names point at Ethereum and Solidity developer tooling, although the evidence does not state who the targets are. Teams whose defences are lists of known-bad package names are always one rename behind.
What is not established should be kept in proportion. The evidence gives no download counts, no dependents, no confirmed victims and no indicators of compromise: the domain, IP and URL lists in the record are empty. This does not support a mass-compromise headline. It does support a short, targeted check, especially for teams building smart-contract tooling.
Engineering assessment
Our assessment: for any team that pulled this package, the exposure is serious, because execution is the default. The hardhat-roof analysis shows a top-level require into a multi-megabyte obfuscated blob. No unusual configuration is needed: importing the package is enough. Whether code also runs at install time through lifecycle scripts is not established. The advisory says "import or install", but the supporting analysis describes only the load-time path. Settings that disable install scripts may therefore not protect a project that actually imports the package.
For everyone else, the realistic risk is low but the lesson is concrete. Byte-identical reuse makes content-based detection cheap and durable. The SHA-256 of lib/config.js (7e4c6d76f1d845f752e37876a2d3f773996719f33c2d1c282e0a84b04444efeb) appears in both the hardhat-roof and solidity-map records. A structural heuristic also carries across renames and slightly modified variants: a pino-like package with an oversized, opaque lib/config.js that the real pino does not ship. A blocklist of three names catches nothing new. A hash and size check on dependency contents catches the next rename for as long as the payload stays unchanged.
The caveat is that hash matching fails as soon as the attacker changes a single byte. Treat hashes as a fast tripwire, not a control. The durable controls are still dependency review on new additions, lockfile diffs in code review, and an allowlist or proxy registry for build systems that touch production credentials.
The case for reviewing new direct dependencies by content and provenance rather than by name and README is stronger after this advisory, because in hardhat-roof the README was copied from pino, and the solidity-map advisory describes the same pino copy.
Is this you?
- Any lockfile (
package-lock.json,yarn.lock,pnpm-lock.yaml) referencessolidity-map,hardhat-rooforchai-foundry, at any depth. - A
node_modulestree on a developer laptop, CI runner or container image containssolidity-maporhardhat-roof(2.21.0 in the records), orchai-foundryat any version. - Any
lib/config.jsin your dependency trees has SHA-2567e4c6d76f1d845f752e37876a2d3f773996719f33c2d1c282e0a84b04444efeb, or anindex.jshas4e6c4e61a7019ab0affe9c2d20e36701b330ec357be205930355bac1464f9316. - A dependency that looks like pino has a
lib/config.jsmeasured in megabytes. - Your team works on Solidity, Hardhat or Ethereum tooling and adds small utility packages without review. This is higher-risk context, not proof of exposure.
If none of these apply, this advisory does not affect you; other renames of the payload would still be caught by the hash and size checks above.
What to do
-
Inventory now. Run these on repositories, CI caches and build images:
npm ls solidity-map hardhat-roof chai-foundrygrep -rE 'solidity-map|hardhat-roof|chai-foundry' --include='*lock*' .
-
Detect by content. Look for oversized config files:
find . -path '*node_modules*' -path '*/lib/config.js' -size +1M -exec sha256sum {} +
Compare the output against the hashes above.
-
If found, remove the package. Do not look for an upgrade, because none exists. Remove it from
package.json, regenerate the lockfile, and purge CI caches and any images built while the package was present. -
Treat any host that installed or imported it as compromised. There is no official remediation guidance in the evidence, so the following is Not vendor guidance:
- Rotate secrets reachable from that host or pipeline, including npm tokens, cloud keys, SSH keys, and wallet or deployer keys.
- Rebuild the host rather than cleaning it.
- Review outbound network logs from the period it was present.
-
Add payload-based checks to CI. As a dependency-scanning rule, flag known payload hashes and unusually large obfuscated files in new dependencies. Not vendor guidance.
-
For technical leads: require review of every new direct dependency. Consider an internal registry proxy with an allowlist for pipelines that hold production or signing credentials. Not vendor guidance.
More supply-chain coverage is on the Engineering Desk news page.
What we don't know yet
- What the dropper does once loaded. Second-stage download, credential or wallet theft and persistence are all possible, but the advisory gives no behaviour and no network indicators.
- Whether code runs at install time through lifecycle scripts, or only on import.
- Whether the package has been removed from the npm registry, and how many downloads or dependents it had.
- How long it was installable before the advisory. The OSV record is dated 2026-10-26; the GitHub advisory was published on 2026-11-05.
- Whether chai-foundry's affected version is also 2.21.0, and what other names the campaign has used. The chai-foundry record was not part of the evidence.
- Whether any real project or victim was affected. The evidence is silent on both.
The desk will watch for a behavioural analysis of the payload, registry takedown status and further renames carrying the same hashes.
Sources
Latest engineering analysis
6 min read
loader-utils-kit: the pino-testkit npm dropper is back under a new name
The obfuscated lib/contract.js dropper first seen in pino-testkit has been republished as loader-utils-kit, so name-based blocklists miss it and teams should search by file hash as well as by name.
Engineering Practice6 min read
Laravel 13.40.0: transaction, replica and Redis changes to test first
A point release that changes how database-heavy Laravel apps behave under failure, plus a security-flavoured Concurrency fix that ships with no advisory.
6 min read
SD-JWT JS silently drops tampered disclosures and reports success (CVE-2026-62374)
A verifier-side conformance gap in the OpenWallet Foundation's SD-JWT JavaScript libraries lets a credential holder silently remove a claim, and key binding does not stop it.