fix(approval): recover legacy list values without character grants
Recover legacy stringified lists with a warning. Reject malformed shapes and nonstring members without admitting approvals or rewriting user config on read. Fixes #104779 Co-authored-by: liuhao1024 <sunsky.lau@gmail.com>
This commit is contained in:
@@ -0,0 +1,21 @@
|
||||
import yaml
|
||||
from tools import approval
|
||||
|
||||
|
||||
def test_legacy_string_allowlist_recovers_only_string_lists(tmp_path, monkeypatch, caplog):
|
||||
monkeypatch.setenv("HERMES_HOME", str(tmp_path))
|
||||
description = "script execution via -e/-c flag"
|
||||
for value in ([description], yaml.safe_dump([description])):
|
||||
(tmp_path / "config.yaml").write_text(yaml.safe_dump({"command_allowlist": value}))
|
||||
assert approval.load_permanent_allowlist() == {description}
|
||||
assert approval.is_approved("probe", description)
|
||||
assert "command_allowlist" in caplog.text
|
||||
|
||||
|
||||
def test_malformed_allowlist_does_not_grant_approval(tmp_path, monkeypatch, caplog):
|
||||
monkeypatch.setenv("HERMES_HOME", str(tmp_path))
|
||||
for value in ("plain text", "[bad", {"not": "a list"}, [True, "candidate"], "[true, candidate]", 42):
|
||||
(tmp_path / "config.yaml").write_text(yaml.safe_dump({"command_allowlist": value}))
|
||||
assert approval.load_permanent_allowlist() == set()
|
||||
assert not approval.is_approved("probe", "candidate")
|
||||
assert "command_allowlist" in caplog.text
|
||||
+17
-1
@@ -330,7 +330,23 @@ def load_permanent_allowlist() -> set:
|
||||
try:
|
||||
from hermes_cli.config import load_config_readonly
|
||||
config = load_config_readonly()
|
||||
patterns = set(config.get("command_allowlist", []) or [])
|
||||
raw = config.get("command_allowlist")
|
||||
legacy = isinstance(raw, str)
|
||||
if legacy:
|
||||
# Old config-set versions serialized list values as scalar strings.
|
||||
import yaml
|
||||
try:
|
||||
raw = yaml.safe_load(raw)
|
||||
except yaml.YAMLError:
|
||||
raw = False
|
||||
if raw is None and not legacy:
|
||||
raw = []
|
||||
if not isinstance(raw, list) or any(not isinstance(item, str) for item in raw):
|
||||
logger.warning("Ignoring malformed command_allowlist; configure a list of strings.")
|
||||
return set()
|
||||
if legacy:
|
||||
logger.warning("Recovered legacy string command_allowlist; re-save it as a list of strings.")
|
||||
patterns = set(raw)
|
||||
if patterns:
|
||||
load_permanent(patterns)
|
||||
return patterns
|
||||
|
||||
@@ -262,6 +262,12 @@ command_allowlist:
|
||||
|
||||
These patterns are loaded at startup and silently approved in all future sessions.
|
||||
|
||||
The setting must be a list of strings. Legacy installs that stored a list as a
|
||||
quoted YAML/JSON string recover that list at load time and log a warning to
|
||||
re-save it with `hermes config edit`. Other malformed values are ignored with
|
||||
a warning; they never become per-character approvals. Loading does not rewrite
|
||||
your configuration file.
|
||||
|
||||
:::tip
|
||||
Use `hermes config edit` to review or remove patterns from your permanent allowlist.
|
||||
:::
|
||||
|
||||
Reference in New Issue
Block a user