Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Action for update winsvc #678

Merged
merged 11 commits into from
Oct 24, 2022
110 changes: 101 additions & 9 deletions lib/agent/updater.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,15 @@
var join = require('path').join,
exists = require('fs').exists,
common = require('./common'),
logger = common.logger.prefix('updater'),
system = common.system,
child_process = require('child_process'); // need to use child_process for stubbing to work in test
const { eq } = require('semver');
var join = require('path').join,
exists = require('fs').exists,
common = require('./common'),
logger = common.logger.prefix('updater'),
system = common.system,
child_process = require('child_process'),
os = require('os'),
exec = child_process.exec,
needle = require('needle'),
updater_path = join(system.paths.package, 'bin', 'updater.exe'),
sys_win = require('./../system/windows/');

var timer, timer2; // for interval check
exports.upgrading = false;
Expand Down Expand Up @@ -32,6 +38,7 @@ var update_client = function(new_version, cb) {
var bin_path = system.paths.package_bin; // /foo/bar/bin/prey
var args = ['config', 'upgrade', new_version];
}

exports.upgrading = true;

var let_the_child_go = function() {
Expand Down Expand Up @@ -115,7 +122,50 @@ var update_client = function(new_version, cb) {
});
}

/**
* Verify if winsvc must be updated
* @param {object} cb - a callback function
*/
exports.check_for_update_winsvc = (cb) => {

/** Skip this block if OS is not windows. */
if (os.platform() != 'win32')
return cb(new Error('Action only allowed on Windows'));

/** Get the current version of winsvc running on the device. */
sys_win.get_winsvc_version((err, current_service_version) => {
if (err) return cb(new Error("Error to get winsvc version"));

if (!current_service_version) {
return cb(new Error("Error to get current winsvc version."));
}

/** Get the latest version of winsvc. */
exports.get_stable_version_winsvc((err, service_version_stable) => {
if (err) return cb(new Error("Error to get stable version"));

logger.notice('New version found winsvc: ' + service_version_stable);

/** check if device is running the latest version. */
if (service_version_stable && eq(current_service_version, service_version_stable)) {
logger.notice('Nothing to do. latest version already installed. ' + service_version_stable);
return cb(null, true)
}

/** Perform the update. */
exports.update_winsvc(updater_path + " -v=" + current_service_version, (err_update) => {
if (err_update)
return cb(new Error("error to update winsvc," + err_update.message))

return cb(null, true)
})
})

})
}

var check_for_update = function(cb) {

if (!exports.check_enabled || exports.upgrading) {
if (cb && typeof cb == 'function') return cb();
else return;
Expand All @@ -130,13 +180,19 @@ var check_for_update = function(cb) {
common.package.new_version_available(branch, common.version, function(err, new_version) {
if (err || !new_version) {
common.package.check_update_success(common.version, versions_path, function(err) {

return cb && cb(err || new Error('Theres no new version available'));
})
} else {
logger.notice('New version found: ' + new_version);
update_client(new_version, cb);
}
})

exports.check_for_update_winsvc((err,is_updated) => {
if(err) logger.info(err.message);
if(is_updated) logger.info("winsvc updated ");
});
}

exports.check = function(id, target, opts, cb) {
Expand All @@ -163,7 +219,9 @@ exports.check = function(id, target, opts, cb) {
if (exports.upgrading) logger.warn('Already running upgrade process.')

common.package.delete_attempts((err) => {
last_time = null;
if(err)
logger.error(err)

check_for_update((err) => {
done(err);
});
Expand Down Expand Up @@ -198,6 +256,16 @@ exports.check = function(id, target, opts, cb) {
common.package.restart_client();
done();
break;

case "update-winsvc":
// update winsvc
logger.info("command updating winsvc");
exports.check_for_update_winsvc((err,is_updated) => {
if(err) logger.info(err.message);
if(is_updated) logger.info("winsvc updated from command");
done();
});
break;

default:
logger.warn("Invalid target for upgrade command")
Expand All @@ -210,7 +278,6 @@ exports.check_every = function(interval, cb) {
if (!system.paths.versions)
return cb && cb(no_versions_support_error());

var interval = interval || 3 * 60 * 60 * 1000; // three hours by default
timer = setInterval(() => {
exports.check_enabled = true;
exports.upgrading = false;
Expand All @@ -220,10 +287,35 @@ exports.check_every = function(interval, cb) {

exports.stop_checking = function() {
if (timer) clearInterval(timer);
if (timer2) clearInterval(timer2);
if (timer2) clearInterval(timer2);
timer = null;
timer2 = null;
}

exports.update_winsvc = (path,cb) => {
exec(path, (err, pid) => {
logger.info("executing service windows update!"+ path)
if (err) return cb(err)
return cb(null,pid)
})
}

exports.get_stable_version_winsvc = function(cb) {
let //releases_host = 'http://172.200.2.6',
//releases_url = releases_host + '/prey-releases/winsvc/',
releases_host = 'https://downloads.preyproject.com',
releases_url = releases_host + '/prey-client-releases/winsvc/',
latest_text = 'latest.txt';

let key = common.config.get('control-panel.device_key').toString() || null;
var options = {
headers: { 'resource-dk': key }
}
needle.get(releases_url + latest_text, key ? options : null, function(err, resp, body) {
var ver = body && body.toString().trim();
cb(err, ver);
});
}

exports.check_for_update = check_for_update;
exports.logger = logger;
223 changes: 223 additions & 0 deletions test/lib/agent/updating_winsvc_spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,223 @@
/* eslint-disable no-undef */
var join = require('path').join,
helpers = require(join('..', '..', 'helpers')),
should = require('should'),
sinon = require('sinon'),
assert = require('assert'),
sys_index_path = helpers.lib_path('system'),
sys_index = require(sys_index_path),
sys_win = require(join(sys_index_path, 'windows')),
os = require('os'),
sending = { count: 0 }
updater = require(helpers.lib_path('agent', 'updater'));

function startCheckUpdateWinsvc(number) {
setInterval(function () {
sending.count += number;
updater.check_for_update_winsvc(() => {
})
}, 100);
}

describe('when os != windows', () => {
before(() => {
sys_index.os_name = "mac"
platform_stub = sinon.stub(os, 'platform').callsFake(() => { return 'mac'; });
})

after(() => {
platform_stub.restore();
})

it('returns an error', (done) => {
updater.check_for_update_winsvc((err) => {
should.exist(err);
err.message.should.containEql('Action only allowed on Windows');
done();
});
})
})

describe('update winsvcs interval', function () {
before(() => {
sys_index.os_name = "windows"
platform_stub_3 = sinon.stub(os, 'platform').callsFake(() => { return 'win32'; });
})

describe('updater winsvc', function () {

beforeEach(function () {
sys_index.os_name = "windows"
this.clock = sinon.useFakeTimers();
});

afterEach(function () {
this.clock = sinon.restore();
});

it('should be sent once', function () {
startCheckUpdateWinsvc(1);
//advance the clock
this.clock.tick(100);
this.clock.tick(100);
assert.equal(sending.count, 2);
});
})

})

describe('when os is windows', () => {
before(() => {
sys_index.os_name = "windows"
sys_index.check_service = sys_win.check_service;
sys_index.run_as_admin = sys_win.run_as_admin;
platform_stub = sinon.stub(os, 'platform').callsFake(() => { return 'win32'; });
})

after(() => {
platform_stub.restore();
})

describe('when get_winsvc_version return error', () => {

var get_winsvc_version_stub;
before(() => {
get_winsvc_version_stub = sinon.stub(sys_win, 'get_winsvc_version').callsFake((cb) => {
cb(new Error('No winsvc version found.'));
});
})

after(() => {
get_winsvc_version_stub.restore();
})

it('should return error to get os edition', (done) => {
updater.check_for_update_winsvc((err) => {
should.exist(err);
err.message.should.containEql('Error to get winsvc version');
done();
});
})
})

describe('when get_stable_version_winsvc return error', () => {

var get_winsvc_version_2_1_0_stub;
var get_stable_version_winsvc_error;
before(() => {
get_winsvc_version_2_1_0_stub = sinon.stub(sys_win, 'get_winsvc_version').callsFake((cb) => {
cb(null, "2.0.1");
});
get_stable_version_winsvc_error = sinon.stub(updater, 'get_stable_version_winsvc').callsFake((cb) => {
cb(new Error('Error get_stable_version_winsvc.'));
});
})

after(() => {
get_winsvc_version_2_1_0_stub.restore();
get_stable_version_winsvc_error.restore();
})

it('should return 2.0.1', (done) => {
updater.check_for_update_winsvc((err) => {
should.exist(err);
err.message.should.containEql('Error to get stable version');
done();
});
})
})

describe('when get_stable_version_winsvc return ok and is updated', () => {

var get_winsvc_version_2_0_2_stub;
var get_stable_version_winsvc_ok;
before(() => {
get_winsvc_version_2_0_2_stub = sinon.stub(sys_win, 'get_winsvc_version').callsFake((cb) => {
cb(null, "2.0.2");
});
get_stable_version_winsvc_ok = sinon.stub(updater, 'get_stable_version_winsvc').callsFake((cb) => {
cb(null, "2.0.2");
});

})

after(() => {
get_winsvc_version_2_0_2_stub.restore();
get_stable_version_winsvc_ok.restore();
})

it('should return 2.0.0', (done) => {
updater.check_for_update_winsvc((err) => {
should.not.exist(err);
done();
});
})
})

describe('when get_stable_version_winsvc return ok and not updated and winsvc return error', () => {

var get_winsvc_version_2_0_3_stub;
var get_stable_version_winsvc_ok;
var update_winsvc_error;
before(() => {
get_winsvc_version_2_0_3_stub = sinon.stub(sys_win, 'get_winsvc_version').callsFake((cb) => {
cb(null, "2.0.3");
});
get_stable_version_winsvc_ok = sinon.stub(updater, 'get_stable_version_winsvc').callsFake((cb) => {
cb(null, "2.0.4");
});
update_winsvc_error = sinon.stub(updater, 'update_winsvc').callsFake((path, cb) => {
cb(new Error('Error to update winsvc.'));
});
})

after(() => {
get_winsvc_version_2_0_3_stub.restore();
get_stable_version_winsvc_ok.restore();
update_winsvc_error.restore();
})

it('should return 2.0.4', (done) => {
updater.check_for_update_winsvc((err) => {
should.exist(err);
err.message.should.containEql('error to update winsvc');
done();
});
})
})

describe('when get_stable_version_winsvc return ok and not updated and winsvc update ok', () => {

var get_winsvc_version_2_0_4_stub;
var get_stable_version_winsvc_ok;
var update_winsvc_error;
before(() => {
get_winsvc_version_2_0_4_stub = sinon.stub(sys_win, 'get_winsvc_version').callsFake((cb) => {
cb(null, "2.0.4");
});
get_stable_version_winsvc_ok = sinon.stub(updater, 'get_stable_version_winsvc').callsFake((cb) => {
cb(null, "2.0.5");
});
update_winsvc_error = sinon.stub(updater, 'update_winsvc').callsFake((path, cb) => {
cb(null, true);
});
})

after(() => {
get_winsvc_version_2_0_4_stub.restore();
get_stable_version_winsvc_ok.restore();
update_winsvc_error.restore();
})

it('should return 2.0.4', (done) => {
updater.check_for_update_winsvc((err, isUpdated) => {
should.not.exist(err);
should.exist(isUpdated);
console.log(isUpdated)
done();
});
})
})
})