fix(controller): hide foreign server IDs from forceUpdateServer response

The previous loop only checked ownership inside the
\`server != nil && server.TaskStream != nil\` branch. A foreign online
server returned permission denied for the whole batch while foreign
offline / unknown IDs silently went into the Offline bucket — the
response-shape delta let a RoleMember enumerate other users' online
server IDs by submitting them in batches.

Drop both foreign and unknown IDs silently into the Offline bucket
(without dispatching the upgrade task) so the response is indistinguishable
across those three states.

Co-authored-by: naiba/CloudCode <hi+cloudcode@nai.ba>
This commit is contained in:
naiba
2026-05-18 15:16:59 +00:00
co-authored by naiba/CloudCode
parent 36297699f5
commit 097d4f7382
2 changed files with 159 additions and 4 deletions
+10 -4
View File
@@ -178,10 +178,16 @@ func forceUpdateServer(c *gin.Context) (*model.ServerTaskResponse, error) {
for _, sid := range forceUpdateServers {
server, _ := singleton.ServerShared.Get(sid)
if server != nil && server.TaskStream != nil {
if !server.HasPermission(c) {
return nil, singleton.Localizer.ErrorT("permission denied")
}
// Per-ID ownership check. Foreign servers (online or offline) and
// unknown IDs MUST be indistinguishable in the response — otherwise the
// response shape leaks server-ID existence/online-state, letting a
// RoleMember enumerate other users' machines. We drop them into the
// Offline bucket without actually dispatching the upgrade task.
if server == nil || !server.HasPermission(c) {
forceUpdateResp.Offline = append(forceUpdateResp.Offline, sid)
continue
}
if server.TaskStream != nil {
if err := server.TaskStream.Send(&pb.Task{
Type: model.TaskTypeUpgrade,
}); err != nil {