fix(security): scope workflow run dispatch and MCP run logs to the token's tags

This commit is contained in:
2026-09-09 08:32:32 +00:00
parent 705085d3c7
commit 5fcfb40084
5 changed files with 47 additions and 6 deletions
+14
View File
@@ -314,6 +314,20 @@ func init() {
return nil, fmt.Errorf("server %q is not part of run %q", serverID, runID)
}
// Membership in the run is not scope: a run started before this
// token was restricted, or by an unrestricted credential, names
// servers this token must not read. The stdout of an
// out-of-scope host is exactly the data the tag restriction
// exists to withhold.
//
// The refusal reuses the membership message verbatim so that
// "in the run but out of your scope" and "not in the run at all"
// are indistinguishable — otherwise the difference between the
// two answers enumerates hosts the token cannot see.
if _, err := services.GetServerScoped(c.InstanceID, serverID, c.TokenScope); err != nil {
return nil, fmt.Errorf("server %q is not part of run %q", serverID, runID)
}
limit := defaultLogLimit
if raw, ok := args["limit"].(float64); ok && int(raw) > 0 {
limit = int(raw)
+10 -1
View File
@@ -76,6 +76,15 @@ func init() {
if err != nil {
return nil, fmt.Errorf("this workflow matches no servers")
}
// This all-or-nothing pre-check is no longer the only guard:
// services.TriggerWorkflow now resolves through
// ResolveTargetsScoped itself, so a run started with this token
// can never touch a server outside its scope regardless of what
// happens here. It is kept because its refusal is the clearer
// answer for a model: the service layer would silently run
// against the in-scope subset, while a partially out-of-scope
// workflow is documented here as refused outright, which is
// behaviour a caller relies on.
scopedTargets, err := services.ResolveTargetsScoped(c.InstanceID, wf.TargetServerIDs, wf.TargetTags, c.TokenScope)
if err != nil || len(scopedTargets) != len(allTargets) {
return nil, fmt.Errorf("%w: no servers visible to this token matched the request", ErrOutOfScope)
@@ -85,7 +94,7 @@ func init() {
return nil, err
}
runID, err := services.TriggerWorkflow(c.InstanceID, workflowID, c.TokenName)
runID, err := services.TriggerWorkflow(c.InstanceID, workflowID, c.TokenName, c.TokenScope)
if err != nil {
return nil, fmt.Errorf("could not start the run: %w", err)
}