Remove pre-1.0 audit and protected request scaffolding
This commit is contained in:
+16
-133
@@ -10,7 +10,7 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
maxAuditSummaryLength = 512
|
||||
maxSummaryLength = 512
|
||||
maxContactNoteLength = 160
|
||||
maxMarketplaceKeywordSize = 80
|
||||
maxMarketplaceListSize = 500
|
||||
@@ -500,7 +500,7 @@ func validateGameClientBridgeManifest(field string, bridge domain.GameClientBrid
|
||||
commandTypes := map[string]struct{}{}
|
||||
for index, command := range bridge.Commands {
|
||||
prefix := fmt.Sprintf("%s.commands[%d]", field, index)
|
||||
if !clientManagerIdentifierPattern.MatchString(command.Type) || command.ProtectedRequest == nil && unsafeGameClientBridgeCommandType(command.Type) {
|
||||
if !clientManagerIdentifierPattern.MatchString(command.Type) || unsafeGameClientBridgeCommandType(command.Type) {
|
||||
violations = append(violations, prefix+".type is invalid or unsafe")
|
||||
}
|
||||
if _, exists := commandTypes[command.Type]; exists {
|
||||
@@ -525,7 +525,6 @@ 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 {
|
||||
@@ -625,7 +624,7 @@ func validateGameClientBridgeManifest(field string, bridge domain.GameClientBrid
|
||||
violations = append(violations, prefix+".key is duplicated")
|
||||
}
|
||||
logProjectionKeys[projection.Key] = struct{}{}
|
||||
violations = append(violations, validateGameClientBridgeLogProjection(prefix, projection, bridge.Commands, runtimeProfiles.ClientManagers)...)
|
||||
violations = append(violations, validateGameClientBridgeLogProjection(prefix, projection)...)
|
||||
}
|
||||
dataPackKeys := map[string]struct{}{}
|
||||
for index, dataPack := range bridge.DataPacks {
|
||||
@@ -640,9 +639,9 @@ func validateGameClientBridgeManifest(field string, bridge domain.GameClientBrid
|
||||
if dataPack.DatabaseUserVersion < 1 || len(dataPack.LogParserRefs) == 0 || len(dataPack.ConfigMapRefs) == 0 {
|
||||
violations = append(violations, prefix+" must declare a database version and parser/config assets")
|
||||
}
|
||||
refs := append(domain.CopyStringSlice(dataPack.LogParserRefs), dataPack.ConfigMapRefs...)
|
||||
refs = append(refs, dataPack.DataRefs...)
|
||||
for _, ref := range refs {
|
||||
refs := append(domain.CopyStringSlice(dataPack.LogParserRefs), dataPack.ConfigMapRefs...)
|
||||
refs = append(refs, dataPack.DataRefs...)
|
||||
for _, ref := range refs {
|
||||
if !safeRelativeJSONRef(ref) {
|
||||
violations = append(violations, prefix+" asset reference is invalid")
|
||||
}
|
||||
@@ -664,8 +663,8 @@ func validateGameClientBridgeManifest(field string, bridge domain.GameClientBrid
|
||||
if !containsString(permissions, template.Permission) {
|
||||
violations = append(violations, prefix+".permission must be declared by the plugin")
|
||||
}
|
||||
if template.ApprovalLevel != domain.GameClientBridgeApprovalLevelOperator && template.ApprovalLevel != domain.GameClientBridgeApprovalLevelPlatformAdmin {
|
||||
violations = append(violations, prefix+".approvalLevel must require operator or platform-admin approval")
|
||||
if template.ApprovalLevel != domain.GameClientBridgeApprovalLevelNone && template.ApprovalLevel != domain.GameClientBridgeApprovalLevelOperator && template.ApprovalLevel != domain.GameClientBridgeApprovalLevelPlatformAdmin {
|
||||
violations = append(violations, prefix+".approvalLevel is invalid")
|
||||
}
|
||||
if template.Kind != domain.GameClientBridgeOperationKindRCON && template.Kind != domain.GameClientBridgeOperationKindSQLiteMutation {
|
||||
violations = append(violations, prefix+".kind is invalid")
|
||||
@@ -689,8 +688,8 @@ func validateGameClientBridgeManifest(field string, bridge domain.GameClientBrid
|
||||
}
|
||||
switch template.Kind {
|
||||
case domain.GameClientBridgeOperationKindRCON:
|
||||
if transport.Kind != "rcon" || !containsString(transport.Capabilities, domain.JobCapabilityRemoteRunProtectedRCON) {
|
||||
violations = append(violations, prefix+" transport must be rcon with remote.run.protected.rcon capability")
|
||||
if transport.Kind != "rcon" || !containsString(transport.Capabilities, domain.JobCapabilityRemoteRunRCONCommand) {
|
||||
violations = append(violations, prefix+" transport must be rcon with remote.run.rcon.command capability")
|
||||
}
|
||||
if template.MaxRowsAffected != 0 {
|
||||
violations = append(violations, prefix+".maxRowsAffected is only valid for sqlite-mutation")
|
||||
@@ -822,7 +821,7 @@ func validateGameClientBridgeManifest(field string, bridge domain.GameClientBrid
|
||||
return violations
|
||||
}
|
||||
|
||||
func validateGameClientBridgeLogProjection(prefix string, projection domain.GameClientBridgeLogProjectionDeclaration, commands []domain.GameClientBridgeCommandDeclaration, clientManagers []domain.RuntimeClientManagerProfile) []string {
|
||||
func validateGameClientBridgeLogProjection(prefix string, projection domain.GameClientBridgeLogProjectionDeclaration) []string {
|
||||
var violations []string
|
||||
if len(projection.StreamKeys) < 1 || len(projection.StreamKeys) > 64 {
|
||||
violations = append(violations, prefix+".streamKeys must contain between 1 and 64 streams")
|
||||
@@ -887,43 +886,6 @@ func validateGameClientBridgeLogProjection(prefix string, projection domain.Game
|
||||
if presence.ActivityTarget != nil {
|
||||
violations = append(violations, validateGameClientBridgeLogProjectionTarget(prefix+".presence.activityTarget", *presence.ActivityTarget, captures)...)
|
||||
}
|
||||
|
||||
announcement := presence.Announcement
|
||||
if !clientManagerIdentifierPattern.MatchString(announcement.ProfileKey) {
|
||||
violations = append(violations, prefix+".presence.announcement.profileKey is invalid")
|
||||
} else {
|
||||
profileFound := false
|
||||
for _, profile := range clientManagers {
|
||||
if profile.Key == announcement.ProfileKey && containsString(profile.Health.RequiredCapabilities, "game-client.bridge") {
|
||||
profileFound = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !profileFound {
|
||||
violations = append(violations, prefix+".presence.announcement.profileKey must reference a declared game-client bridge profile")
|
||||
}
|
||||
}
|
||||
var command *domain.GameClientBridgeCommandDeclaration
|
||||
for index := range commands {
|
||||
if commands[index].Type == announcement.CommandType {
|
||||
command = &commands[index]
|
||||
break
|
||||
}
|
||||
}
|
||||
if command == nil {
|
||||
violations = append(violations, prefix+".presence.announcement.commandType must reference a declared command")
|
||||
}
|
||||
if !gameClientBridgeFieldPattern.MatchString(announcement.TextField) {
|
||||
violations = append(violations, prefix+".presence.announcement.textField is invalid")
|
||||
} else if command != nil && command.ProtectedRequest != nil && command.ProtectedRequest.TextField != announcement.TextField {
|
||||
violations = append(violations, prefix+".presence.announcement.textField must match the command protected request")
|
||||
}
|
||||
if strings.TrimSpace(announcement.NewTextTemplate) == "" || len([]rune(announcement.NewTextTemplate)) > 4096 {
|
||||
violations = append(violations, prefix+".presence.announcement.newTextTemplate is empty or too large")
|
||||
}
|
||||
if strings.TrimSpace(announcement.ReturningTextTemplate) == "" || len([]rune(announcement.ReturningTextTemplate)) > 4096 {
|
||||
violations = append(violations, prefix+".presence.announcement.returningTextTemplate is empty or too large")
|
||||
}
|
||||
return violations
|
||||
}
|
||||
|
||||
@@ -1032,50 +994,6 @@ 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 emptyGameClientBridgeOperationMutation(value domain.GameClientBridgeOperationMutationDeclaration) bool {
|
||||
return value.FieldKey == "" && value.TableKey == "" && value.IdentityKey == "" && value.ValueKey == "" && value.ConfirmationQueryKey == "" && value.AllowedValueType == "" && value.MinValue == 0 && value.MaxValue == 0
|
||||
}
|
||||
@@ -1744,12 +1662,7 @@ func ValidateJob(job domain.Job) error {
|
||||
if job.ExecutionInput.SourceRCON != nil {
|
||||
violations = append(violations, validateRuntimeSourceRCONPlan("executionInput.sourceRcon", job.ExecutionInput.SourceRCON)...)
|
||||
isSourceCommand := job.Capability == domain.JobCapabilityRemoteRunRCONCommand
|
||||
isProtectedRCON := job.Capability == domain.JobCapabilityRemoteRunProtectedRCON
|
||||
wantAdapterKind := "rcon"
|
||||
if isProtectedRCON {
|
||||
wantAdapterKind = "protected-rcon"
|
||||
}
|
||||
if (!isSourceCommand && !isProtectedRCON) || job.ExecutionInput.RemoteAdapterKind != wantAdapterKind {
|
||||
if !isSourceCommand || job.ExecutionInput.RemoteAdapterKind != "rcon" {
|
||||
violations = append(violations, "executionInput.sourceRcon is allowed only for rcon jobs")
|
||||
}
|
||||
if job.RetryPolicy.MaxAttempts != 1 {
|
||||
@@ -1766,8 +1679,8 @@ func ValidateJob(job domain.Job) error {
|
||||
if len([]byte(job.ExecutionResult.Content)) > maxJobExecutionContentSize {
|
||||
violations = append(violations, "executionResult.content is too large")
|
||||
}
|
||||
if len(job.ExecutionResult.AuditSummary) > maxAuditSummaryLength {
|
||||
violations = append(violations, "executionResult.auditSummary is too long")
|
||||
if len(job.ExecutionResult.Summary) > maxSummaryLength {
|
||||
violations = append(violations, "executionResult.summary is too long")
|
||||
}
|
||||
if job.Capability == domain.JobCapabilityConfigWrite || job.Capability == domain.JobCapabilityFilesRead || job.Capability == domain.JobCapabilityFilesWrite {
|
||||
if job.ServerInstanceID == "" {
|
||||
@@ -1875,26 +1788,6 @@ func ValidateLogStream(stream domain.LogStream) error {
|
||||
return finish(violations)
|
||||
}
|
||||
|
||||
func ValidateAuditEvent(event domain.AuditEvent) error {
|
||||
var violations []string
|
||||
violations = appendRequired(violations, "id", event.ID)
|
||||
violations = appendRequired(violations, "actorId", event.ActorID)
|
||||
violations = appendRequired(violations, "action", event.Action)
|
||||
violations = appendRequired(violations, "resourceKind", event.ResourceKind)
|
||||
violations = appendRequired(violations, "resourceId", event.ResourceID)
|
||||
violations = appendRequired(violations, "summary", event.Summary)
|
||||
if !validAuditResult(event.Result) {
|
||||
violations = append(violations, "result is invalid")
|
||||
}
|
||||
if len(event.Summary) > maxAuditSummaryLength {
|
||||
violations = append(violations, "summary is too long")
|
||||
}
|
||||
if looksLikeRawSecret(event.Summary) {
|
||||
violations = append(violations, "summary must be redacted")
|
||||
}
|
||||
return finish(violations)
|
||||
}
|
||||
|
||||
func MissingCapabilities(actual []string, required []string) []string {
|
||||
actualSet := make(map[string]struct{}, len(actual))
|
||||
for _, capability := range actual {
|
||||
@@ -2446,7 +2339,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.JobCapabilityRemoteRunProtectedSQL, domain.JobCapabilityRemoteRunProgram,
|
||||
domain.JobCapabilityRunSelfUpdate, domain.JobCapabilityDependenciesCheck, domain.JobCapabilityDependenciesInstall,
|
||||
domain.JobCapabilityDeploymentPlan, domain.JobCapabilityDeploymentShellPosix, domain.JobCapabilityDeploymentShellPowerShell, domain.JobCapabilityDeploymentShellCmd,
|
||||
domain.JobCapabilityClientManagerDeploy, domain.JobCapabilityClientManagerControl, domain.JobCapabilityClientManagerUpdate,
|
||||
@@ -2479,8 +2372,7 @@ func remoteCapabilityRequiresInputRef(capability string) bool {
|
||||
domain.JobCapabilityRemoteRunFilesWrite,
|
||||
domain.JobCapabilityRemoteRunDBMySQLQuery,
|
||||
domain.JobCapabilityRemoteRunDBSQLiteQuery,
|
||||
domain.JobCapabilityRemoteRunRCONCommand, domain.JobCapabilityRemoteRunProtectedSQL,
|
||||
domain.JobCapabilityRemoteRunProtectedRCON, domain.JobCapabilityRemoteRunProgram:
|
||||
domain.JobCapabilityRemoteRunRCONCommand, domain.JobCapabilityRemoteRunProtectedSQL, domain.JobCapabilityRemoteRunProgram:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
@@ -2781,12 +2673,3 @@ func validLogStorageBackend(backend domain.LogStorageBackend) bool {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func validAuditResult(result domain.AuditResult) bool {
|
||||
switch result {
|
||||
case domain.AuditResultSuccess, domain.AuditResultDenied, domain.AuditResultFailed, domain.AuditResultQueued:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user