Files
browser/platform/validator/production_ops.go
T
2026-07-20 16:42:33 +08:00

251 lines
10 KiB
Go

package validator
import (
"fmt"
"strings"
"browser.local/platform/domain"
)
func ValidateCapacityAdmissionRequest(request domain.CapacityAdmissionRequest) error {
var violations []string
violations = appendRequired(violations, "capability", request.Capability)
if request.ServerInstanceID != "" && !safeIdentifier(request.ServerInstanceID) {
violations = append(violations, "serverInstanceId is invalid")
}
if request.RunEndpointID != "" && !safeIdentifier(request.RunEndpointID) {
violations = append(violations, "runEndpointId is invalid")
}
if request.TargetKey != "" && !validLogicalFileKey(request.TargetKey) {
violations = append(violations, "targetKey is invalid")
}
if unsafeProductionText(request.Capability) || unsafeProductionText(request.IdempotencyKey) {
violations = append(violations, "capacity request contains unsafe content")
}
return finish(violations)
}
func ValidateCapacityAdmissionDecision(decision domain.CapacityAdmissionDecision) error {
var violations []string
if !validCapacityAdmissionState(decision.State) {
violations = append(violations, "state is invalid")
}
violations = appendRequired(violations, "reason", decision.Reason)
if len(decision.Reason) > maxProductionMessageLength || unsafeProductionText(decision.Reason) {
violations = append(violations, "reason is unsafe")
}
for i, code := range decision.PressureCodes {
if !validCapacityPressureCode(code) {
violations = append(violations, fmt.Sprintf("pressureCodes[%d] is invalid", i))
}
}
if decision.RunningJobs < 0 || decision.QueuedJobs < 0 || decision.MaxJobs < 0 {
violations = append(violations, "capacity counts must not be negative")
}
return finish(violations)
}
func ValidateAlertRecord(alert domain.AlertRecord) error {
var violations []string
violations = appendRequired(violations, "id", alert.ID)
violations = appendRequired(violations, "sourceKind", alert.SourceKind)
violations = appendRequired(violations, "sourceId", alert.SourceID)
violations = appendRequired(violations, "ruleKey", alert.RuleKey)
violations = appendRequired(violations, "title", alert.Title)
violations = appendRequired(violations, "message", alert.Message)
if !validAlertSeverity(alert.Severity) {
violations = append(violations, "severity is invalid")
}
if !validAlertState(alert.State) {
violations = append(violations, "state is invalid")
}
if alert.OccurrenceCount <= 0 {
violations = append(violations, "occurrenceCount must be positive")
}
for _, value := range []fieldString{{field: "title", value: alert.Title}, {field: "message", value: alert.Message}, {field: "resolutionNote", value: alert.ResolutionNote}} {
if len(value.value) > maxProductionMessageLength || unsafeProductionText(value.value) {
violations = append(violations, value.field+" is unsafe")
}
}
return finish(violations)
}
func ValidateAlertAcknowledgeRequest(request domain.AlertAcknowledgeRequest) error {
return validateAlertNoteRequest(request.AlertID, request.Note)
}
func ValidateAlertResolveRequest(request domain.AlertResolveRequest) error {
return validateAlertNoteRequest(request.AlertID, request.Note)
}
func ValidateAlertRetryRequest(request domain.AlertRetryRequest) error {
var violations []string
violations = appendRequired(violations, "alertId", request.AlertID)
violations = appendRequired(violations, "idempotencyKey", request.IdempotencyKey)
if unsafeProductionText(request.AlertID) || unsafeProductionText(request.IdempotencyKey) {
violations = append(violations, "alert retry request is unsafe")
}
return finish(violations)
}
func ValidatePluginLifecycleInstallation(installation domain.PluginLifecycleInstallation) error {
var violations []string
violations = appendRequired(violations, "id", installation.ID)
violations = appendRequired(violations, "pluginId", installation.PluginID)
violations = appendRequired(violations, "serverInstanceId", installation.ServerInstanceID)
if !validPluginLifecycleState(installation.DesiredState) {
violations = append(violations, "desiredState is invalid")
}
if !validPluginLifecycleState(installation.CurrentState) {
violations = append(violations, "currentState is invalid")
}
if installation.LastOperation != "" && !validPluginLifecycleOperation(installation.LastOperation) {
violations = append(violations, "lastOperation is invalid")
}
for _, value := range []fieldString{{field: "compatibility", value: installation.Compatibility}, {field: "failureReason", value: installation.FailureReason}} {
if len(value.value) > maxProductionMessageLength || unsafeProductionText(value.value) {
violations = append(violations, value.field+" is unsafe")
}
}
return finish(violations)
}
func ValidatePluginLifecycleRequest(request domain.PluginLifecycleRequest) error {
var violations []string
violations = appendRequired(violations, "pluginId", request.PluginID)
violations = appendRequired(violations, "serverInstanceId", request.ServerInstanceID)
violations = appendRequired(violations, "idempotencyKey", request.IdempotencyKey)
if !validPluginLifecycleOperation(request.Operation) {
violations = append(violations, "operation is invalid")
}
if (request.Operation == domain.PluginLifecycleOperationDisable || request.Operation == domain.PluginLifecycleOperationRollback || request.Operation == domain.PluginLifecycleOperationRetire) && !request.Confirmed {
violations = append(violations, "confirmed is required for disruptive plugin lifecycle operation")
}
for _, value := range []fieldString{{field: "targetVersion", value: request.TargetVersion}, {field: "idempotencyKey", value: request.IdempotencyKey}} {
if unsafeProductionText(value.value) {
violations = append(violations, value.field+" is unsafe")
}
}
return finish(violations)
}
func ValidateAIConfigDiffPreview(preview domain.AIConfigDiffPreview) error {
var violations []string
violations = appendRequired(violations, "id", preview.ID)
violations = appendRequired(violations, "requestId", preview.RequestID)
violations = appendRequired(violations, "createdBy", preview.CreatedBy)
violations = appendRequired(violations, "serverInstanceId", preview.ServerInstanceID)
violations = appendRequired(violations, "key", preview.Key)
violations = appendRequired(violations, "diffSummary", preview.DiffSummary)
if preview.ConfigVersion <= 0 {
violations = append(violations, "configVersion must be positive")
}
if !validAIConfigDiffState(preview.State) {
violations = append(violations, "state is invalid")
}
if !validLogicalFileKey(preview.Key) || !validConfigFileKey(preview.Key) {
violations = append(violations, "key is invalid")
}
if len([]byte(preview.ProposedConfig)) > maxServerConfigContentSize || containsUnsafeRuntimeSecret(preview.ProposedConfig) || looksLikeRawHostPath(preview.ProposedConfig) {
violations = append(violations, "proposedConfig is unsafe")
}
if len(preview.DiffSummary) > maxProductionMessageLength || unsafeProductionText(preview.DiffSummary) {
violations = append(violations, "diffSummary is unsafe")
}
return finish(violations)
}
func ValidateAIConfigDiffApprovalRequest(request domain.AIConfigDiffApprovalRequest) error {
var violations []string
violations = appendRequired(violations, "diffId", request.DiffID)
violations = appendRequired(violations, "idempotencyKey", request.IdempotencyKey)
if unsafeProductionText(request.DiffID) || unsafeProductionText(request.IdempotencyKey) {
violations = append(violations, "AI config approval request is unsafe")
}
return finish(violations)
}
func validateAlertNoteRequest(alertID string, note string) error {
var violations []string
violations = appendRequired(violations, "alertId", alertID)
if unsafeProductionText(alertID) || len(note) > maxProductionMessageLength || unsafeProductionText(note) {
violations = append(violations, "alert note request is unsafe")
}
return finish(violations)
}
func validCapacityAdmissionState(state domain.CapacityAdmissionState) bool {
switch state {
case domain.CapacityAdmissionAccepted, domain.CapacityAdmissionDeferred, domain.CapacityAdmissionDenied:
return true
default:
return false
}
}
func validCapacityPressureCode(code domain.CapacityPressureCode) bool {
switch code {
case domain.CapacityPressureEndpointOffline, domain.CapacityPressureEndpointStale, domain.CapacityPressureCapabilityGap, domain.CapacityPressureJobLimit, domain.CapacityPressureQueueLimit, domain.CapacityPressureBacklog:
return true
default:
return false
}
}
func validAlertSeverity(severity domain.AlertSeverity) bool {
switch severity {
case domain.AlertSeverityInfo, domain.AlertSeverityWarning, domain.AlertSeverityCritical:
return true
default:
return false
}
}
func validAlertState(state domain.AlertState) bool {
switch state {
case domain.AlertStateActive, domain.AlertStateAcknowledged, domain.AlertStateResolved:
return true
default:
return false
}
}
func validPluginLifecycleState(state domain.PluginLifecycleState) bool {
switch state {
case domain.PluginLifecycleStatePending, domain.PluginLifecycleStateInstalled, domain.PluginLifecycleStateEnabled, domain.PluginLifecycleStateDisabled, domain.PluginLifecycleStateUpgrading, domain.PluginLifecycleStateRollingBack, domain.PluginLifecycleStateRetired, domain.PluginLifecycleStateFailed:
return true
default:
return false
}
}
func validPluginLifecycleOperation(operation domain.PluginLifecycleOperation) bool {
switch operation {
case domain.PluginLifecycleOperationInstall, domain.PluginLifecycleOperationEnable, domain.PluginLifecycleOperationDisable, domain.PluginLifecycleOperationUpgrade, domain.PluginLifecycleOperationRollback, domain.PluginLifecycleOperationRetire, domain.PluginLifecycleOperationDependencyCheck:
return true
default:
return false
}
}
func validAIConfigDiffState(state domain.AIConfigDiffState) bool {
switch state {
case domain.AIConfigDiffStatePending, domain.AIConfigDiffStateApproved, domain.AIConfigDiffStateCancelled, domain.AIConfigDiffStateExpired:
return true
default:
return false
}
}
func unsafeProductionText(value string) bool {
lower := strings.ToLower(value)
return containsUnsafeRuntimeSecret(value) ||
looksLikeRawHostPath(value) ||
strings.Contains(lower, "api key") ||
strings.Contains(lower, "apikey") ||
strings.Contains(lower, "provider base url") ||
strings.Contains(lower, "direct run") ||
strings.Contains(lower, "rcon password") ||
strings.Contains(lower, "dsn=")
}