prey/prey-node-client

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

Summary

Maintainability
B
5 hrs
Test Coverage
"use strict";

//////////////////////////////////////////
// Prey Files Provider
// (C) 2019 Prey, Inc.
// By Tomas Pollak - http://forkhq.com
// GPLv3 Licensed
//////////////////////////////////////////

var fs       = require('fs'),
    path     = require('path'),
    execSync = require("child_process").execSync;

var common      = require('./../../common'),
    system      = common.system,
    logger      = common.logger.prefix('files'),
    node_bin    = path.join(system.paths.current, 'bin', 'node'),
    run_as_user = common.system.run_as_user,
    finder      = require('./finder'),
    os_name     = process.platform.replace('darwin', 'mac').replace('win32', 'windows');

var MAX_BUFFER = 20*1024*1024;

function get_home() {
  return process.env.HOME || process.env.HOMEDIR || process.env.USERPROFILE;
}

// returns list of recently modified files. unless since option is passed,
// will return list of modified since one hour ago
exports.get_files_recently_modified_list = function(options, callback) {

  var callback = (typeof options == 'function') ? options : callback,
    options = options || {};

  var path = options.path || get_home(),
    modified_since = new Date() - (options.since || 1000 * 60 * 60); // one hour ago

  var criteria = function(file, stat) {
    return stat.mtime.getTime() > modified_since;
  };

  get_list({
    path: path,
    criteria: criteria
  }, callback);

};

exports.get_files_matching_filename_list = function(options, callback) {

  if (!options.string)
    return callback(new Error('No search string given.'));

  var path = options.path || get_home();
  var search_string = options.string || options.search_string;
  var extensions = options.extensions || ".*";
  var modifiers = options.case_sensitive ? '' : 'i';

  var regex = new RegExp(".*" + search_string + ".*\." + extensions, modifiers);

  var criteria = function(file) {
    return regex.test(file);
  };

  get_list({
    path: path,
    criteria: criteria
  }, callback);

};

var get_list = function(options, callback) {

  var path = options.path,
    matches_criteria = options.criteria,
    matches = [];

  if (!path)
    return callback(new Error('Cannot find files without a path.'));

  let exists = fs.existsSync(path)
  if (!exists)
    return callback(new Error("Path not found: " + path));

  finder.eachFileOrDirectory(path, function(err, file, stat) {

    // if we get a hidden file or error, skip to next
    if (err || /\/\./.test(file)) return;

    if (!stat.isDirectory() && matches_criteria && matches_criteria(file, stat)) {
      // console.log("File matches criteria: " + file)
      matches.push(file);
    }
  }, function() {
    callback(null, matches);
  });
};

module.exports.get_tree = function(options, cb) {

  var dir = options.path;

  // We need to run the tree walker script personifying
  // the owner of the corresponding /Users or /home subdirectories
  if (os_name !== 'windows' && !options.user) {
    cb(new Error('Options should specify user.'));
  }

  if (!dir) {
    switch (os_name) {
      case 'windows':
        dir = 'C:';
        break;
      case 'linux':
        dir = '/home/' + options.user;
        break;
      case 'mac':
        dir = '/Users/' + options.user;
        break;
    }
  } else {
    // TODO validate if path is children of user's home dir
  }

  var argsv; 
  if (os_name == 'windows')
    argsv = [path.join(__dirname, 'tree.js'), options.depth, path.resolve('', dir + '\\')];
  else
    argsv = [path.join(__dirname, 'tree.js'), options.depth, '"' + dir + '"'];
  
  var opts = {
    user: options.user,
    bin: node_bin,
    type: 'exec',
    args: argsv,
    opts: {
      maxBuffer: MAX_BUFFER,
      env: process.env
    }
  };

  var cp = run_as_user(opts, function(err, out) {
    if (err) return cb(err);

    try {
      var files = JSON.parse(out);
    } catch (e) {
      return cb(new Error('Unable to parse files data'));
    }

    if (files.length == 0) {
      logger.info("without files in folder!");
      return cb(null, "[]");
    }

    files.forEach((file, index) => {
      var done = (isHidden) => {
        file.hidden = isHidden;
        if (index == files.length - 1) {
          out = JSON.stringify(files);
          logger.debug(out);
          return cb(null, out);
        }
      }

      if (os_name == 'windows') {
        try {
          var stdout = execSync("cscript " + __dirname + "/attr.js " + file.path + " //Nologo");
          if (stdout.toString().includes('Error')) return done(false);
          stdout = JSON.parse(stdout);
          return done(stdout.hidden);

        } catch(e) {
          done(false);
        }
      }
      else done((/(^|\/)\.[^\/\.]/g).test(file.name));
    })  
  });
}

exports.get_file = function() {}