diff --git a/hermes_cli/web_server.py b/hermes_cli/web_server.py index 8c33a383e5f..2c4e6ecbae6 100644 --- a/hermes_cli/web_server.py +++ b/hermes_cli/web_server.py @@ -53,7 +53,7 @@ try: from fastapi.middleware.cors import CORSMiddleware from fastapi.responses import FileResponse, HTMLResponse, JSONResponse from fastapi.staticfiles import StaticFiles - from pydantic import BaseModel + from pydantic import BaseModel, field_validator except ImportError: raise SystemExit( "Web UI requires fastapi and uvicorn.\n" @@ -425,6 +425,20 @@ class EnvVarUpdate(BaseModel): key: str value: str + @field_validator("key") + @classmethod + def key_must_be_nonempty(cls, v: str) -> str: + if not v.strip(): + raise ValueError("key must not be empty") + return v + + @field_validator("value") + @classmethod + def value_must_be_nonempty(cls, v: str) -> str: + if not v.strip(): + raise ValueError("value must not be empty; use DELETE /api/env to remove a key") + return v + class EnvVarDelete(BaseModel): key: str diff --git a/tests/hermes_cli/test_web_server.py b/tests/hermes_cli/test_web_server.py index e83f5bdeb30..9a19580a915 100644 --- a/tests/hermes_cli/test_web_server.py +++ b/tests/hermes_cli/test_web_server.py @@ -1924,4 +1924,34 @@ class TestPtyWebSocket: f"/api/events?token={self.token}" ): pass - assert exc.value.code == 4400 + + +class TestEnvVarUpdateValidation: + """PUT /api/env must reject empty values to prevent .env key destruction.""" + + def test_rejects_empty_value(self): + from hermes_cli.web_server import EnvVarUpdate + import pydantic + + with pytest.raises(pydantic.ValidationError): + EnvVarUpdate(key="SOME_KEY", value="") + + def test_rejects_whitespace_only_value(self): + from hermes_cli.web_server import EnvVarUpdate + import pydantic + + with pytest.raises(pydantic.ValidationError): + EnvVarUpdate(key="SOME_KEY", value=" ") + + def test_accepts_nonempty_value(self): + from hermes_cli.web_server import EnvVarUpdate + + update = EnvVarUpdate(key="SOME_KEY", value="sk-abc123") + assert update.value == "sk-abc123" + + def test_rejects_empty_key(self): + from hermes_cli.web_server import EnvVarUpdate + import pydantic + + with pytest.raises(pydantic.ValidationError): + EnvVarUpdate(key="", value="some-value")