fix(env): strip null bytes from .env before python-dotenv loads
Null bytes in API key values (introduced by copy-paste) crash
os.environ[k] = v with ValueError: embedded null byte, preventing
hermes from starting at all.
This commit is contained in:
@@ -140,6 +140,10 @@ def _sanitize_env_file_if_needed(path: Path) -> None:
|
|||||||
This produces mangled values — e.g. a bot token duplicated 8×
|
This produces mangled values — e.g. a bot token duplicated 8×
|
||||||
(see #8908).
|
(see #8908).
|
||||||
|
|
||||||
|
Also strips embedded null bytes which crash ``os.environ[k] = v``
|
||||||
|
with ``ValueError: embedded null byte`` — typically introduced by
|
||||||
|
copy-pasting API keys from terminals or rich-text editors.
|
||||||
|
|
||||||
We delegate to ``hermes_cli.config._sanitize_env_lines`` which
|
We delegate to ``hermes_cli.config._sanitize_env_lines`` which
|
||||||
already knows all valid Hermes env-var names and can split
|
already knows all valid Hermes env-var names and can split
|
||||||
concatenated lines correctly.
|
concatenated lines correctly.
|
||||||
@@ -155,7 +159,11 @@ def _sanitize_env_file_if_needed(path: Path) -> None:
|
|||||||
try:
|
try:
|
||||||
with open(path, **read_kw) as f:
|
with open(path, **read_kw) as f:
|
||||||
original = f.readlines()
|
original = f.readlines()
|
||||||
sanitized = _sanitize_env_lines(original)
|
# Strip null bytes before _sanitize_env_lines so they never
|
||||||
|
# reach python-dotenv (which passes them to os.environ and
|
||||||
|
# crashes with ValueError).
|
||||||
|
stripped = [line.replace("\x00", "") for line in original]
|
||||||
|
sanitized = _sanitize_env_lines(stripped)
|
||||||
if sanitized != original:
|
if sanitized != original:
|
||||||
import tempfile
|
import tempfile
|
||||||
fd, tmp = tempfile.mkstemp(
|
fd, tmp = tempfile.mkstemp(
|
||||||
|
|||||||
@@ -70,6 +70,23 @@ def test_user_env_takes_precedence_over_project_env(tmp_path, monkeypatch):
|
|||||||
assert os.getenv("OPENAI_API_KEY") == "project-key"
|
assert os.getenv("OPENAI_API_KEY") == "project-key"
|
||||||
|
|
||||||
|
|
||||||
|
def test_null_bytes_in_user_env_are_stripped(tmp_path, monkeypatch):
|
||||||
|
home = tmp_path / "hermes"
|
||||||
|
home.mkdir()
|
||||||
|
env_file = home / ".env"
|
||||||
|
# Null bytes can be introduced when copy-pasting API keys.
|
||||||
|
env_file.write_text("GLM_API_KEY=abc\x00\x00\nOPENAI_API_KEY=sk-123\n", encoding="utf-8")
|
||||||
|
|
||||||
|
monkeypatch.delenv("GLM_API_KEY", raising=False)
|
||||||
|
monkeypatch.delenv("OPENAI_API_KEY", raising=False)
|
||||||
|
|
||||||
|
loaded = load_hermes_dotenv(hermes_home=home)
|
||||||
|
|
||||||
|
assert loaded == [env_file]
|
||||||
|
assert os.getenv("GLM_API_KEY") == "abc"
|
||||||
|
assert os.getenv("OPENAI_API_KEY") == "sk-123"
|
||||||
|
|
||||||
|
|
||||||
def test_main_import_applies_user_env_over_shell_values(tmp_path, monkeypatch):
|
def test_main_import_applies_user_env_over_shell_values(tmp_path, monkeypatch):
|
||||||
home = tmp_path / "hermes"
|
home = tmp_path / "hermes"
|
||||||
home.mkdir()
|
home.mkdir()
|
||||||
|
|||||||
Reference in New Issue
Block a user