Register runtime.break_glass_restart in the multi-service operation normalizer and enforce it through the real profile gate. Authorize only the exact trusted prgs-controller profile plus that capability; remove substring controller authority so declared reconciler roles and cleanup_merged_pr_branch semantics are preserved. Route non-dry-run apply through a canonical injectable executor delegate with truthful execution flags. Correct under/over-redaction for credentials while preserving benign sec- text. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
This commit is contained in:
+32
-3
@@ -97,6 +97,26 @@ GITEA_OPERATION_ALIASES = {
|
||||
_REVIEW_MERGE_OPS = frozenset({"gitea.pr.approve", "gitea.pr.merge"})
|
||||
_AUTHOR_ONLY_OPS = frozenset({"gitea.pr.create", "gitea.branch.push"})
|
||||
|
||||
# First-class operation services that may appear in multi-service profile
|
||||
# allowlists. ``runtime.*`` is the control-plane capability namespace used by
|
||||
# non-Gitea MCP tools such as ``runtime.break_glass_restart`` (#664 B13).
|
||||
# Unknown foreign prefixes (e.g. ``jenkins.*`` under service=gitea) still fail
|
||||
# closed — they are not registered here.
|
||||
KNOWN_OPERATION_SERVICES = frozenset({"gitea", "runtime"})
|
||||
|
||||
|
||||
def service_for_operation(op, default="gitea"):
|
||||
"""Return the registered service prefix for a fully-qualified *op*.
|
||||
|
||||
Unqualified names and unknown prefixes fall back to *default* so callers
|
||||
keep the historical Gitea-centric gate behaviour.
|
||||
"""
|
||||
if isinstance(op, str) and "." in op:
|
||||
prefix = op.split(".", 1)[0]
|
||||
if prefix in KNOWN_OPERATION_SERVICES:
|
||||
return prefix
|
||||
return default
|
||||
|
||||
|
||||
def normalize_operation(op, service="gitea"):
|
||||
"""Return the canonical namespaced name for *op*, or fail closed (#106).
|
||||
@@ -133,6 +153,12 @@ def check_operation(op, allowed, forbidden=(), service="gitea"):
|
||||
Reasons: ``allowed``, ``invalid-operation``, ``invalid-forbidden-entry``,
|
||||
``forbidden``, ``no-allowed-operations``, ``not-allowed``.
|
||||
|
||||
Multi-service profile allowlists (#664 B13): each allow/forbid entry is
|
||||
normalized with its own registered service prefix (``gitea.*`` or
|
||||
``runtime.*``) so a gate defaulting to service=gitea can still enforce an
|
||||
exact ``runtime.break_glass_restart`` grant. Unknown / misspelled
|
||||
operations and foreign service prefixes remain fail-closed.
|
||||
|
||||
Fail-closed rules:
|
||||
- an *op* that cannot be normalized is denied (``invalid-operation``)
|
||||
- a forbidden entry that cannot be normalized denies the request
|
||||
@@ -143,14 +169,16 @@ def check_operation(op, allowed, forbidden=(), service="gitea"):
|
||||
- ``forbidden`` always overrides ``allowed``
|
||||
- an empty or missing allowed list denies everything
|
||||
"""
|
||||
op_service = service_for_operation(op, default=service)
|
||||
try:
|
||||
op_n = normalize_operation(op, service)
|
||||
op_n = normalize_operation(op, op_service)
|
||||
except ConfigError:
|
||||
return (False, "invalid-operation")
|
||||
forbidden_n = set()
|
||||
for entry in (forbidden or ()):
|
||||
try:
|
||||
forbidden_n.add(normalize_operation(entry, service))
|
||||
entry_service = service_for_operation(entry, default=service)
|
||||
forbidden_n.add(normalize_operation(entry, entry_service))
|
||||
except ConfigError:
|
||||
return (False, "invalid-forbidden-entry")
|
||||
if op_n in forbidden_n:
|
||||
@@ -160,7 +188,8 @@ def check_operation(op, allowed, forbidden=(), service="gitea"):
|
||||
allowed_n = set()
|
||||
for entry in allowed:
|
||||
try:
|
||||
allowed_n.add(normalize_operation(entry, service))
|
||||
entry_service = service_for_operation(entry, default=service)
|
||||
allowed_n.add(normalize_operation(entry, entry_service))
|
||||
except ConfigError:
|
||||
continue
|
||||
if op_n in allowed_n:
|
||||
|
||||
Reference in New Issue
Block a user