diff --git a/install-scripts/install-safe-chain.ps1 b/install-scripts/install-safe-chain.ps1 index f95fdfd..fac897b 100644 --- a/install-scripts/install-safe-chain.ps1 +++ b/install-scripts/install-safe-chain.ps1 @@ -150,6 +150,19 @@ function Remove-VoltaInstallation { # Main installation function Install-SafeChain { + # Validate SAFE_CHAIN_DIR before using it to write files + if ($env:SAFE_CHAIN_DIR) { + if (-not [System.IO.Path]::IsPathRooted($env:SAFE_CHAIN_DIR)) { + Write-Error-Custom "SAFE_CHAIN_DIR must be an absolute path, got: $($env:SAFE_CHAIN_DIR)" + } + if ($env:SAFE_CHAIN_DIR -match '\.\.') { + Write-Error-Custom "SAFE_CHAIN_DIR must not contain path traversal (..)" + } + if ($env:SAFE_CHAIN_DIR -match '^[A-Za-z]:[/\\]?$' -or $env:SAFE_CHAIN_DIR -eq '/') { + Write-Error-Custom "SAFE_CHAIN_DIR cannot be a root or drive-root directory" + } + } + # Show deprecation warning if SAFE_CHAIN_VERSION is set if (-not [string]::IsNullOrWhiteSpace($env:SAFE_CHAIN_VERSION)) { Write-Warn "SAFE_CHAIN_VERSION environment variable is deprecated." diff --git a/install-scripts/install-safe-chain.sh b/install-scripts/install-safe-chain.sh index f65b1d7..57b06d3 100755 --- a/install-scripts/install-safe-chain.sh +++ b/install-scripts/install-safe-chain.sh @@ -247,6 +247,20 @@ parse_arguments() { # Main installation main() { + # Validate SAFE_CHAIN_DIR before using it to write files + if [ -n "${SAFE_CHAIN_DIR}" ]; then + case "${SAFE_CHAIN_DIR}" in + /*) ;; # absolute path — OK + *) error "SAFE_CHAIN_DIR must be an absolute path, got: ${SAFE_CHAIN_DIR}" ;; + esac + case "${SAFE_CHAIN_DIR}" in + *../*|*/..*|..) error "SAFE_CHAIN_DIR must not contain path traversal (..)" ;; + esac + if [ "${SAFE_CHAIN_DIR}" = "/" ]; then + error "SAFE_CHAIN_DIR cannot be the root directory" + fi + fi + # Initialize argument flags USE_CI_SETUP=false diff --git a/install-scripts/uninstall-safe-chain.ps1 b/install-scripts/uninstall-safe-chain.ps1 index 32a27a5..a4f1fc1 100644 --- a/install-scripts/uninstall-safe-chain.ps1 +++ b/install-scripts/uninstall-safe-chain.ps1 @@ -75,6 +75,19 @@ function Remove-VoltaInstallation { # Main uninstallation function Uninstall-SafeChain { + # Validate SAFE_CHAIN_DIR before using it to delete files + if ($env:SAFE_CHAIN_DIR) { + if (-not [System.IO.Path]::IsPathRooted($env:SAFE_CHAIN_DIR)) { + Write-Error-Custom "SAFE_CHAIN_DIR must be an absolute path, got: $($env:SAFE_CHAIN_DIR)" + } + if ($env:SAFE_CHAIN_DIR -match '\.\.') { + Write-Error-Custom "SAFE_CHAIN_DIR must not contain path traversal (..)" + } + if ($env:SAFE_CHAIN_DIR -match '^[A-Za-z]:[/\\]?$' -or $env:SAFE_CHAIN_DIR -eq '/') { + Write-Error-Custom "SAFE_CHAIN_DIR cannot be a root or drive-root directory" + } + } + Write-Info "Uninstalling safe-chain..." # Run teardown if safe-chain is available diff --git a/install-scripts/uninstall-safe-chain.sh b/install-scripts/uninstall-safe-chain.sh index fcb5153..5440730 100755 --- a/install-scripts/uninstall-safe-chain.sh +++ b/install-scripts/uninstall-safe-chain.sh @@ -139,6 +139,20 @@ remove_nvm_installation() { # Main uninstallation main() { + # Validate SAFE_CHAIN_DIR before using it to delete files + if [ -n "${SAFE_CHAIN_DIR}" ]; then + case "${SAFE_CHAIN_DIR}" in + /*) ;; # absolute path — OK + *) error "SAFE_CHAIN_DIR must be an absolute path, got: ${SAFE_CHAIN_DIR}" ;; + esac + case "${SAFE_CHAIN_DIR}" in + *../*|*/..*|..) error "SAFE_CHAIN_DIR must not contain path traversal (..)" ;; + esac + if [ "${SAFE_CHAIN_DIR}" = "/" ]; then + error "SAFE_CHAIN_DIR cannot be the root directory" + fi + fi + SAFE_CHAIN_LOCATION="$DOT_SAFE_CHAIN/bin/safe-chain" if [ -x "$SAFE_CHAIN_LOCATION" ]; then diff --git a/packages/safe-chain/src/shell-integration/startup-scripts/init-fish.fish b/packages/safe-chain/src/shell-integration/startup-scripts/init-fish.fish index a705634..11d1d55 100644 --- a/packages/safe-chain/src/shell-integration/startup-scripts/init-fish.fish +++ b/packages/safe-chain/src/shell-integration/startup-scripts/init-fish.fish @@ -1,4 +1,8 @@ -set -l safe_chain_base (if set -q SAFE_CHAIN_DIR; echo $SAFE_CHAIN_DIR; else; echo $HOME/.safe-chain; end) +# Guard against PATH separator injection: reject SAFE_CHAIN_DIR values containing ':' +set -l safe_chain_base $HOME/.safe-chain +if set -q SAFE_CHAIN_DIR; and not string match -q '*:*' -- $SAFE_CHAIN_DIR + set safe_chain_base $SAFE_CHAIN_DIR +end set -gx PATH $PATH $safe_chain_base/bin function npx diff --git a/packages/safe-chain/src/shell-integration/startup-scripts/init-posix.sh b/packages/safe-chain/src/shell-integration/startup-scripts/init-posix.sh index b567902..45c6fd9 100644 --- a/packages/safe-chain/src/shell-integration/startup-scripts/init-posix.sh +++ b/packages/safe-chain/src/shell-integration/startup-scripts/init-posix.sh @@ -1,4 +1,10 @@ -export PATH="$PATH:${SAFE_CHAIN_DIR:-$HOME/.safe-chain}/bin" +# Guard against PATH separator injection: reject SAFE_CHAIN_DIR values containing ':' +case "${SAFE_CHAIN_DIR}" in + *:*) _sc_base="${HOME}/.safe-chain" ;; + *) _sc_base="${SAFE_CHAIN_DIR:-${HOME}/.safe-chain}" ;; +esac +export PATH="$PATH:${_sc_base}/bin" +unset _sc_base function npx() { wrapSafeChainCommand "npx" "$@" diff --git a/packages/safe-chain/src/shell-integration/startup-scripts/init-pwsh.ps1 b/packages/safe-chain/src/shell-integration/startup-scripts/init-pwsh.ps1 index bcdd1c6..f814917 100644 --- a/packages/safe-chain/src/shell-integration/startup-scripts/init-pwsh.ps1 +++ b/packages/safe-chain/src/shell-integration/startup-scripts/init-pwsh.ps1 @@ -2,7 +2,8 @@ # $IsWindows is only available in PowerShell Core 6.0+. If it doesn't exist, assume Windows PowerShell $isWindowsPlatform = if (Test-Path variable:IsWindows) { $IsWindows } else { $true } $pathSeparator = if ($isWindowsPlatform) { ';' } else { ':' } -$safeChainBase = if ($env:SAFE_CHAIN_DIR) { $env:SAFE_CHAIN_DIR } else { Join-Path $HOME '.safe-chain' } +# Guard against PATH separator injection: reject SAFE_CHAIN_DIR values containing the path separator +$safeChainBase = if ($env:SAFE_CHAIN_DIR -and -not $env:SAFE_CHAIN_DIR.Contains($pathSeparator)) { $env:SAFE_CHAIN_DIR } else { Join-Path $HOME '.safe-chain' } $safeChainBin = Join-Path $safeChainBase 'bin' $env:PATH = "$env:PATH$pathSeparator$safeChainBin"