refactor(scum): declare protected run requests

This commit is contained in:
npc0-hue
2026-07-29 22:37:16 +08:00
parent d7465bfd32
commit 99be8f0f3a
28 changed files with 497 additions and 152 deletions
+53 -6
View File
@@ -420,10 +420,14 @@ func validateGameClientBridgeManifest(field string, bridge domain.GameClientBrid
violations = append(violations, prefix+".profileKey must reference a declared Client Manager profile")
}
}
transports := map[string]domain.RuntimeTransportProfile{}
for _, transport := range runtimeProfiles.TransportProfiles {
transports[transport.Key] = transport
}
commandTypes := map[string]struct{}{}
for index, command := range bridge.Commands {
prefix := fmt.Sprintf("%s.commands[%d]", field, index)
if !clientManagerIdentifierPattern.MatchString(command.Type) || unsafeGameClientBridgeCommandType(command.Type) {
if !clientManagerIdentifierPattern.MatchString(command.Type) || command.ProtectedRequest == nil && unsafeGameClientBridgeCommandType(command.Type) {
violations = append(violations, prefix+".type is invalid or unsafe")
}
if _, exists := commandTypes[command.Type]; exists {
@@ -448,6 +452,7 @@ func validateGameClientBridgeManifest(field string, bridge domain.GameClientBrid
if command.MaxPayloadBytes <= 0 || command.MaxPayloadBytes > maxGameClientBridgePayloadSize {
violations = append(violations, prefix+".maxPayloadBytes is invalid")
}
violations = append(violations, validateGameClientBridgeProtectedRequest(prefix+".protectedRequest", command.ProtectedRequest, transports)...)
}
snapshotTypes := map[string]struct{}{}
for index, snapshot := range bridge.Snapshots {
@@ -468,10 +473,6 @@ func validateGameClientBridgeManifest(field string, bridge domain.GameClientBrid
}
}
queryTemplates := map[string]domain.GameClientBridgeQueryTemplateDeclaration{}
transports := map[string]domain.RuntimeTransportProfile{}
for _, transport := range runtimeProfiles.TransportProfiles {
transports[transport.Key] = transport
}
for index, template := range bridge.QueryTemplates {
prefix := fmt.Sprintf("%s.queryTemplates[%d]", field, index)
if !clientManagerIdentifierPattern.MatchString(template.Key) {
@@ -649,6 +650,50 @@ func unsafeGameClientBridgeCommandType(value string) bool {
return has("shell", "powershell", "script", "terminal", "execute", "exec", "eval") || has("command", "cmd", "process", "system", "os", "executor") && has("run")
}
func validateGameClientBridgeProtectedRequest(prefix string, request *domain.GameClientBridgeProtectedRequestDeclaration, transports map[string]domain.RuntimeTransportProfile) []string {
if request == nil {
return nil
}
var violations []string
if !oneOf(request.Kind, "sql", "rcon", "program") {
violations = append(violations, prefix+".kind is invalid")
}
for field, value := range map[string]string{"transportKey": request.TransportKey, "targetKey": request.TargetKey, "textField": request.TextField} {
if !validDistributionLogicalKey(value) || unsafeGameClientBridgePayloadKey(value) {
violations = append(violations, prefix+"."+field+" is invalid")
}
}
if request.MaxTextBytes < 1 || request.MaxTextBytes > maxGameClientBridgePayloadString {
violations = append(violations, prefix+".maxTextBytes is invalid")
}
transport, exists := transports[request.TransportKey]
if !exists {
return append(violations, prefix+".transportKey must reference a declared runtime transport profile")
}
if transport.TargetKey != request.TargetKey {
violations = append(violations, prefix+".targetKey must match the declared runtime transport profile")
}
wantKind, wantCapability := "", ""
switch request.Kind {
case "sql":
wantCapability = domain.JobCapabilityRemoteRunProtectedSQL
case "rcon":
wantKind, wantCapability = "rcon", domain.JobCapabilityRemoteRunProtectedRCON
case "program":
wantKind, wantCapability = "program", domain.JobCapabilityRemoteRunProgram
}
if request.Kind == "sql" && transport.Kind != "mysql" && transport.Kind != "sqlite" {
violations = append(violations, prefix+".transportKey must use mysql or sqlite for sql requests")
}
if wantKind != "" && transport.Kind != wantKind {
violations = append(violations, prefix+".transportKey does not match protected request kind")
}
if wantCapability != "" && !containsString(transport.Capabilities, wantCapability) {
violations = append(violations, prefix+".transportKey is missing required protected transport capability")
}
return violations
}
func ValidatePluginBridgeAuthorizeRequest(request domain.PluginBridgeAuthorizeRequest) error {
var violations []string
violations = appendRequired(violations, "pluginId", request.PluginID)
@@ -1902,6 +1947,7 @@ func validPluginRunCapability(capability string) bool {
domain.JobCapabilityRemoteRunProcessStart, domain.JobCapabilityRemoteRunProcessStop,
domain.JobCapabilityRemoteRunDBMySQLQuery, domain.JobCapabilityRemoteRunDBSQLiteQuery,
domain.JobCapabilityRemoteRunLogsTransfer, domain.JobCapabilityRemoteRunRCONCommand,
domain.JobCapabilityRemoteRunProtectedSQL, domain.JobCapabilityRemoteRunProtectedRCON, domain.JobCapabilityRemoteRunProgram,
domain.JobCapabilityRunSelfUpdate, domain.JobCapabilityDependenciesCheck, domain.JobCapabilityDependenciesInstall,
domain.JobCapabilityDeploymentPlan, domain.JobCapabilitySCUMDeploymentPlan, domain.JobCapabilityDeploymentShellPosix, domain.JobCapabilityDeploymentShellPowerShell, domain.JobCapabilityDeploymentShellCmd,
domain.JobCapabilityClientManagerDeploy, domain.JobCapabilityClientManagerControl, domain.JobCapabilityClientManagerUpdate,
@@ -1934,7 +1980,8 @@ func remoteCapabilityRequiresInputRef(capability string) bool {
domain.JobCapabilityRemoteRunFilesWrite,
domain.JobCapabilityRemoteRunDBMySQLQuery,
domain.JobCapabilityRemoteRunDBSQLiteQuery,
domain.JobCapabilityRemoteRunRCONCommand:
domain.JobCapabilityRemoteRunRCONCommand, domain.JobCapabilityRemoteRunProtectedSQL,
domain.JobCapabilityRemoteRunProtectedRCON, domain.JobCapabilityRemoteRunProgram:
return true
default:
return false