Skip to content

Commit 5eb91b4

Browse files
authored
Refactor npm config functions and improve security
Refactor npm configuration handling and improve security for PAT.
1 parent 358cc57 commit 5eb91b4

1 file changed

Lines changed: 42 additions & 50 deletions

File tree

deepstudio/private-install.ps1

Lines changed: 42 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -44,12 +44,6 @@ function Require-Npm {
4444
}
4545
}
4646

47-
function Get-NpmPath {
48-
$cmd = Get-Command npm -ErrorAction Stop
49-
# On Windows this is typically ...\npm.cmd
50-
return $cmd.Source
51-
}
52-
5347
function Read-Secure([string]$msg) {
5448
$secure = Read-Host $msg -AsSecureString
5549
$bstr = [Runtime.InteropServices.Marshal]::SecureStringToBSTR($secure)
@@ -72,21 +66,30 @@ function Ensure-Registry([string]$reg) {
7266
return $reg
7367
}
7468

75-
function SetCfg([string]$k, [string]$v) {
76-
if ($DryRun) { Info "DRYRUN: npm config set --global $k <value>"; return }
77-
npm config set --global $k $v | Out-Null
78-
if ($LASTEXITCODE -ne 0) { throw "npm config set failed ($k). exit=$LASTEXITCODE" }
69+
function New-TempNpmrc {
70+
$path = Join-Path $env:TEMP ("deepstudio-npmrc-" + [Guid]::NewGuid().ToString("N") + ".npmrc")
71+
return $path
7972
}
8073

81-
function DelCfg([string]$k) {
82-
if ($DryRun) { Info "DRYRUN: npm config delete --global $k"; return }
83-
try { npm config delete --global $k | Out-Null } catch {}
84-
}
74+
function Write-IsolatedNpmrc([string]$path, [string]$registry, [string]$authPrefix, [string]$pat) {
75+
# Use _authToken to avoid conflicts with user's existing .npmrc and avoid base64 encoding pitfalls.
76+
# NOTE: This writes PAT into a temporary file; we delete it in finally.
77+
$content = @"
78+
registry=$registry/
79+
${authPrefix}:_authToken=$pat
80+
"@
8581

86-
# IMPORTANT: Do NOT name parameters as $args (PowerShell automatic variable).
87-
function Run-NpmInstall([string[]]$npmArgs) {
88-
$cmdLine = "npm " + ($npmArgs -join " ")
82+
if ($DryRun) {
83+
Info "DRYRUN: write isolated npmrc to $path"
84+
Info ("DRYRUN: npmrc content (token masked):`nregistry=$registry/`n${authPrefix}:_authToken=$(Mask-Token $pat)")
85+
return
86+
}
8987

88+
# ASCII is safest for .npmrc formatting
89+
Set-Content -Path $path -Value $content -Encoding ASCII
90+
}
91+
92+
function Run-NpmViaCmd([string]$cmdLine) {
9093
if ($DryRun) {
9194
Info ("DRYRUN: " + $cmdLine)
9295
return
@@ -146,7 +149,6 @@ function Run-NpmInstall([string[]]$npmArgs) {
146149
}
147150
}
148151

149-
150152
# ---------------------------
151153
# Start
152154
# ---------------------------
@@ -171,7 +173,7 @@ $registry = Ensure-Registry $registryInput
171173
$uri = [Uri]$registry
172174
$authPrefix = "//" + $uri.Host + $uri.AbsolutePath + "/"
173175

174-
# Read PAT securely then TRIM it (fix common 401 due to whitespace/newlines)
176+
# Read PAT securely then TRIM it (fix common whitespace/newlines)
175177
$patRaw = Read-Secure "Enter Azure DevOps PAT (Packaging:Read)"
176178
if ([string]::IsNullOrWhiteSpace($patRaw)) { throw "PAT is empty." }
177179

@@ -183,16 +185,11 @@ Info ("PAT (masked): " + (Mask-Token $pat))
183185
Info "Tip: if masked prefix/suffix looks wrong, you likely pasted extra chars/spaces."
184186
Info ""
185187

186-
$patB64 = [Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes($pat))
187-
$pat = $null
188-
189-
# npm install args
188+
# npm install args / loglevel
190189
$logLevel = if ($VerboseInstall) { "verbose" } else { "notice" }
191-
$npmInstallArgs = @(
192-
"install", "-g", "$PackageName@latest",
193-
"--registry", "$registry/",
194-
"--loglevel", $logLevel
195-
)
190+
191+
# Create isolated npmrc to avoid user's existing ~/.npmrc conflicts
192+
$tmpNpmrc = New-TempNpmrc
196193

197194
# Optional: extra debug signal for npm
198195
if ($VerboseInstall) {
@@ -201,25 +198,23 @@ if ($VerboseInstall) {
201198
}
202199

203200
try {
204-
Info "Configuring npm auth for this registry..."
205-
SetCfg "registry" "$registry/"
206-
SetCfg "${authPrefix}:username" "ms"
207-
SetCfg "${authPrefix}:_password" $patB64
208-
SetCfg "${authPrefix}:email" "npm@example.com"
209-
# NOTE: always-auth intentionally not set (deprecated/invalid in modern npm)
201+
Info "Preparing isolated npm config (ignores your existing .npmrc)..."
202+
Write-IsolatedNpmrc -path $tmpNpmrc -registry $registry -authPrefix $authPrefix -pat $pat
203+
204+
# Build command line (use cmd.exe to avoid PowerShell alias/function issues with npm)
205+
# IMPORTANT: Use --userconfig so npm only reads our isolated temp npmrc for this run.
206+
$installCmd = 'npm install -g "{0}@latest" --registry "{1}/" --loglevel {2} --userconfig "{3}"' -f $PackageName, $registry, $logLevel, $tmpNpmrc
210207

211208
Info ""
212-
Info ("Command: npm " + ($npmInstallArgs -join " "))
209+
Info ("Command: " + $installCmd)
213210
Info ""
214211

215-
Run-NpmInstall $npmInstallArgs
212+
Run-NpmViaCmd $installCmd
216213

217-
# Extra safety: verify it's actually installed
214+
# Extra safety: verify it's actually installed (still using isolated userconfig is ok)
218215
if (-not $DryRun) {
219-
npm list -g --depth=0 "$PackageName" | Out-Null
220-
if ($LASTEXITCODE -ne 0) {
221-
throw "Install command finished but package not found in global npm list. Something is wrong."
222-
}
216+
$verifyCmd = 'npm list -g --depth=0 "{0}" --userconfig "{1}"' -f $PackageName, $tmpNpmrc
217+
Run-NpmViaCmd $verifyCmd
223218
}
224219

225220
Info ""
@@ -247,7 +242,7 @@ catch {
247242
} else {
248243
Warn ""
249244
Warn "Tip: re-run with verbose:"
250-
Warn " `$env:DEEPSTUDIO_VERBOSE='1'; irm <url> | iex"
245+
Warn " `$env:DEEPSTUDIO_VERBOSE='1'; `$env:DEEPSTUDIO_LOG='1'; irm <url> | iex"
251246
}
252247

253248
if ($EnableLog -and -not $DryRun) {
@@ -258,18 +253,15 @@ catch {
258253
throw
259254
}
260255
finally {
261-
Info ""
262-
Info "Cleaning up npm global config..."
263-
DelCfg "registry"
264-
DelCfg "${authPrefix}:username"
265-
DelCfg "${authPrefix}:_password"
266-
DelCfg "${authPrefix}:email"
267-
256+
# Clean up temp npmrc and env vars
257+
if (-not $DryRun) {
258+
Remove-Item $tmpNpmrc -Force -ErrorAction SilentlyContinue
259+
}
268260
if ($VerboseInstall) {
269261
Remove-Item Env:\NPM_CONFIG_LOGLEVEL -ErrorAction SilentlyContinue
270262
Remove-Item Env:\NPM_CONFIG_PROGRESS -ErrorAction SilentlyContinue
271263
}
272-
264+
Info ""
273265
Info "✅ Cleanup complete."
274266
Info ""
275267
}

0 commit comments

Comments
 (0)