fix(#2622): restore settings allowlist guard (Opus HALT) + validate plugin name
CRITICAL (Opus HALT on prior commit): the PR's edit to save_settings() replaced 'if k in _SETTINGS_ALLOWED_KEYS' with 'if k=="dashboard_plugins": continue' and orphaned the whole validation body under the continue. Effects: (a) settings save broken for every key except dashboard_plugins; (b) the allowlist security boundary gone -> any client key (password_hash, signing_key_*) became settable. Restored the guard + correct indentation; dashboard_plugins handled by the deep-merge above. Verified in-process: language persists, password_hash/signing_key injection rejected, dashboard_plugins still deep-merges. Also (Opus SHOULD-FIX #3): validate plugin name against ^[a-z][a-z0-9_-]{0,63}$ so a manifest name like '../foo' can't make the URL-space ambiguous. Regression tests added for both (the allowlist bug had ZERO coverage).
This commit is contained in:
@@ -15,10 +15,15 @@ Each plugin may have:
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
import re
|
||||
from pathlib import Path
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# Valid dashboard-plugin name: a safe slug (it becomes a URL path component and
|
||||
# a settings key). Lowercase alnum + - / _, 1-64 chars, must start with a letter.
|
||||
_VALID_PLUGIN_NAME = re.compile(r"^[a-z][a-z0-9_-]{0,63}$")
|
||||
|
||||
# plugin_name -> manifest dict (as loaded from manifest.json)
|
||||
PLUGIN_MANIFESTS: dict[str, dict] = {}
|
||||
|
||||
@@ -52,6 +57,13 @@ def load_plugins() -> None:
|
||||
|
||||
name = manifest.get("name") or entry.name
|
||||
|
||||
# Validate the plugin name: it becomes a URL path component
|
||||
# (/dashboard-plugins/<name>/...) and a settings key. Restrict to a safe
|
||||
# slug so a manifest like name:"../foo" can't make the URL-space ambiguous.
|
||||
if not _VALID_PLUGIN_NAME.match(str(name)):
|
||||
logger.warning("Skipping plugin with invalid name %r (must match %s)", name, _VALID_PLUGIN_NAME.pattern)
|
||||
continue
|
||||
|
||||
tab = manifest.get("tab", {})
|
||||
tab_path = tab.get("path", f"/{name}")
|
||||
|
||||
|
||||
Reference in New Issue
Block a user