Skip to content

Commit 29593e0

Browse files
fix(p0): enforce system-scope upgrade guardrails (#33)
1 parent aabdad3 commit 29593e0

File tree

1 file changed

+17
-1
lines changed

1 file changed

+17
-1
lines changed

src/commands/upgrade.ts

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,12 @@ import {
1212
applyUpdatePortableExe,
1313
detectInstallContext
1414
} from '../core/selfUpdate.js';
15+
import { isAdmin } from '../system/powershell.js';
16+
17+
function isSystemScopePath(filePath: string): boolean {
18+
const normalized = filePath.toLowerCase();
19+
return normalized.includes('\\program files') || normalized.includes('\\program files (x86)') || normalized.includes('\\programdata');
20+
}
1521

1622
export const upgradeCommand = new Command('upgrade')
1723
.description('Upgrade cloudsqlctl to the latest version')
@@ -87,12 +93,22 @@ export const upgradeCommand = new Command('upgrade')
8793

8894
// 5. Apply Update
8995
const context = options.asset === 'auto' ? detectInstallContext() : options.asset;
96+
const admin = await isAdmin();
97+
const systemScope = isSystemScopePath(process.execPath);
98+
99+
if (context === 'installer' && !admin && options.elevate === false) {
100+
throw new Error('System-scope update requires elevation. Re-run without --no-elevate or run as admin.');
101+
}
90102

91103
if (context === 'installer' || asset.name.endsWith('.exe') && asset.name.includes('setup')) {
92104
if (!options.json) logger.info('Applying update via installer...');
93-
await applyUpdateInstaller(downloadPath, options.silent !== false, options.elevate !== false);
105+
const shouldElevate = !admin && options.elevate !== false;
106+
await applyUpdateInstaller(downloadPath, options.silent !== false, shouldElevate);
94107
} else {
95108
if (!options.json) logger.info('Applying portable update...');
109+
if (systemScope && !admin) {
110+
throw new Error('Portable updates to system-scope installs require admin. Use the installer or re-run as admin.');
111+
}
96112
// For portable, we need to know the target exe.
97113
// If running packaged, it's process.execPath.
98114
// If running node, we can't really update "node.exe", so we assume dev env and warn.

0 commit comments

Comments
 (0)