Skip to content

Commit

Permalink
feat: Allow to elevate system privileges in runtime (#191)
Browse files Browse the repository at this point in the history
  • Loading branch information
mykola-mokhnach authored Aug 19, 2023
1 parent 5d42008 commit 95e0acb
Show file tree
Hide file tree
Showing 3 changed files with 37 additions and 10 deletions.
17 changes: 9 additions & 8 deletions lib/installer.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { exec } from 'teen_process';
import log from './logger';
import ES6Error from 'es6-error';
import { queryRegistry } from './registry';
import { shellExec } from './utils';

// https://github.com/microsoft/WinAppDriver/releases
const WAD_VER = '1.2.99';
Expand Down Expand Up @@ -49,7 +50,7 @@ async function fetchMsiInstallLocation (installerGuid) {
const scriptPath = path.join(tmpRoot, 'get_wad_inst_location.vbs');
try {
await fs.writeFile(scriptPath, INST_LOCATION_SCRIPT_BY_GUID(installerGuid), 'latin1');
const {stdout} = await exec('cscript.exe', ['/Nologo', scriptPath]);
const {stdout} = await shellExec('cscript.exe', ['/Nologo', scriptPath]);
return _.trim(stdout);
} finally {
await fs.rimraf(tmpRoot);
Expand Down Expand Up @@ -125,14 +126,14 @@ async function downloadWAD () {
return installerPath;
}

const isAdmin = _.memoize(async function isAdmin () {
const isAdmin = async function isAdmin () {
try {
await exec('fsutil.exe', ['dirty', 'query', process.env.SystemDrive || 'C:']);
return true;
} catch (ign) {
return false;
}
});
};

async function setupWAD () {
if (!system.isWindows()) {
Expand All @@ -148,14 +149,14 @@ async function setupWAD () {
log.info(`WinAppDriver doesn't exist, setting up`);
}

if (!await isAdmin()) {
throw new Error(`You are not running as an administrator so WinAppDriver cannot be installed for you; please reinstall as admin`);
}

const installerPath = await downloadWAD();
log.info(`Running WinAppDriver v${WAD_VER} installer`);
try {
await exec(installerPath, ['/install', '/quiet', '/norestart']);
await shellExec(installerPath, ['/install', '/quiet', '/norestart']);
} catch (e) {
/** @type {import('node:child_process').ExecException} */
const error = e;
throw new Error(`WinAppDriver cannot be installed: ${error.message}. Exit code: ${error.code}`);
} finally {
await fs.rimraf(installerPath);
}
Expand Down
4 changes: 2 additions & 2 deletions lib/registry.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import _ from 'lodash';
import { exec } from 'teen_process';
import { shellExec } from './utils';

const REG = 'reg.exe';
const ENTRY_PATTERN = /^\s+(\w+)\s+([A-Z_]+)\s*(.*)/;
Expand Down Expand Up @@ -62,7 +62,7 @@ function parseRegQueryOutput (output) {
async function queryRegistry (root) {
let stdout;
try {
({stdout} = await exec(REG, ['query', root, '/s']));
({stdout} = await shellExec(REG, ['query', root, '/s']));
} catch (e) {
return [];
}
Expand Down
26 changes: 26 additions & 0 deletions lib/utils.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { util} from 'appium/support';
import { promisify } from 'node:util';
import { exec } from 'node:child_process';
import B from 'bluebird';

const execAsync = promisify(exec);

/**
* This API triggers UAC when necessary
* unlike the 'spawn' call used by teen_process's exec.
* See https://github.com/nodejs/node-v0.x-archive/issues/6797
*
* @param {string} cmd
* @param {string[]} args
* @param {import('node:child_process').ExecOptions & {timeoutMs?: number}} opts
* @returns {Promise<{stdout: string; stderr: string;}>}
* @throws {import('node:child_process').ExecException}
*/
export async function shellExec(cmd, args = [], opts = {}) {
const {
timeoutMs = 60 * 1000 * 5
} = opts;
const fullCmd = util.quote([cmd, ...args]);
return await B.resolve(execAsync(fullCmd, opts))
.timeout(timeoutMs, `The command '${fullCmd}' timed out after ${timeoutMs}ms`);
}

0 comments on commit 95e0acb

Please sign in to comment.