fix: scope monitor runner and workflow targets to the caller's fleet
GET /api/monitors and GET /api/monitors/:id returned Monitor.Runner unfiltered; for an agent-pushed monitor that field is literally a server ID, so a restricted token learned which out-of-scope server a monitor runs on directly, not merely that one exists. services.RedactMonitorRunner replaces Runner with models.RunnerRestricted when it names a server outside the caller's scope, resolved once via the new services.VisibleServerIDs rather than per monitor. The monitor itself is still returned — a restricted operator may legitimately need to see that it exists and is up or down — only the runner field goes neutral; omitting the monitor entirely was considered and rejected as more surprising than one field changing. Runner "server" (control-plane-run) is never touched. The MCP list_monitors/get_monitor_status projections never had a Runner field to begin with, so REST and the tool surface already agreed; a comment now records why. GET /api/workflows and GET /api/workflows/:id returned Workflow.TargetServerIDs unfiltered — directly naming out-of-scope servers, worse than a count. services.FilterVisibleServerIDs narrows the list to what VisibleServerIDs admits and reports hidden (no count) when at least one target was dropped; WorkflowResponse wraps *models.Workflow with a scoped TargetServerIDs and a TargetsRestricted flag. TargetTags is left untouched — the tag vocabulary is already ruled acceptable to expose. The MCP list_workflows/get_workflow tools get the identical treatment: list_workflows' target count is now based on the filtered ID list, and get_workflow's workflowDetail carries the same TargetsRestricted flag, so a model that sees a filtered target list and then has run_workflow refuse the same workflow for out-of-scope targets is not left concluding the refusal invented a problem the list never mentioned. All four routes recorded in serverScopedRoutes as true; none is boot-enforced, for the same substring-filter reason as the key routes added in the previous round.
This commit is contained in:
@@ -21,6 +21,14 @@ const (
|
||||
|
||||
const RunnerServer = "server"
|
||||
|
||||
// RunnerRestricted replaces a monitor's runner in an API response when the
|
||||
// real value is a server ID the acting token's scope does not admit. The
|
||||
// monitor itself is still returned — a restricted operator may legitimately
|
||||
// need to see its name and state — only where it runs is hidden, the same
|
||||
// way a workflow's target list can omit an ID without the whole workflow
|
||||
// disappearing from a list.
|
||||
const RunnerRestricted = "restricted"
|
||||
|
||||
type MonitorTarget struct {
|
||||
URL string `bson:"url,omitempty" json:"url,omitempty"`
|
||||
Host string `bson:"host,omitempty" json:"host,omitempty"`
|
||||
|
||||
Reference in New Issue
Block a user