Skip to content

Commit 36baafc

Browse files
joshsmithxrmclaude
andcommitted
refactor: use positional args for bash -c in devcontainer exec
Replace string interpolation of $workdir (and other variables) in bash -c commands with positional arguments via -- $arg. This prevents shell metacharacter injection from directory/branch names. Converts all 13 call sites to use 'cd "$1" && ...' -- $workdir pattern, verified working through the PowerShell → devcontainer exec → docker exec → bash argument chain. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 978a40e commit 36baafc

File tree

1 file changed

+13
-14
lines changed

1 file changed

+13
-14
lines changed

scripts/devcontainer.ps1

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -373,14 +373,14 @@ switch ($Command) {
373373
$subdir = Select-WorkingDirectory -Target $Target
374374
$workdir = if ($subdir) { $subdir } else { '.' }
375375
Write-Step 'Building PPDS CLI...'
376-
devcontainer exec --workspace-folder $WorkspaceFolder bash -c "cd $workdir && dotnet build src/PPDS.Cli -f net10.0"
376+
devcontainer exec --workspace-folder $WorkspaceFolder bash -c 'cd "$1" && dotnet build src/PPDS.Cli -f net10.0' -- $workdir
377377
if ($LASTEXITCODE -ne 0) {
378378
Write-Err 'Build failed.'
379379
exit 1
380380
}
381381

382382
Write-Step 'Launching TUI...'
383-
devcontainer exec --workspace-folder $WorkspaceFolder bash -c "cd $workdir && PPDS_FORCE_TUI=1 dotnet src/PPDS.Cli/bin/Debug/net10.0/ppds.dll"
383+
devcontainer exec --workspace-folder $WorkspaceFolder bash -c 'cd "$1" && PPDS_FORCE_TUI=1 dotnet src/PPDS.Cli/bin/Debug/net10.0/ppds.dll' -- $workdir
384384
}
385385

386386
'down' {
@@ -406,12 +406,12 @@ switch ($Command) {
406406
$workdir = if ($subdir) { $subdir } else { '.' }
407407

408408
# Get branch name and local HEAD SHA from container
409-
$branch = (devcontainer exec --workspace-folder $WorkspaceFolder bash -c "cd $workdir && git branch --show-current").Trim()
409+
$branch = (devcontainer exec --workspace-folder $WorkspaceFolder bash -c 'cd "$1" && git branch --show-current' -- $workdir).Trim()
410410
if (-not $branch) {
411411
Write-Err "Could not determine branch (detached HEAD?)."
412412
exit 1
413413
}
414-
$localSha = (devcontainer exec --workspace-folder $WorkspaceFolder bash -c "cd $workdir && git rev-parse HEAD").Trim()
414+
$localSha = (devcontainer exec --workspace-folder $WorkspaceFolder bash -c 'cd "$1" && git rev-parse HEAD' -- $workdir).Trim()
415415

416416
# Compare against actual remote state (not local tracking refs which may be stale)
417417
$remoteSha = (git -C $WorkspaceFolder ls-remote origin "refs/heads/${branch}" 2>$null)
@@ -423,11 +423,11 @@ switch ($Command) {
423423
}
424424

425425
if (-not $remoteSha) {
426-
$ahead = (devcontainer exec --workspace-folder $WorkspaceFolder bash -c "cd $workdir && git rev-list --count HEAD").Trim()
426+
$ahead = (devcontainer exec --workspace-folder $WorkspaceFolder bash -c 'cd "$1" && git rev-list --count HEAD' -- $workdir).Trim()
427427
Write-Step "New branch '$branch' ($ahead commit(s))."
428428
}
429429
else {
430-
$ahead = (devcontainer exec --workspace-folder $WorkspaceFolder bash -c "cd $workdir && git rev-list --count $remoteSha..HEAD 2>/dev/null").Trim()
430+
$ahead = (devcontainer exec --workspace-folder $WorkspaceFolder bash -c 'cd "$1" && git rev-list --count "$2..HEAD" 2>/dev/null' -- $workdir $remoteSha).Trim()
431431
if (-not $ahead -or $ahead -eq '0') {
432432
Write-Ok "Branch '$branch' is already up-to-date on origin."
433433
return
@@ -438,9 +438,9 @@ switch ($Command) {
438438
# Check if force push is needed (e.g., after rebasing onto main)
439439
$forceNeeded = $false
440440
if ($remoteSha) {
441-
$hasRemote = (devcontainer exec --workspace-folder $WorkspaceFolder bash -c "cd $workdir && git cat-file -t $remoteSha 2>/dev/null && echo yes || echo no").Trim()
441+
$hasRemote = (devcontainer exec --workspace-folder $WorkspaceFolder bash -c 'cd "$1" && git cat-file -t "$2" 2>/dev/null && echo yes || echo no' -- $workdir $remoteSha).Trim()
442442
if ($hasRemote -eq 'yes') {
443-
$isFF = (devcontainer exec --workspace-folder $WorkspaceFolder bash -c "cd $workdir && git merge-base --is-ancestor $remoteSha HEAD 2>/dev/null && echo yes || echo no").Trim()
443+
$isFF = (devcontainer exec --workspace-folder $WorkspaceFolder bash -c 'cd "$1" && git merge-base --is-ancestor "$2" HEAD 2>/dev/null && echo yes || echo no' -- $workdir $remoteSha).Trim()
444444
}
445445
else {
446446
$isFF = 'no'
@@ -453,7 +453,7 @@ switch ($Command) {
453453

454454
# Create git bundle in container
455455
Write-Step 'Bundling commits...'
456-
devcontainer exec --workspace-folder $WorkspaceFolder bash -c "cd $workdir && git bundle create /tmp/push.bundle $branch" | Out-Null
456+
devcontainer exec --workspace-folder $WorkspaceFolder bash -c 'cd "$1" && git bundle create /tmp/push.bundle "$2"' -- $workdir $branch | Out-Null
457457
if ($LASTEXITCODE -ne 0) {
458458
Write-Err 'Failed to create git bundle.'
459459
exit 1
@@ -498,7 +498,7 @@ switch ($Command) {
498498

499499
# Update container's remote tracking ref so git status shows up-to-date
500500
Write-Step 'Updating container remote refs...'
501-
devcontainer exec --workspace-folder $WorkspaceFolder bash -c "cd $workdir && git fetch origin $branch 2>/dev/null || git update-ref refs/remotes/origin/$branch HEAD"
501+
devcontainer exec --workspace-folder $WorkspaceFolder bash -c 'cd "$1" && git fetch origin "$2" 2>/dev/null || git update-ref "refs/remotes/origin/$2" HEAD' -- $workdir $branch
502502

503503
$verb = if ($forceNeeded) { 'Force pushed' } else { 'Pushed' }
504504
Write-Ok "$verb '$branch' to origin ($ahead commit(s))."
@@ -511,7 +511,7 @@ switch ($Command) {
511511
$workdir = if ($subdir) { $subdir } else { '.' }
512512

513513
# Get current branch
514-
$branch = (devcontainer exec --workspace-folder $WorkspaceFolder bash -c "cd $workdir && git branch --show-current").Trim()
514+
$branch = (devcontainer exec --workspace-folder $WorkspaceFolder bash -c 'cd "$1" && git branch --show-current' -- $workdir).Trim()
515515
if (-not $branch) {
516516
Write-Err "Could not determine branch (detached HEAD?)."
517517
exit 1
@@ -526,7 +526,7 @@ switch ($Command) {
526526

527527
# Rebase onto origin/main
528528
Write-Step "Rebasing '$branch' onto origin/main..."
529-
devcontainer exec --workspace-folder $WorkspaceFolder bash -c "cd $workdir && git rebase origin/main"
529+
devcontainer exec --workspace-folder $WorkspaceFolder bash -c 'cd "$1" && git rebase origin/main' -- $workdir
530530
if ($LASTEXITCODE -ne 0) {
531531
Write-Err "Rebase has conflicts."
532532
Write-Step 'Launching Claude Code to help resolve conflicts...'
@@ -537,8 +537,7 @@ switch ($Command) {
537537
}
538538
$safeBranch = $branch -replace '[^a-zA-Z0-9_\-/.]', ''
539539
$conflictPrompt = "A git rebase of branch '${safeBranch}' onto origin/main has resulted in merge conflicts. Run git status to see conflicted files. Analyze each conflict, resolve them, git add the resolved files, and run git rebase --continue. If there are multiple conflicting commits, continue resolving until the rebase is complete. ${planInstruction}"
540-
$escapedPrompt = $conflictPrompt.Replace("'", "'\\''")
541-
devcontainer exec --workspace-folder $WorkspaceFolder bash -c "cd $workdir && claude --dangerously-skip-permissions -p '${escapedPrompt}'"
540+
devcontainer exec --workspace-folder $WorkspaceFolder bash -c 'cd "$1" && claude --dangerously-skip-permissions -p "$2"' -- $workdir $conflictPrompt
542541
Write-Step "Claude session ended. Verify rebase is complete, then 'push' when ready."
543542
exit 1
544543
}

0 commit comments

Comments
 (0)