patch: refactor permission code for windows

This commit is contained in:
Edwin Joassart 2025-07-17 17:50:15 +02:00
parent 2eb3acd094
commit fdf8820b5a
No known key found for this signature in database
GPG Key ID: 6337EBE5AC716051
2 changed files with 42 additions and 39 deletions

View File

@ -102,31 +102,7 @@ export async function elevateCommand(
return { cancelled: false };
}
const isWindows = os.platform() === 'win32';
// Augment the command to pass the environment variables as args
// Powershell (required to ask for elevated privileges) as of win10 cannot pass environment variables as a map, so we pass them as args
// For the sake of consistency, we do the same for Linux and macOS, but it's likely not required
// Once we deprecate win10 we can move to a more elegant solution (passing the env to powershell)
// const envFilter: string[] = [
// 'ETCHER_SERVER_ADDRESS',
// 'ETCHER_SERVER_PORT',
// 'ETCHER_SERVER_ID',
// 'ETCHER_NO_SPAWN_UTIL',
// 'ETCHER_TERMINATE_TIMEOUT',
// 'UV_THREADPOOL_SIZE',
// ];
// const preparedCmd = [
// command[0],
// ...command.slice(1),
// ...Object.keys(options.env)
// .filter((key) => Object.prototype.hasOwnProperty.call(options.env, key))
// .filter((key) => envFilter.includes(key))
// .map((key) => `--${key}=${options.env[key]}`),
// ];
if (isWindows) {
if (os.platform() === 'win32') {
return elevateScriptWindows(command, options.env);
}
if (

View File

@ -1,11 +1,6 @@
/*
* This is heavily inspired (read: a ripof) https://github.com/balena-io-modules/sudo-prompt
* Which was a fork of https://github.com/jorangreef/sudo-prompt
/* *
*
* This and the original code was released under The MIT License (MIT)
*
* Copyright (c) 2015 Joran Dirk Greef
* Copyright (c) 2024 Balena
* Copyright (c) 2025 Balena
*
The MIT License (MIT)
@ -35,7 +30,29 @@ export async function sudo(
env: any,
): Promise<{ cancelled: boolean; stdout?: string; stderr?: string }> {
try {
// WindowsWriteCommandScript(instance, end)
// Augment the command to pass the environment variables as args
// Powershell (required to ask for elevated privileges) as of win10
// cannot pass environment variables as a map, so we pass them as args
// this is a workaround as we can't use an equivalent of `sudo -E` on Windows
const envFilter: string[] = [
'ETCHER_SERVER_ADDRESS',
'ETCHER_SERVER_PORT',
'ETCHER_SERVER_ID',
'ETCHER_NO_SPAWN_UTIL',
'ETCHER_TERMINATE_TIMEOUT',
'UV_THREADPOOL_SIZE',
];
const preparedCmd = [
command[0],
...command.slice(1),
...Object.keys(env)
.filter((key) => Object.prototype.hasOwnProperty.call(env, key))
.filter((key) => envFilter.includes(key))
.map((key) => `--${key}=${env[key]}`),
];
const spawnCommand = [];
spawnCommand.push('Start-Process');
@ -45,12 +62,13 @@ export async function sudo(
// Escape characters for PowerShell using single quotes:
// Escape single quotes for PowerShell using backtick:
// See: https://ss64.com/ps/syntax-esc.html
spawnCommand.push(`'${command[0].replace(/'/g, "`'")}'`);
spawnCommand.push(`'${preparedCmd[0].replace(/'/g, "`'")}'`);
spawnCommand.push('-ArgumentList');
// Join and escape arguments for PowerShell
spawnCommand.push(
`'${command
`'${preparedCmd
.slice(1)
.map((a) => a.replace(/'/g, "`'"))
.join(' ')}'`,
@ -58,18 +76,27 @@ export async function sudo(
spawnCommand.push('-WindowStyle hidden');
spawnCommand.push('-Verb runAs');
const child = spawn('powershell.exe', spawnCommand, {
env,
});
const child = spawn('powershell.exe', spawnCommand);
let result = { status: 'waiting' };
// child.stdout.on('data', (data) => {
// console.log(`stdout: ${data}`);
// });
// child.stderr.on('data', (data) => {
// console.error(`stderr: ${data}`);
// result = { status: data.toString() };
// });
child.on('close', (code) => {
if (code === 0) {
// User accepted UAC, process started
console.log('UAC accepted, process started');
result = { status: 'granted' };
} else {
// User cancelled or error occurred
console.log('UAC cancelled or error occurred');
result = { status: 'cancelled' };
}
});
@ -80,7 +107,7 @@ export async function sudo(
// we don't spawn directly in the promise otherwise resolving stop the process
return new Promise((resolve, reject) => {
setTimeout(() => {
setInterval(() => {
if (result.status === 'waiting') {
// Still waiting for user input, don't resolve or reject yet
return;