Skip to content

Commit 4a8e8b2

Browse files
authored
Use environment variables instead of string interpolation in PowerShell downloader (#291)
1 parent 452d527 commit 4a8e8b2

File tree

1 file changed

+19
-13
lines changed

1 file changed

+19
-13
lines changed

src/manage/urlutils.py

Lines changed: 19 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -277,6 +277,7 @@ def _powershell_urlopen(request):
277277

278278
def _powershell_urlretrieve(request):
279279
from base64 import b64encode
280+
import json
280281
import subprocess
281282

282283
headers = request.headers
@@ -287,23 +288,27 @@ def _powershell_urlretrieve(request):
287288
if auth:
288289
headers = {**headers, "Authorization": _basic_auth_header(*auth)}
289290

290-
def _f(v):
291-
if isinstance(v, str):
292-
return "'" + v.replace("'", "''") + "'"
293-
return str(v)
294-
295-
ps_headers = " ".join(f"{k!r}={_f(v)};" for k, v in headers.items())
296-
297291
powershell = Path(os.getenv("SystemRoot")) / "System32/WindowsPowerShell/v1.0/powershell.exe"
298-
script = fr"""$ProgressPreference = "SilentlyContinue"
299-
$headers = @{{ {ps_headers} }}
300-
$r = Invoke-WebRequest '{request.url}' -UseBasicParsing `
292+
# Security hardening: avoid PowerShell command injection by using env vars instead of interpolation
293+
script = r"""$ProgressPreference = "SilentlyContinue"
294+
$url = $env:PYMANAGER_URL
295+
$outfile = $env:PYMANAGER_OUTFILE
296+
$method = $env:PYMANAGER_METHOD
297+
$headers = ConvertFrom-Json $env:PYMANAGER_HEADERS
298+
$r = Invoke-WebRequest -Uri $url -UseBasicParsing `
301299
-Headers $headers `
302300
-UseDefaultCredentials `
303-
-Method "{request.method}" `
304-
-OutFile "{request.outfile}"
301+
-Method $method `
302+
-OutFile $outfile
305303
"""
306-
LOGGER.debug("PowerShell script: %s", script)
304+
LOGGER.debug("PowerShell download invoked (env-based)")
305+
env = os.environ.copy()
306+
env.update({
307+
"PYMANAGER_URL": request.url,
308+
"PYMANAGER_OUTFILE": str(request.outfile),
309+
"PYMANAGER_METHOD": request.method,
310+
"PYMANAGER_HEADERS": json.dumps(headers),
311+
})
307312
with subprocess.Popen(
308313
[powershell,
309314
"-ExecutionPolicy", "Bypass",
@@ -312,6 +317,7 @@ def _f(v):
312317
"-EncodedCommand", b64encode(script.encode("utf-16-le"))
313318
],
314319
cwd=request.outfile.parent,
320+
env=env,
315321
creationflags=subprocess.CREATE_NO_WINDOW,
316322
stdout=subprocess.PIPE,
317323
stderr=subprocess.STDOUT,

0 commit comments

Comments
 (0)