Merge the wilm work, act on the five-agent review, verify the launch-path policy on Windows
This commit is contained in:
@@ -185,6 +185,36 @@ def requires_target(action):
|
||||
return action_targetkind(action) is not None
|
||||
|
||||
|
||||
# Monitor attachment as a policy action. A monitor definition is bound to a PC
|
||||
# type by carrying a ConfigurationPolicyAction whose name is 'monitor:<id>'.
|
||||
# Distinct names let one policy attach several monitors and fold naturally
|
||||
# (first-match-wins per name), and they are validated in the policy layer but
|
||||
# never enqueued as commands because ALLOWED_ACTIONS_FOR intersects with
|
||||
# COMMAND_ALLOWLIST, which carries no monitor: entries.
|
||||
MONITOR_ACTION_PREFIX = 'monitor:'
|
||||
|
||||
|
||||
def monitor_action_name(endpointmonitorid):
|
||||
"""Build the policy action name that attaches a monitor definition."""
|
||||
return f'{MONITOR_ACTION_PREFIX}{endpointmonitorid}'
|
||||
|
||||
|
||||
def is_monitor_action(action):
|
||||
"""True when action names a monitor attachment (monitor:<id>)."""
|
||||
return isinstance(action, str) and action.startswith(MONITOR_ACTION_PREFIX)
|
||||
|
||||
|
||||
def monitor_id_from_action(action):
|
||||
"""The monitor id an action attaches, or None when it is not a monitor action."""
|
||||
if not is_monitor_action(action):
|
||||
return None
|
||||
raw = action[len(MONITOR_ACTION_PREFIX):]
|
||||
try:
|
||||
return int(raw)
|
||||
except (TypeError, ValueError):
|
||||
return None
|
||||
|
||||
|
||||
def ALLOWED_ACTIONS_FOR(pctype=None, assetid=None):
|
||||
"""The action names an endpoint may run.
|
||||
|
||||
@@ -211,6 +241,44 @@ def ALLOWED_ACTIONS_FOR(pctype=None, assetid=None):
|
||||
# behavior). This is intentionally permissive; deny-by-default would be
|
||||
# a separate, explicit setting.
|
||||
return base
|
||||
# Attaching a monitor expresses intent to watch a process/service AND to
|
||||
# control it: the panel renders that row's Start/Stop/Status buttons, so the
|
||||
# gate must permit the matching generic primitives or every click 409s
|
||||
# "not permitted by policy". Expand each monitor:<id> to its kind's control
|
||||
# actions before intersecting.
|
||||
allowed = allowed | _monitor_control_actions(allowed)
|
||||
# Never widen past the code-level allowlist: policy can only gate within it.
|
||||
return base & allowed
|
||||
|
||||
|
||||
def _monitor_control_actions(action_names):
|
||||
"""Control primitives granted by the monitor: attachments in a policy.
|
||||
|
||||
A process monitor grants process_start/stop/status; a service monitor grants
|
||||
service_start/stop/status. The monitor's kind is read from the catalog, so a
|
||||
definition's kind is the single source of truth for what its row can do.
|
||||
"""
|
||||
ids = set()
|
||||
for name in action_names:
|
||||
if isinstance(name, str) and name.startswith(MONITOR_ACTION_PREFIX):
|
||||
monitor_id = monitor_id_from_action(name)
|
||||
if monitor_id is not None:
|
||||
ids.add(monitor_id)
|
||||
if not ids:
|
||||
return set()
|
||||
|
||||
try:
|
||||
from .models import EndpointMonitor
|
||||
except Exception:
|
||||
return set()
|
||||
|
||||
granted = set()
|
||||
for monitor in EndpointMonitor.query.filter(
|
||||
EndpointMonitor.endpointmonitorid.in_(ids)).all():
|
||||
kind = (monitor.kind or '').lower()
|
||||
if kind == 'process':
|
||||
granted |= {'process_start', 'process_stop', 'process_status'}
|
||||
elif kind == 'service':
|
||||
granted |= {'service_start', 'service_stop', 'service_status'}
|
||||
return granted
|
||||
|
||||
|
||||
Reference in New Issue
Block a user