fix(plugins): validator accepts the config_schema types the loader and renderer accept

`hermes plugins validate` kept a private `_CONFIG_TYPES` copy that never learned
`secret` (or `object`) when the manifest loader and the Desktop settings renderer
did, so catalog admission (`pinned-source-validate`) went red for any plugin that
declares a `type: secret` setting — the documented way to surface an API key in
the Plugins tab (#120088 web-search-plus 4.3.0, #120492 browserclaw 3.1.0).

Derive the admission set from `plugins_manifest._CONFIG_SCHEMA_TYPES` so the three
cannot drift again; `mapping`/`map`, which only the validator ever accepted (the
loader warned "unknown type" on every load), are no longer admitted.
This commit is contained in:
teknium1
2026-09-23 10:55:04 -07:00
committed by Teknium
parent 8a1272b814
commit 0a2e1732a0
2 changed files with 24 additions and 4 deletions

View File

@@ -24,12 +24,12 @@ from pathlib import Path
from typing import Any, Dict, List, Optional, Tuple
from hermes_cli.plugin_validate_desktop import check_desktop_surface
from hermes_cli.plugins_manifest import _CONFIG_SCHEMA_TYPES
_UPPER_SNAKE_RE = re.compile(r"^[A-Z][A-Z0-9_]*$")
_CONFIG_TYPES = {
"str", "string", "int", "integer", "float", "number",
"bool", "boolean", "list", "array", "dict", "mapping", "map",
}
# Admission accepts exactly the ``config_schema`` types the loader type-checks at load time (and the
# Desktop settings renderer keys its field table on) — a private copy drifted and rejected ``secret``.
_CONFIG_TYPES = frozenset(_CONFIG_SCHEMA_TYPES)
_PROBE_TIMEOUT = 30
_PROBE_SENTINEL = "HERMES_VALIDATE_JSON:"

View File

@@ -87,6 +87,26 @@ def test_requires_hermes_spec_is_validated(tmp_path):
assert any(name == "requires_hermes" and ok for name, ok, _ in report.checks)
def test_config_schema_admits_every_type_the_loader_and_renderer_accept(tmp_path):
"""A ``type:`` the Desktop settings renderer/loader accept (``secret`` + ``env:``, ``object``) must
pass admission — the catalog validator rejecting a documented type blocks pins of plugins that
declare a secret setting."""
from hermes_cli.plugins_manifest import _CONFIG_SCHEMA_TYPES
from hermes_cli.plugins_settings import _FIELD_TYPES
assert set(_FIELD_TYPES) == set(_CONFIG_SCHEMA_TYPES)
schema = {f"k_{t}": {"type": t} for t in _FIELD_TYPES}
schema["api_key"] = {"type": "secret", "env": "FIXTURE_API_KEY", "description": "token"}
d = _make_plugin(tmp_path, manifest=dict(BASE_MANIFEST, config_schema=schema))
report = validate_plugin_dir(d)
assert ("config schema", True, "shape valid") in report.checks, report.failures
bad = validate_plugin_dir(_make_plugin(
tmp_path / "bad", manifest=dict(BASE_MANIFEST, config_schema={"x": {"type": "mapping"}})))
assert [ok for n, ok, _ in bad.checks if n == "config schema"] == [False], bad.checks
def test_admission_runs_the_install_scanner(tmp_path):
"""Admission and install must agree: a tree the installer would hard-block (dangerous) fails
validation; caution findings are surfaced to the reviewer as warnings without failing."""