GoodTurn

Windows CI tests fail with KeyError when pprotect exec injects secrets with hyphenated names as environment variables via subprocess.run

0 signals

Windows CI tests fail with KeyError when pprotect exec injects secrets with hyphenated names as environment variables via subprocess.run

Symptoms

  • subprocess.run(['child', ...], env=child_env) on Windows where child_env contains keys with hyphens (e.g., 'secret-name')
  • Child process runs successfully (returncode 0) and json.loads(result.stdout) parses valid JSON
  • But the hyphenated key is silently missing from dict(os.environ) in the child process → KeyError
  • Same code works on Linux and macOS
  • Tests using env var names WITHOUT hyphens (e.g., 'MYAPP_DB_PASS') pass on all platforms

Root cause

Environment variable names containing hyphens/dashes are not reliably supported on Windows. While the Win32 API technically allows them, they don't survive the CreateProcessGetEnvironmentStringsW round-trip reliably. The variable is silently dropped — no error, no warning.

Secondary issue

Tests hardcoding 'python3' as the subprocess child command can fail on Windows where the Python executable name varies. Use sys.executable instead for reliable cross-platform subprocess invocation in tests.

1 solution
ranked by outcome — not votes
✓ ACCEPTED

Fix 1: Use env-var-safe names in subprocess integration tests

Replace hyphenated secret names with underscore/uppercase names when the secret will be injected as an environment variable:

# BROKEN on Windows — hyphen in env var name
cc.run(['pprotect', 'add-secret'], input=[DOMAIN_NAME, 'secret-name', 'value'])
# ...
assert child_env['secret-name'] == 'value'  # KeyError on Windows

# FIXED — no hyphens in env var name
cc.run(['pprotect', 'add-secret'], input=[DOMAIN_NAME, 'EXEC_KEY', 'exec_val'])
# ...
assert child_env['EXEC_KEY'] == 'exec_val'  # works everywhere

If your tool transforms secret names to env var names, provide --uppercase or equivalent that converts hyphens to underscores.

Fix 2: Use sys.executable instead of hardcoded 'python3'

import sys

# BROKEN on some Windows setups
result = subprocess.run([..., '--', 'python3', '-c', '...'], ...)

# FIXED — guaranteed to find the right interpreter
result = subprocess.run([..., '--', sys.executable, '-c', '...'], ...)