prey/prey-node-client

View on GitHub
lib/agent/providers/hardware/index.js

Summary

Maintainability
D
2 days
Test Coverage
const os = require('os');
const network = require('network');
const si = require('systeminformation');
const storage = require('../../utils/storage');

const osName = process.platform.replace('darwin', 'mac').replace('win32', 'windows');
// eslint-disable-next-line import/no-dynamic-require
const osFunctions = require(`./${osName}`);
const exp = module.exports;
const hooks = require('../../hooks');
const common = require('../../common');

const logger = common.logger.prefix('hardware');

exp.get_processor_info = (callback) => {
  const cpus = os.cpus();
  const cpuInfo = {
    model: cpus[0].model.trim(),
    speed: cpus[0].speed,
    cores: cpus.length,
  };
  callback(null, cpuInfo);
};

exp.get_firmware_info = (callback) => {
  // eslint-disable-next-line consistent-return
  osFunctions.get_firmware_info((err, dataFirmware) => {
    const data = dataFirmware;
    if (err) return callback(err);
    if (data.device_type) data.device_type = data.device_type.replace('Notebook', 'Laptop');
    callback(null, data);
  });
};

exp.get_model_name = (cb) => {
  si.system((stdoutsi) => {
    if (!stdoutsi || !stdoutsi.model) return cb(null, '');
    return cb(null, stdoutsi.model);
  });
};

exp.get_vendor_name = (cb) => {
  si.system((stdoutsi) => {
    if (!stdoutsi || !stdoutsi.manufacturer) return cb(null, '');
    return cb(null, stdoutsi.manufacturer);
  });
};


exp.get_network_interfaces_list = network.get_interfaces_list;
exp.get_tpm_module = osFunctions.get_tpm_module;
exp.get_os_edition = osFunctions.get_os_edition;
exp.get_winsvc_version = osFunctions.get_winsvc_version;
exp.get_rp_module = osFunctions.get_recovery_partition_status;
exp.get_processor_info = (osName === 'mac') ? osFunctions.get_processor_info : exp.get_processor_info;
exp.get_prey_user_version = (osName === 'mac') ? osFunctions.get_prey_user_version : null;
exp.get_osquery_running = (osName === 'windows' || osName === 'mac') ? osFunctions.get_osquery_running : null;
exp.get_killswitch_compatible = (osName === 'windows') ? osFunctions.get_killswitch_compatible : null;

exp.get_first_mac_address = (callback) => {
  network.get_interfaces_list((err, list) => {
    if (err) return callback(err);
    if (list && list[0] && list[0].mac_address) return callback(null, list[0].mac_address);
    return callback(new Error("Couldn't find any valid MAC addresses!"));
  });
};

exp.get_ram_module_list = osFunctions.get_ram_module_list;

exp.track_hardware_changes = (dataTrack) => {
  const removeKeysFromObjectImmutable = (obj, keys) => Object.keys(obj).reduce((acc, key) => {
    if (!keys.includes(key)) {
      acc[key] = obj[key];
    }
    return acc;
  }, {});
  const exclusionList = ['tpm_module', 'os_edition', 'winsvc_version', 'rp_module', 'prey_user_version', 'killswitch_compatible', 'network_interfaces_list'];

  let data;
  if (typeof dataTrack !== 'object') data = dataTrack;
  if (dataTrack && typeof dataTrack === 'object') data = removeKeysFromObjectImmutable(dataTrack, exclusionList);

  let diffCount = 0;
  const saveData = () => {
    storage.do('set', { type: 'keys', id: 'hardware', data: { value: JSON.stringify(dataTrack) } }, (err) => {
      if (err) logger.error('Unable to save hardware data');
    });
  };

  const orderObject = (olderObject) => {
    if (!olderObject) return olderObject;
    const ordered = Object.keys(olderObject).sort().reduce(
      (newObj, key) => {
        const obj = newObj;
        obj[key] = olderObject[key];
        return obj;
      },
      {},
    );
    return ordered;
  };

  // eslint-disable-next-line consistent-return
  const compareField = (current, storedToCompare) => {
    const stored = storedToCompare;
    if (stored instanceof Array) {
      if (!current || (current.length > stored.length)) {
        diffCount += 1;
        return diffCount;
      }
      let hasKeyName = (stored.length > 0) ? Object.prototype.hasOwnProperty.call(stored[0], 'name') : false;
      let hasKeyBank = (stored.length > 0) ? Object.prototype.hasOwnProperty.call(stored[0], 'bank') : false;
      if (hasKeyName) stored.sort((a, b) => (a.name > b.name ? 1 : -1));
      if (hasKeyBank) stored.sort((a, b) => (a.bank > b.bank ? 1 : -1));
      if (current instanceof Array) {
        hasKeyName = (current.length > 0) ? Object.prototype.hasOwnProperty.call(current[0], 'name') : false;
        hasKeyBank = (current.length > 0) ? Object.prototype.hasOwnProperty.call(current[0], 'bank') : false;

        if (hasKeyName) current.sort((a, b) => (a.name > b.name ? 1 : -1));
        if (hasKeyBank) current.sort((a, b) => (a.bank > b.bank ? 1 : -1));
      }
      // eslint-disable-next-line consistent-return
      stored.forEach((value) => {
        if (value instanceof Object) {
          // eslint-disable-next-line no-use-before-define
          compareSubField(orderObject(current), orderObject(stored));
        } else {
          const foundIndex = current.findIndex((element) => element === value);
          if (foundIndex === -1) {
            diffCount += 1;
            return diffCount;
          }
        }
      });
    } else if (stored instanceof Object) {
      // eslint-disable-next-line no-use-before-define
      compareSubField(orderObject(current), orderObject(stored));
    } else if (current !== stored) diffCount += 1;
  };

  // eslint-disable-next-line consistent-return
  const compareSubField = (curr, stored) => {
    let current = curr;
    if (!curr || (Object.keys(curr).length > Object.keys(stored).length)) {
      current = (curr instanceof Object) ? JSON.parse(JSON.stringify(curr)) : undefined;
      if (current && stored) {
        if (Object.keys(current).length > Object.keys(stored).length) {
          diffCount += 1;
          return diffCount;
        }
      }
    }
    Object.keys(stored).forEach((key) => {
      if (!key.includes('ip') && !key.includes('netmask') && current && stored) {
        compareField(current[key], stored[key]);
      }
    });
  };

  // eslint-disable-next-line consistent-return
  storage.do('query', { type: 'keys', column: 'id', data: 'hardware' }, (err, storedDt) => {
    let storedData = storedDt;
    if (err) logger.error('Unable to read hardware data');
    if (storedData && storedData.length === 0) return saveData();

    try {
      storedData = JSON.parse(storedData[0].value);
    } catch (e) {
      logger.info(`Error: ${e}`);
    }
    // eslint-disable-next-line consistent-return
    let dataStoredModified;
    if (typeof storedData !== 'object') dataStoredModified = storedData;
    if (storedData && typeof storedData === 'object') dataStoredModified = removeKeysFromObjectImmutable(storedData, exclusionList);

    compareField(data, dataStoredModified);

    if (diffCount > 0) {
      hooks.trigger('hardware_changed');
      storage.do('del', { type: 'keys', id: 'hardware' }, (errDel) => {
        if (errDel) logger.error('Unable to delete hardware data');
        saveData();
      });
    }
  });
};