fix(security): scope package search, run documents, workloads and vulnerability reads to the token's tags
This commit is contained in:
@@ -3,6 +3,7 @@ package mcp
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@@ -403,6 +404,15 @@ func init() {
|
||||
f := services.FindingFilter{
|
||||
Severity: stringArg(args, "severity"),
|
||||
State: stringArg(args, "status"),
|
||||
|
||||
// The service layer drops findings on servers outside this
|
||||
// token's scope, so the affected-host count below is over
|
||||
// visible servers only. An unfiltered count is the same
|
||||
// aggregate leak ListKeys.AssignedCount was fixed for: it
|
||||
// says something exists on a machine the caller must not
|
||||
// know about. A CVE affecting only out-of-scope hosts
|
||||
// disappears from the list rather than showing a zero.
|
||||
TokenScope: c.TokenScope,
|
||||
}
|
||||
findings, err := services.ListInstanceFindings(c.InstanceID, f)
|
||||
if err != nil {
|
||||
@@ -420,12 +430,29 @@ func init() {
|
||||
}
|
||||
}
|
||||
|
||||
// Go randomises map iteration order, so truncating a ranged map
|
||||
// to a page made two identical calls return different CVEs — a
|
||||
// model comparing its own two answers would see the fleet change
|
||||
// under it. Sorting by CVE ID (then package, since the key is a
|
||||
// pair) makes the page deterministic.
|
||||
keys := make([]key, 0, len(meta))
|
||||
for k := range meta {
|
||||
keys = append(keys, k)
|
||||
}
|
||||
sort.Slice(keys, func(i, j int) bool {
|
||||
if keys[i].cve != keys[j].cve {
|
||||
return keys[i].cve < keys[j].cve
|
||||
}
|
||||
return keys[i].pkg < keys[j].pkg
|
||||
})
|
||||
|
||||
limit := pageLimit(args)
|
||||
out := make([]vulnSummary, 0, limit)
|
||||
for k, v := range meta {
|
||||
for _, k := range keys {
|
||||
if len(out) == limit {
|
||||
break
|
||||
}
|
||||
v := meta[k]
|
||||
v.AffectedNum = counts[k]
|
||||
out = append(out, v)
|
||||
}
|
||||
@@ -490,7 +517,7 @@ func init() {
|
||||
"install is returned instead")
|
||||
}
|
||||
|
||||
hits, err := services.SearchPackages(c.InstanceID, name)
|
||||
hits, err := services.SearchPackages(c.InstanceID, name, c.TokenScope)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not search packages: %w", err)
|
||||
}
|
||||
@@ -501,10 +528,11 @@ func init() {
|
||||
if len(out) == limit {
|
||||
break
|
||||
}
|
||||
// A hit's server must be resolved through the token's own
|
||||
// scope: SearchPackages runs unscoped across the instance,
|
||||
// so a server outside the token's tag restriction is
|
||||
// dropped here rather than named to the caller.
|
||||
// SearchPackages now filters by the token's scope itself,
|
||||
// so this resolve is how the hostname is obtained rather
|
||||
// than the only scope check. It stays scoped anyway: this
|
||||
// loop is what turns a server ID into a name the model
|
||||
// sees, and a second check costs nothing.
|
||||
srv, err := services.GetServerScoped(c.InstanceID, h.ServerID, c.TokenScope)
|
||||
if err != nil {
|
||||
continue
|
||||
|
||||
Reference in New Issue
Block a user