Skip to content

Commit e86fcb7

Browse files
chore: new release with minimal changes
1 parent 705aac4 commit e86fcb7

File tree

13 files changed

+43
-31
lines changed

13 files changed

+43
-31
lines changed

CHANGELOG.md

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -95,5 +95,3 @@
9595
- **Build**: Resolved duplicate exports in service module.
9696
- **Types**: Fixed TypeScript errors in self-heal and service commands.
9797
- **Linting**: Corrected Markdown formatting issues.
98-
99-

docs/commands.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -328,4 +328,3 @@ Options:
328328
--json Output status in JSON format
329329
-h, --help display help for command
330330
```
331-

src/commands/auth.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ authCommand.command('set-service-account')
106106
if (scope === 'Machine') {
107107
// Hardening ACLs
108108
// Remove inheritance, grant Administrators and SYSTEM full access
109-
await runPs(`icacls "${dest}" /inheritance:r /grant:r "Administrators:(R)" "SYSTEM:(R)"`);
109+
await runPs('& { icacls $args[0] /inheritance:r /grant:r "Administrators:(R)" "SYSTEM:(R)" }', [dest]);
110110
logger.info('Applied security ACLs to credentials file.');
111111
}
112112

src/core/selfUpdate.ts

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

178178
if (elevate) {
179179
// Use PowerShell Start-Process -Verb RunAs
180-
const psCommand = `Start-Process -FilePath "${installerPath}" -ArgumentList "${args.join(' ')}" -Verb RunAs -Wait`;
181-
await execa('powershell', ['-NoProfile', '-Command', psCommand]);
180+
// We use a script block and pass arguments to avoid injection
181+
const psCommand = '& { param($p, $a) Start-Process -FilePath $p -ArgumentList $a -Verb RunAs -Wait }';
182+
await execa('powershell', ['-NoProfile', '-Command', psCommand, installerPath, args.join(' ')]);
182183
} else {
183184
await execa(installerPath, args);
184185
}

src/core/updater.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import crypto from 'crypto';
44
import path from 'path';
55
import { PATHS } from '../system/paths.js';
66
import { logger } from './logger.js';
7+
import { escapeRegExp } from './utils.js';
78

89
const GITHUB_REPO = 'GoogleCloudPlatform/cloud-sql-proxy';
910
const ASSET_NAME = 'cloud-sql-proxy.x64.exe';
@@ -46,7 +47,8 @@ export async function downloadProxy(version: string, targetPath: string = PATHS.
4647
// Extract checksum from release body
4748
const { body } = response.data;
4849
// Regex to match: | [cloud-sql-proxy.x64.exe](...) | <hash> |
49-
const checksumRegex = new RegExp(`\\| \\[${ASSET_NAME.replace(/\./g, '\\.')}\\]\\(.*?\\) \\| ([a-f0-9]{64}) \\|`);
50+
const escapedAssetName = escapeRegExp(ASSET_NAME);
51+
const checksumRegex = new RegExp(`\\| \\[${escapedAssetName}\\]\\(.*?\\) \\| ([a-f0-9]{64}) \\|`);
5052
const match = body.match(checksumRegex);
5153

5254
if (match && match[1]) {

src/core/utils.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,7 @@ export async function getTail(filePath: string, lines: number = 10): Promise<str
1212
export function sleep(ms: number) {
1313
return new Promise(resolve => setTimeout(resolve, ms));
1414
}
15+
16+
export function escapeRegExp(string: string): string {
17+
return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
18+
}

src/system/env.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,24 +8,24 @@ export async function setEnv(name: string, value: string, scope: 'Machine' | 'Us
88
throw new Error('Admin privileges required to set system environment variables.');
99
}
1010
logger.info(`Setting ${scope} environment variable: ${name}=${value}`);
11-
await runPs(`[Environment]::SetEnvironmentVariable("${name}", "${value}", "${scope}")`);
11+
await runPs('& { [Environment]::SetEnvironmentVariable($args[0], $args[1], $args[2]) }', [name, value, scope]);
1212
}
1313

1414
export async function setupEnvironment(scope: 'Machine' | 'User' = 'User', force: boolean = false) {
1515
logger.info(`Configuring ${scope} environment variables...`);
1616
const paths = scope === 'Machine' ? SYSTEM_PATHS : USER_PATHS;
1717

18-
const currentHome = await runPs(`[Environment]::GetEnvironmentVariable("${ENV_VARS.HOME}", "${scope}")`);
18+
const currentHome = await runPs('& { [Environment]::GetEnvironmentVariable($args[0], $args[1]) }', [ENV_VARS.HOME, scope]);
1919
if (force || !currentHome) {
2020
await setEnv(ENV_VARS.HOME, paths.HOME, scope);
2121
}
2222

23-
const currentLogs = await runPs(`[Environment]::GetEnvironmentVariable("${ENV_VARS.LOGS}", "${scope}")`);
23+
const currentLogs = await runPs('& { [Environment]::GetEnvironmentVariable($args[0], $args[1]) }', [ENV_VARS.LOGS, scope]);
2424
if (force || !currentLogs) {
2525
await setEnv(ENV_VARS.LOGS, paths.LOGS, scope);
2626
}
2727

28-
const currentProxy = await runPs(`[Environment]::GetEnvironmentVariable("${ENV_VARS.PROXY_PATH}", "${scope}")`);
28+
const currentProxy = await runPs('& { [Environment]::GetEnvironmentVariable($args[0], $args[1]) }', [ENV_VARS.PROXY_PATH, scope]);
2929
if (force || !currentProxy) {
3030
await setEnv(ENV_VARS.PROXY_PATH, paths.PROXY_EXE, scope);
3131
}
@@ -38,9 +38,9 @@ export async function checkEnvironment(scope: 'Machine' | 'User' = 'User'): Prom
3838
}
3939

4040
export async function checkEnvironmentDetailed(scope: 'Machine' | 'User' = 'User'): Promise<{ ok: boolean, problems: string[], values: { home: string, logs: string, proxy: string } }> {
41-
const home = await runPs(`[Environment]::GetEnvironmentVariable("${ENV_VARS.HOME}", "${scope}")`);
42-
const logs = await runPs(`[Environment]::GetEnvironmentVariable("${ENV_VARS.LOGS}", "${scope}")`);
43-
const proxy = await runPs(`[Environment]::GetEnvironmentVariable("${ENV_VARS.PROXY_PATH}", "${scope}")`);
41+
const home = await runPs('& { [Environment]::GetEnvironmentVariable($args[0], $args[1]) }', [ENV_VARS.HOME, scope]);
42+
const logs = await runPs('& { [Environment]::GetEnvironmentVariable($args[0], $args[1]) }', [ENV_VARS.LOGS, scope]);
43+
const proxy = await runPs('& { [Environment]::GetEnvironmentVariable($args[0], $args[1]) }', [ENV_VARS.PROXY_PATH, scope]);
4444

4545
const problems: string[] = [];
4646

src/system/powershell.ts

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

4-
export async function runPs(command: string): Promise<string> {
4+
export async function runPs(command: string, args: string[] = []): Promise<string> {
55
try {
6-
const { stdout } = await execa('powershell', ['-NoProfile', '-NonInteractive', '-Command', command]);
6+
const { stdout } = await execa('powershell', [
7+
'-NoProfile',
8+
'-NonInteractive',
9+
'-Command',
10+
command,
11+
...args
12+
]);
713
return stdout.trim();
814
} catch (error) {
915
logger.debug(`PowerShell command failed: ${command}`, error);
@@ -23,7 +29,7 @@ export async function isAdmin(): Promise<boolean> {
2329

2430
export async function getEnvVar(name: string, scope: 'Machine' | 'User' | 'Process' = 'Machine'): Promise<string | null> {
2531
try {
26-
const val = await runPs(`[Environment]::GetEnvironmentVariable("${name}", "${scope}")`);
32+
const val = await runPs('& { [Environment]::GetEnvironmentVariable($args[0], $args[1]) }', [name, scope]);
2733
return val || null;
2834
} catch {
2935
return null;

src/system/service.ts

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { logger } from '../core/logger.js';
44

55
export async function isServiceInstalled(): Promise<boolean> {
66
try {
7-
await runPs(`Get-Service -Name "${SERVICE_NAME}" -ErrorAction Stop`);
7+
await runPs('& { Get-Service -Name $args[0] -ErrorAction Stop }', [SERVICE_NAME]);
88
return true;
99
} catch {
1010
return false;
@@ -13,7 +13,7 @@ export async function isServiceInstalled(): Promise<boolean> {
1313

1414
export async function isServiceRunning(): Promise<boolean> {
1515
try {
16-
const status = await runPs(`(Get-Service -Name "${SERVICE_NAME}").Status`);
16+
const status = await runPs('& { (Get-Service -Name $args[0]).Status }', [SERVICE_NAME]);
1717
return status === 'Running';
1818
} catch {
1919
return false;
@@ -54,8 +54,8 @@ export async function installService(instance: string, port: number = 5432, extr
5454

5555
const binPath = buildServiceBinPath(SYSTEM_PATHS.PROXY_EXE, instance, port, extraArgs);
5656

57-
// Use New-Service with single-quoted BinaryPathName
58-
await runPs(`New-Service -Name "${SERVICE_NAME}" -BinaryPathName '${binPath}' -StartupType Automatic`);
57+
// Use New-Service with arguments to avoid injection
58+
await runPs('& { New-Service -Name $args[0] -BinaryPathName $args[1] -StartupType Automatic }', [SERVICE_NAME, binPath]);
5959
}
6060

6161
export async function updateServiceBinPath(instance: string, port: number = 5432, extraArgs: string[] = []) {
@@ -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
77-
await runPs(`$svc = Get-CimInstance Win32_Service -Filter "Name='${SERVICE_NAME}'"; Invoke-CimMethod -InputObject $svc -MethodName Change -Arguments @{ PathName = '${binPath}' }`);
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]);
7878
}
7979

8080
export async function uninstallService() {
@@ -90,23 +90,23 @@ export async function uninstallService() {
9090
await stopService();
9191

9292
// Use sc.exe delete instead of Remove-Service for better compatibility
93-
await runPs(`sc.exe delete "${SERVICE_NAME}"`);
93+
await runPs('& { sc.exe delete $args[0] }', [SERVICE_NAME]);
9494
}
9595

9696
export async function startService() {
9797
if (!await isAdmin()) {
9898
throw new Error('Admin privileges required to start service.');
9999
}
100100
logger.info(`Starting service ${SERVICE_NAME}...`);
101-
await runPs(`Start-Service -Name "${SERVICE_NAME}"`);
101+
await runPs('& { Start-Service -Name $args[0] }', [SERVICE_NAME]);
102102
}
103103

104104
export async function stopService() {
105105
if (!await isAdmin()) {
106106
throw new Error('Admin privileges required to stop service.');
107107
}
108108
logger.info(`Stopping service ${SERVICE_NAME}...`);
109-
await runPs(`Stop-Service -Name "${SERVICE_NAME}" -Force`);
109+
await runPs('& { Stop-Service -Name $args[0] -Force }', [SERVICE_NAME]);
110110
}
111111

112112
export async function setServiceStartupType(type: 'Automatic' | 'Manual' | 'Disabled' | 'Delayed') {
@@ -116,10 +116,10 @@ export async function setServiceStartupType(type: 'Automatic' | 'Manual' | 'Disa
116116

117117
if (type === 'Delayed') {
118118
// Delayed start is a special case of Automatic
119-
await runPs(`Set-Service -Name "${SERVICE_NAME}" -StartupType Automatic`);
120-
await runPs(`sc.exe config "${SERVICE_NAME}" start= delayed-auto`);
119+
await runPs('& { Set-Service -Name $args[0] -StartupType Automatic }', [SERVICE_NAME]);
120+
await runPs('& { sc.exe config $args[0] start= delayed-auto }', [SERVICE_NAME]);
121121
} else {
122-
await runPs(`Set-Service -Name "${SERVICE_NAME}" -StartupType ${type}`);
122+
await runPs('& { Set-Service -Name $args[0] -StartupType $args[1] }', [SERVICE_NAME, type]);
123123
}
124124
logger.info(`Service startup type set to ${type}.`);
125125
}

test.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
test

0 commit comments

Comments
 (0)