diff --git a/CHANGELOG.md b/CHANGELOG.md index c0025e16e..c118cd0cd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,18 @@ # Change Log +## [v1.13.3](https://github.com/prey/prey-node-client/tree/v1.13.3) (2024-10-15) +[Full Changelog](https://github.com/prey/prey-node-client/compare/v1.13.2..v1.13.3) + +- Feat: PDC now informs if OS Query is running on the device or not. ([Beregcamlost](https://github.com/beregcamlost)) ([SoraKenji](https://github.com/SoraKenji)) + +- Fix: Fix to get location properly after getting location permission from Prey.app requested for MacOS. ([Beregcamlost](https://github.com/beregcamlost)) ([SoraKenji](https://github.com/SoraKenji)) + +- Fix: Fix for "device not connected" trigger. It was not working properly. ([Beregcamlost](https://github.com/beregcamlost)) ([SoraKenji](https://github.com/SoraKenji)) + +- Fix: Remove difference between some triggers' actions being stored in DB or not. Now everything is persisted. ([Beregcamlost](https://github.com/beregcamlost)) ([SoraKenji](https://github.com/SoraKenji)) + +- Fix: Fix a bug when two actions runs too quickly one after another and being marked as running with the same id ([Beregcamlost](https://github.com/beregcamlost)) ([SoraKenji](https://github.com/SoraKenji)) + ## [v1.13.2](https://github.com/prey/prey-node-client/tree/v1.13.2) (2024-09-12) [Full Changelog](https://github.com/prey/prey-node-client/compare/v1.13.1..v1.13.2) diff --git a/lib/agent/actions/triggers/index.js b/lib/agent/actions/triggers/index.js index 237ad3d01..868acde66 100644 --- a/lib/agent/actions/triggers/index.js +++ b/lib/agent/actions/triggers/index.js @@ -370,7 +370,7 @@ exports.activate = (trigger) => { const currentDate = new Date().getTime(); if (currentDate > opts) { - if ((trigger.persist === true || trigger.persist === 1) && !trigger.last_exec) { + if ((trigger.persist === true || trigger.persist === 1) && (!trigger.last_exec || trigger.last_exec.toLowerCase() === 'null')) { runTriggerActions(trigger); return 6; } diff --git a/lib/agent/commands.js b/lib/agent/commands.js index 985e215c1..9f5100761 100644 --- a/lib/agent/commands.js +++ b/lib/agent/commands.js @@ -1,116 +1,113 @@ -var common = require('./common'); -var hooks = require('./hooks'); -var actions = require('./actions'); -var triggers = require('./triggers'); -var providers = require('./providers'); -var reports = require('./reports'); -var updater = require('./updater'); -var storage = require('./utils/storage'); -var devices = require('./control-panel/api/devices'); -var logger = common.logger.prefix('commands'); -var watching = false; // flag for storing new commands when fired -var id; +const common = require('./common'); +const hooks = require('./hooks'); +const actions = require('./actions'); +const triggers = require('./triggers'); +const providers = require('./providers'); +const reports = require('./reports'); +const updater = require('./updater'); +const storage = require('./utils/storage'); +const devices = require('./control-panel/api/devices'); + +const logger = common.logger.prefix('commands'); +let watching = false; // flag for storing new commands when fired +let id; const { v4: uuidv4 } = require('uuid'); + const actions_not_allowed = ['user_activated']; -//////////////////////////////////////////////////////////////////// +/// ///////////////////////////////////////////////////////////////// // helpers // transforms this 'host:myhost.com user:god' // into this: {host: 'myhost.com', user: 'god' } -var parse_arguments = function (args) { +const parse_arguments = function (args) { if (!args || args.trim() === '') return; try { - var formatted = args + const formatted = args .trim() .replace(/([\w\.]+)/g, '"$1"') .replace(/" /g, '",'); - return JSON.parse('{' + formatted + '}'); + return JSON.parse(`{${formatted}}`); } catch (e) { console.log('Invalid argument format.'); } }; -var handle_error = function (err) { +const handle_error = function (err) { hooks.trigger('error', err); }; -//////////////////////////////////////////////////////////////////// +/// ///////////////////////////////////////////////////////////////// // build/run/parse/perform/process exports exports.build = function build(command, target, options) { - var obj = { command: command, target: target }; + const obj = { command, target }; if (options) obj.options = options; return obj; }; exports.run = function (command, target, options) { - var obj = exports.build(command, target, options); + const obj = exports.build(command, target, options); if (obj) exports.perform(obj); }; exports.parse = function (body) { - var c; - var matches; + let c; + let matches; if ((matches = body.match(/^help\s?(\w+)?/))) c = ['help', matches[1]]; // on [event] [start|stop] [something] if ( (matches = body.match( - /^(on|once) ([\w\-]+) (config|start|stop|get|set|send) (.+)/ + /^(on|once) ([\w\-]+) (config|start|stop|get|set|send) (.+)/, )) - ) - c = ['hook', matches[1], matches[2], body]; + ) c = ['hook', matches[1], matches[2], body]; - if ((matches = body.match(/^config read ([\w-]+)/))) - c = ['config', [matches[1]]]; + if ((matches = body.match(/^config read ([\w-]+)/))) c = ['config', [matches[1]]]; - if ((matches = body.match(/^config update (\w+)\s(?:to )?(\w+)/))) - c = ['command', this.build('update', matches[1], matches[2])]; + if ((matches = body.match(/^config update (\w+)\s(?:to )?(\w+)/))) c = ['command', this.build('update', matches[1], matches[2])]; - if ((matches = body.match(/^upgrade/))) - c = ['command', this.build('upgrade')]; + if ((matches = body.match(/^upgrade/))) c = ['command', this.build('upgrade')]; - if ((matches = body.match(/^start ([\w\-]+)(?: (using|with) )?(.*)/))) + if ((matches = body.match(/^start ([\w\-]+)(?: (using|with) )?(.*)/))) { c = [ 'command', this.build('start', matches[1], parse_arguments(matches[3])), ]; + } - if ((matches = body.match(/^watch ([\w\-]+)(?: (using|with) )?(.*)/))) + if ((matches = body.match(/^watch ([\w\-]+)(?: (using|with) )?(.*)/))) { c = [ 'command', this.build('watch', matches[1], parse_arguments(matches[3])), ]; + } - if ((matches = body.match(/^stop ([\w\-]+)/))) - c = ['command', this.build('stop', matches[1])]; + if ((matches = body.match(/^stop ([\w\-]+)/))) c = ['command', this.build('stop', matches[1])]; - if ((matches = body.match(/^unwatch ([\w\-]+)/))) - c = ['command', this.build('unwatch', matches[1])]; + if ((matches = body.match(/^unwatch ([\w\-]+)/))) c = ['command', this.build('unwatch', matches[1])]; if ( (matches = body.match( - /^(?:get|send) ([\w\/\.]+)(?: to )?([\w@\.:\/]+)?(?: (using|with) )?(.*)/ + /^(?:get|send) ([\w\/\.]+)(?: to )?([\w@\.:\/]+)?(?: (using|with) )?(.*)/, )) ) { // var destination = matches[2] ? [matches[1].trim(), matches[2].trim(), matches[3]] : {}; - if (matches[1][0] == '/' && matches[1].match(/\.(...?)/)) - c = ['send_file', [matches[1].trim()]]; + if (matches[1][0] == '/' && matches[1].match(/\.(...?)/)) c = ['send_file', [matches[1].trim()]]; else if (matches[1]) c = ['command', this.build('get', matches[1].trim())]; } return c; }; -exports.perform = function (command, persist = 1) { +exports.perform = function (command) { if (!command) return handle_error(new Error('No command received')); - if (typeof command.options == 'string') { + if (typeof command.options === 'string') { try { command.options = JSON.parse(command.options); } catch (e) { - logger.warn('Error parsing command options: ' + e.message); + logger.warn(`Error parsing command options: ${e.message}`); } } @@ -128,9 +125,9 @@ exports.perform = function (command, persist = 1) { command = command.body; } - logger.info('Command received: ' + JSON.stringify(command)); + logger.info(`Command received: ${JSON.stringify(command)}`); - var methods = { + const methods = { start: actions.start, stop: actions.stop, watch: triggers.add, @@ -145,9 +142,9 @@ exports.perform = function (command, persist = 1) { // This kind of report should be storable. To ensure it can be stored we need // to change it to {command: 'report', target: 'stolen'} if ( - command.command === 'get' && - command.target === 'report' && - command.options.interval + command.command === 'get' + && command.target === 'report' + && command.options.interval ) { command.command = 'report'; command.target = 'stolen'; @@ -155,21 +152,22 @@ exports.perform = function (command, persist = 1) { // Automation command to mark as missing and recovered from here if ( - command.command === 'start' && - (command.target === 'missing' || command.target === 'recover') + command.command === 'start' + && (command.target === 'missing' || command.target === 'recover') ) { - var set_missing = command.target === 'missing' ? true : false; + const set_missing = command.target === 'missing'; // Set as missing or recovered on the control panel devices.post_missing(set_missing, (err) => { - if (err) + if (err) { logger.warn( - 'Unable to set missing state to the device: ' + err.message + `Unable to set missing state to the device: ${err.message}`, ); + } }); // Initialize (or end) reports process on the client - var is_stolen = reports.running().some((e) => e.name == 'stolen'); + const is_stolen = reports.running().some((e) => e.name == 'stolen'); if (set_missing && !is_stolen) { command.command = 'report'; command.target = 'stolen'; @@ -179,43 +177,45 @@ exports.perform = function (command, persist = 1) { } } - var type = command.command || command.name, - method = methods[type]; + const type = command.command || command.name; + const method = methods[type]; if (method && !actions_not_allowed.find((x) => x == command.target)) { hooks.trigger('command', method, command.target, command.options); if (command.command != 'start') { if ( - command.command == 'get' || - command.command == 'report' || - command.command == 'cancel' + command.command == 'get' + || command.command == 'report' + || command.command == 'cancel' ) { - if (command.command == 'cancel' && command.target == 'stolen') + if (command.command == 'cancel' && command.target == 'stolen') { delete_same_target(id, 'stolen', () => { logger.debug('Deleted report stored command'); }); + } method(command.target, command.options); } else { method(id, command.target, command.options); } } else { - verify_if_executed(id, (err, executed) => { + verify_if_executed(id, (err, executed, idOverall) => { // Was executed and finished if (!executed) { - let target = verify_if_is_full_wipe(command.target, 'fullwipe'); + const target = verify_if_is_full_wipe(command.target, 'fullwipe'); command.options.target = command.target; command.target = target; - method(id, command.target, command.options); + method(idOverall, command.target, command.options); } else { - logger.warn(`Action with id ${id} was already executed`); + logger.warn(`Action with id ${idOverall} was already executed`); } }); } - if (persist) update_stored(type, id, command.target, command.options); + + update_stored(type, id, command.target, command.options); } else { handle_error( - new Error('Unknown command: ' + (command.command || command.name)) + new Error(`Unknown command: ${command.command || command.name}`), ); } }; @@ -225,13 +225,13 @@ exports.process = function (str) { var commands = JSON.parse(str); logger.info('Got commands.'); } catch (e) { - return handle_error(new Error('Invalid commands: ' + str)); + return handle_error(new Error(`Invalid commands: ${str}`)); } commands.forEach(this.perform); }; -//////////////////////////////////////////////////////////////////// +/// ///////////////////////////////////////////////////////////////// // command persistence exports.store = store; @@ -249,49 +249,51 @@ var delete_same_target = (id, target, cb) => { }); } else if (index == actions.length - 1) return cb(); }); - } + }, ); }; var store = function (type, id, name, opts, cb) { - logger.debug('Storing command in DB: ' + [type, name].join('-')); + logger.debug(`Storing command in DB: ${[type, name].join('-')}`); delete_same_target(id, name, () => { - if (name == 'geofencing' || name == 'triggers' || name == 'fileretrieval') - return cb && cb(); + if (name == 'geofencing' || name == 'triggers' || name == 'fileretrieval') return cb && cb(); storage.do( 'set', { type: 'commands', - id: id, + id, data: { command: type, target: name, options: opts }, }, - cb + cb, ); }); }; -var remove = function (type, id, name, cb) { - logger.debug('Removing command from DB: ' + [type, name].join('-')); - storage.do('del', { type: 'commands', id: id }, cb); +const remove = function (type, id, name, cb) { + logger.debug(`Removing command from DB: ${[type, name].join('-')}`); + storage.do('del', { type: 'commands', id }, cb); }; var verify_if_executed = function (id, cb) { storage.do( 'query', { type: 'commands', column: 'id', data: id }, - function (err, rows) { - if (err) return cb(err); + (err, rows) => { + if (err) { + return cb(err, null, id); + } - if ((rows && rows.length == 0) || (rows[0] && rows[0].stopped == 'NULL')) - return cb(null, false); - else return cb(null, true); - } + if ((rows && rows.length == 0) || (rows[0] && rows[0].stopped == 'NULL')) { + return cb(null, false, id); + } + return cb(null, true, id); + }, ); }; var verify_if_is_full_wipe = function (target, word) { - let result = target.split(word); + const result = target.split(word); if (result && result.length == 2 && result[1] == 'windows') return word; return target; }; @@ -300,36 +302,36 @@ var verify_if_is_full_wipe = function (target, word) { var update_stored = function (type, id, name, opts) { if (!watching) return; - var storable = ['start', 'watch', 'report']; + const storable = ['start', 'watch', 'report']; if (type == 'cancel') - // report cancelled - remove('report', id, name); - else if (storable.indexOf(type) !== -1) { + // report cancelled + { remove('report', id, name); } else if (storable.indexOf(type) !== -1) { store(type, id, name, opts); } }; // listen for new commands and add them to storage, in case the app crashes -var watch_stopped = function () { +const watch_stopped = function () { if (!watching) return; - hooks.on('action', function (event, id) { + hooks.on('action', (event, id) => { if (event == 'stopped' || event == 'failed') { storage.do( 'update', { type: 'commands', - id: id, + id, columns: 'stopped', values: new Date().toISOString(), }, (err) => { - if (err) + if (err) { logger.warn( - 'Unable to update stopped action timestamp for id:' + id + `Unable to update stopped action timestamp for id:${id}`, ); - } + } + }, ); } @@ -338,21 +340,22 @@ var watch_stopped = function () { 'update', { type: 'commands', - id: id, + id, columns: 'started', values: new Date().toISOString(), }, (err) => { - if (err) + if (err) { logger.warn( - 'Unable to update started action timestamp for id:' + id + `Unable to update started action timestamp for id:${id}`, ); - } + } + }, ); } }); - hooks.on('trigger', function (event, name) { + hooks.on('trigger', (event, name) => { if (event == 'stopped') remove('watch', name); }); }; @@ -370,10 +373,10 @@ exports.run_stored = function (cb) { storage.do('all', { type: 'commands' }, (err, commands) => { if (err || !commands) return logger.error(err.message); - var count = Object.keys(commands).length; + const count = Object.keys(commands).length; if (count <= 0) return; - for (let id in commands) { + for (const id in commands) { if (commands[id].stopped == 'NULL') { logger.warn('Relaunching '); // modificar mensaje exports.perform(commands[id]); diff --git a/package-lock.json b/package-lock.json index 0eb7cb416..735527499 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "prey", - "version": "1.13.2", + "version": "1.13.3", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index c9eaaa288..bef9ea714 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prey", - "version": "1.13.2", + "version": "1.13.3", "author": "Engineering ", "keywords": [ "prey",