Skip to content

Commit b9d02aa

Browse files
chore: new release with minimal changes
1 parent 6e5c9d9 commit b9d02aa

File tree

4 files changed

+49
-11
lines changed

4 files changed

+49
-11
lines changed

src/core/selfUpdate.ts

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -177,9 +177,21 @@ export async function applyUpdateInstaller(installerPath: string, silent: boolea
177177

178178
if (elevate) {
179179
// Use PowerShell Start-Process -Verb RunAs
180-
// Use $args array to pass parameters safely to the script block, avoiding command injection
181-
const psCommand = '& { $p, $a = $args; Start-Process -FilePath $p -ArgumentList $a -Verb RunAs -Wait }';
182-
await execa('powershell', ['-NoProfile', '-NonInteractive', '-Command', psCommand, installerPath, ...args]);
180+
// To prevent command injection, we pass arguments via environment variables.
181+
const envVars: Record<string, string> = {
182+
'PS_INSTALLER_PATH': installerPath,
183+
'PS_INSTALLER_ARGS': args.join(' ')
184+
};
185+
186+
const psCommand = `
187+
$p = [System.Environment]::GetEnvironmentVariable('PS_INSTALLER_PATH')
188+
$a = [System.Environment]::GetEnvironmentVariable('PS_INSTALLER_ARGS')
189+
Start-Process -FilePath $p -ArgumentList $a -Verb RunAs -Wait
190+
`.trim();
191+
192+
await execa('powershell', ['-NoProfile', '-NonInteractive', '-Command', psCommand], {
193+
env: { ...process.env, ...envVars }
194+
});
183195
} else {
184196
await execa(installerPath, args);
185197
}

src/system/powershell.ts

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,40 @@
11
import { execa } from 'execa';
22
import { logger } from '../core/logger.js';
33

4-
export async function runPs(command: string, args: string[] = []): Promise<string> {
4+
export async function runPs(script: string, args: string[] = []): Promise<string> {
55
try {
6+
// To prevent command injection, we pass arguments via environment variables
7+
// and reconstruct the $args array inside PowerShell.
8+
// This ensures that arguments are never interpreted as code by the PowerShell parser.
9+
const envVars: Record<string, string> = {};
10+
args.forEach((arg, i) => {
11+
envVars[`PS_ARG_${i}`] = arg;
12+
});
13+
14+
const wrapper = `
15+
$args = @()
16+
for ($i = 0; $true; $i++) {
17+
$varName = "PS_ARG_$i"
18+
if (Test-Path "Env:$varName") {
19+
$args += [System.Environment]::GetEnvironmentVariable($varName)
20+
} else {
21+
break
22+
}
23+
}
24+
& { ${script} } @args
25+
`.trim();
26+
627
const { stdout } = await execa('powershell', [
728
'-NoProfile',
829
'-NonInteractive',
930
'-Command',
10-
command,
11-
...args
12-
]);
31+
wrapper
32+
], {
33+
env: { ...process.env, ...envVars }
34+
});
1335
return stdout.trim();
1436
} catch (error) {
15-
logger.debug(`PowerShell command failed: ${command}`, error);
37+
logger.debug(`PowerShell script failed: ${script}`, error);
1638
throw error;
1739
}
1840
}

src/system/service.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,8 +73,8 @@ export async function updateServiceBinPath(instance: string, port: number = 5432
7373

7474
const binPath = buildServiceBinPath(SYSTEM_PATHS.PROXY_EXE, instance, port, extraArgs);
7575

76-
// Use CIM/WMI to update service path with arguments
77-
await runPs('& { $svc = Get-CimInstance Win32_Service -Filter "Name=$($args[0])"; Invoke-CimMethod -InputObject $svc -MethodName Change -Arguments @{ PathName = $args[1] } }', [SERVICE_NAME, binPath]);
76+
// Use CIM/WMI to update service path with arguments safely
77+
await runPs('& { $svc = Get-CimInstance Win32_Service | Where-Object { $_.Name -eq $args[0] }; Invoke-CimMethod -InputObject $svc -MethodName Change -Arguments @{ PathName = $args[1] } }', [SERVICE_NAME, binPath]);
7878
}
7979

8080
export async function uninstallService() {

tests/system.test.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,11 @@ describe('System Module', () => {
5151

5252
const result = await isServiceInstalled();
5353
expect(result).toBe(true);
54-
expect(execa).toHaveBeenCalledWith('powershell', expect.arrayContaining([expect.stringContaining('Get-Service -Name $args[0]')]));
54+
expect(execa).toHaveBeenCalledWith(
55+
'powershell',
56+
expect.arrayContaining([expect.stringContaining('Get-Service -Name $args[0]')]),
57+
expect.any(Object)
58+
);
5559
});
5660

5761
it('should return false if service check fails', async () => {

0 commit comments

Comments
 (0)