package validator import ( "strings" "browser.local/platform/domain" ) 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") } 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 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 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=") }