worm-sign
Version:
A prescient scanner to detect and banish Shai Hulud malware from your dependencies.
74 lines (73 loc) • 2.36 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.EntropyCalculator = void 0;
exports.calculateEntropy = calculateEntropy;
exports.calculateEntropyStream = calculateEntropyStream;
exports.isHighEntropy = isHighEntropy;
/**
* Shannon Entropy Analysis
*
* This module calculates the Shannon entropy of strings to detect
* high-entropy content, which is often indicative of packed or
* obfuscated malware payloads (e.g. bun_environment.js).
*/
class EntropyCalculator {
frequencies = {};
totalBytes = 0;
update(chunk) {
const len = chunk.length;
this.totalBytes += len;
for (let i = 0; i < len; i++) {
const byte = typeof chunk === 'string' ? chunk.charCodeAt(i) : chunk[i];
this.frequencies[byte] = (this.frequencies[byte] || 0) + 1;
}
}
digest() {
if (this.totalBytes === 0)
return 0;
let entropy = 0;
for (const count of Object.values(this.frequencies)) {
const p = count / this.totalBytes;
entropy -= p * Math.log2(p);
}
return entropy;
}
}
exports.EntropyCalculator = EntropyCalculator;
/**
* Calculates the Shannon entropy of a string.
* Formula: H(X) = - sum(P(xi) * log2(P(xi)))
*
* @param str The input string
* @returns The entropy value (typically between 0 and 8)
*/
function calculateEntropy(input) {
const calculator = new EntropyCalculator();
calculator.update(input);
return calculator.digest();
}
/**
* Calculates entropy from a readable stream.
*/
function calculateEntropyStream(stream) {
return new Promise((resolve, reject) => {
const calculator = new EntropyCalculator();
stream.on('data', (chunk) => calculator.update(chunk));
stream.on('error', reject);
stream.on('end', () => resolve(calculator.digest()));
});
}
/**
* Checks if a string has suspiciously high entropy.
*
* @param str The string to check
* @param threshold The threshold (default 5.2 based on research)
* @returns True if entropy exceeds threshold
*/
function isHighEntropy(str, threshold = 5.2) {
// Short strings can have artificially high or low entropy and are less likely to be packed payloads
if (str.length < 50) {
return false;
}
return calculateEntropy(str) > threshold;
}