function extractBehaviourFeatures(actions) { if (!actions || actions.length < 2) return null; const actionCounts = {}; const idleThreshold = 5000; const windowSize = 5; const intervals = []; const sequenceBuffer = []; let totalInterval = 0; let minInterval = Infinity; let maxInterval = -Infinity; let idleTimeCount = 0; let idleTimeTotal = 0; let lastTimestamp = actions[0].timestamp; let prevAction = actions[0].opcode; let sequenceMap = {}; for (let i = 1; i < actions.length; i++) { const current = actions[i]; const interval = current.timestamp - lastTimestamp; lastTimestamp = current.timestamp; intervals.push(interval); totalInterval += interval; minInterval = Math.min(minInterval, interval); maxInterval = Math.max(maxInterval, interval); actionCounts[current.opcode] = (actionCounts[current.opcode] || 0) + 1; if (interval > idleThreshold) { idleTimeCount++; idleTimeTotal += interval; }; sequenceBuffer.push(current.opcode); if (sequenceBuffer.length === windowSize) { const key = sequenceBuffer.join(","); sequenceMap[key] = (sequenceMap[key] || 0) + 1; sequenceBuffer.shift(); }; prevAction = current.opcode; }; const meanInterval = totalInterval / intervals.length; const stdDevInterval = Math.sqrt(intervals.reduce((sum, val) => sum + (val - meanInterval) ** 2, 0) / intervals.length); const uniqueActionsCount = Object.keys(actionCounts).length; const idleTimeRatio = idleTimeCount / (actions.length - 1); const maxSequenceRepeats = Math.max(...Object.values(sequenceMap)) || 0; const totalActions = actions.length; let entropy = 0; for (const count of Object.values(actionCounts)) { const p = count / totalActions; entropy -= p * Math.log2(p); }; return { timestamp: new Date(), timestampStart: null, meanInterval, stdDevInterval, minInterval, maxInterval, uniqueActionsCount, actionEntropy: entropy, maxSequenceRepeats, idleTimeRatio }; }; module.exports = extractBehaviourFeatures;