fix(keychain): single source of truth for key list + robust USER fallback

Addresses Greptile review on PR #407:

- P1: setup-keychain.sh ALL_KEYS was missing GOOGLE_GENAI_API_KEY and
  XIAOHONGSHU_API_BASE relative to _load_keychain's inline list, so users
  manually storing those keys would not see them in --list and the
  interactive prompt would never offer to set them.

  Hoist the canonical key list into lib/env.py::KEYCHAIN_KEYS, have
  get_config() pass it through, and add a parity test that parses
  ALL_KEYS out of setup-keychain.sh and asserts equality. Drift is now
  caught at CI time instead of after a user reports a missing key.

- P2: os.environ.get("USER", "") silently returned "" under sudo, in
  Docker without --env USER, or in CI runners that strip USER. The
  resulting `security find-generic-password -a ""` call would never
  match items stored by setup-keychain.sh, so all lookups silently
  returned nothing. Fall back to pwd.getpwuid(os.getuid()).pw_name when
  USER is absent.

The P2 process-listing comment ("secret visible briefly via ps because
security has no stdin path for -w") has no clean fix — the README
already documents the manual `security add-generic-password` invocation
as an alternative for users with strict secret hygiene.
This commit is contained in:
Trevin Chow
2026-05-16 19:25:05 -07:00
parent 74a387b093
commit d0dcf751f1
3 changed files with 52 additions and 9 deletions
+30
View File
@@ -10,6 +10,7 @@ Covers:
from __future__ import annotations
import re
import subprocess
import sys
from pathlib import Path
@@ -21,6 +22,8 @@ sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "skills" / "last30d
from lib import env # noqa: E402
SETUP_KEYCHAIN_SH = Path(__file__).resolve().parents[1] / "skills" / "last30days" / "scripts" / "setup-keychain.sh"
# ---------------------------------------------------------------------------
# _load_keychain unit tests
@@ -150,3 +153,30 @@ def test_get_config_openai_key_can_come_from_keychain(clean_env):
cfg = env.get_config()
assert cfg["OPENAI_API_KEY"] == "sk-from-kc"
assert cfg["OPENAI_AUTH_SOURCE"] == "api_key"
# ---------------------------------------------------------------------------
# Drift guard: lib/env.py KEYCHAIN_KEYS and setup-keychain.sh ALL_KEYS must
# stay in lockstep. A mismatch means users storing a key via the helper script
# wouldn't see it picked up by the loader, or vice versa.
# ---------------------------------------------------------------------------
def _parse_all_keys_from_shell(script: Path) -> list[str]:
text = script.read_text(encoding="utf-8")
match = re.search(r"ALL_KEYS=\(\s*(.*?)\s*\)", text, re.DOTALL)
if not match:
raise AssertionError(f"ALL_KEYS=( ... ) array not found in {script}")
body = match.group(1)
# Strip shell comments and split on whitespace
body = re.sub(r"#[^\n]*", "", body)
return [tok for tok in body.split() if tok]
def test_keychain_keys_match_setup_script():
shell_keys = _parse_all_keys_from_shell(SETUP_KEYCHAIN_SH)
python_keys = list(env.KEYCHAIN_KEYS)
assert shell_keys == python_keys, (
"lib/env.py::KEYCHAIN_KEYS and scripts/setup-keychain.sh::ALL_KEYS "
f"have drifted.\n python: {python_keys}\n shell: {shell_keys}"
)