feat: 完整游戏运维功能
This commit is contained in:
@@ -2,6 +2,7 @@ package validator
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"browser.local/platform/domain"
|
||||
@@ -14,6 +15,7 @@ func ValidateRuntimeBinding(binding domain.RuntimeBinding) error {
|
||||
violations = appendRequired(violations, "id", binding.ID)
|
||||
violations = appendRequired(violations, "serverInstanceId", binding.ServerInstanceID)
|
||||
violations = appendRequired(violations, "pluginId", binding.PluginID)
|
||||
violations = appendRequired(violations, "pluginVersion", binding.PluginVersion)
|
||||
violations = appendRequired(violations, "profileKey", binding.ProfileKey)
|
||||
violations = appendRequired(violations, "mode", binding.Mode)
|
||||
if !validRuntimeBindingStatus(binding.Status) {
|
||||
@@ -23,7 +25,10 @@ func ValidateRuntimeBinding(binding domain.RuntimeBinding) error {
|
||||
if !validDistributionLogicalKey(key) {
|
||||
violations = append(violations, "bindings key is invalid")
|
||||
}
|
||||
if containsUnsafeRuntimeSecret(value) || looksLikeRawHostPath(value) || strings.Contains(strings.ToLower(value), "://") && !strings.HasPrefix(value, "secret://") {
|
||||
trimmed := strings.TrimSpace(value)
|
||||
lowerKey := strings.ToLower(key)
|
||||
sensitiveKey := strings.Contains(lowerKey, "password") || strings.Contains(lowerKey, "credential") || strings.Contains(lowerKey, "secret") || strings.Contains(lowerKey, "token") || strings.Contains(lowerKey, "dsn")
|
||||
if trimmed != value || strings.HasPrefix(value, "/") || strings.HasPrefix(value, `\`) || containsUnsafeRuntimeSecret(value) || looksLikeRawHostPath(value) || strings.Contains(strings.ToLower(value), "://") && !strings.HasPrefix(value, "secret://") || sensitiveKey && value != "" && !strings.HasPrefix(value, "secret://") {
|
||||
violations = append(violations, "bindings."+key+" must use safe logical or secret refs")
|
||||
}
|
||||
}
|
||||
@@ -176,6 +181,18 @@ func ValidateDependencyStatus(status domain.DependencyStatus) error {
|
||||
if status.InstallPlanKey != "" && !validDistributionLogicalKey(status.InstallPlanKey) {
|
||||
violations = append(violations, "installPlanKey is invalid")
|
||||
}
|
||||
if status.PlanDigest != "" && !validSHA256Checksum(status.PlanDigest) {
|
||||
violations = append(violations, "planDigest must be sha256:<hex>")
|
||||
}
|
||||
if len(status.JobID) > 180 || containsUnsafeRuntimeSecret(status.JobID) || looksLikeRawHostPath(status.JobID) {
|
||||
violations = append(violations, "jobId is unsafe or too long")
|
||||
}
|
||||
if status.CompletedSteps < 0 || status.CompletedSteps > 64 {
|
||||
violations = append(violations, "completedSteps is out of bounds")
|
||||
}
|
||||
if len(status.Evidence) > maxDistributionMessageLength || containsUnsafeRuntimeSecret(status.Evidence) || looksLikeRawHostPath(status.Evidence) {
|
||||
violations = append(violations, "evidence is unsafe or too long")
|
||||
}
|
||||
if len(status.Message) > maxDistributionMessageLength || containsUnsafeRuntimeSecret(status.Message) || looksLikeRawHostPath(status.Message) {
|
||||
violations = append(violations, "message is unsafe or too long")
|
||||
}
|
||||
@@ -229,6 +246,9 @@ func ValidateRunUpdateJob(job domain.RunUpdateJob) error {
|
||||
violations = appendRequired(violations, "runEndpointId", job.RunEndpointID)
|
||||
violations = appendRequired(violations, "artifactId", job.ArtifactID)
|
||||
violations = appendRequired(violations, "checksum", job.Checksum)
|
||||
violations = appendRequired(violations, "targetOs", job.TargetOS)
|
||||
violations = appendRequired(violations, "targetArch", job.TargetArch)
|
||||
violations = appendRequired(violations, "targetRelease", job.TargetRelease)
|
||||
violations = appendRequired(violations, "idempotencyKey", job.IdempotencyKey)
|
||||
if job.Checksum != "" && !validSHA256Checksum(job.Checksum) {
|
||||
violations = append(violations, "checksum must be sha256:<hex>")
|
||||
@@ -236,6 +256,13 @@ func ValidateRunUpdateJob(job domain.RunUpdateJob) error {
|
||||
if !validDistributionJobStatus(job.Status) {
|
||||
violations = append(violations, "status is invalid")
|
||||
}
|
||||
violations = appendDistributionTargetViolations(violations, job.TargetOS, job.TargetArch)
|
||||
if !validRunUpdatePhase(job.Phase) {
|
||||
violations = append(violations, "phase is invalid")
|
||||
}
|
||||
if len(job.Message) > maxDistributionMessageLength || containsUnsafeRuntimeSecret(job.Message) || looksLikeRawHostPath(job.Message) {
|
||||
violations = append(violations, "message is unsafe or too long")
|
||||
}
|
||||
if containsUnsafeRuntimeSecret(job.IdempotencyKey) || looksLikeRawHostPath(job.IdempotencyKey) {
|
||||
violations = append(violations, "idempotencyKey is unsafe")
|
||||
}
|
||||
@@ -248,6 +275,15 @@ func ValidateRunUpdateJob(job domain.RunUpdateJob) error {
|
||||
return finish(violations)
|
||||
}
|
||||
|
||||
func validRunUpdatePhase(phase domain.RunUpdatePhase) bool {
|
||||
switch phase {
|
||||
case domain.RunUpdatePhaseQueued, domain.RunUpdatePhaseDownloading, domain.RunUpdatePhaseStaged, domain.RunUpdatePhaseRestartRequested, domain.RunUpdatePhaseActivating, domain.RunUpdatePhaseSucceeded, domain.RunUpdatePhaseRolledBack, domain.RunUpdatePhaseFailed:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func ValidateRunDistributionGenerateRequest(request domain.RunDistributionGenerateRequest) error {
|
||||
var violations []string
|
||||
violations = appendRequired(violations, "serverInstanceId", request.ServerInstanceID)
|
||||
@@ -341,8 +377,8 @@ func validateRepositoryURL(field string, value string) []string {
|
||||
if strings.TrimSpace(value) == "" {
|
||||
return nil
|
||||
}
|
||||
lowered := strings.ToLower(strings.TrimSpace(value))
|
||||
if !strings.HasPrefix(lowered, "https://") || !strings.HasSuffix(lowered, ".git") {
|
||||
parsed, err := url.ParseRequestURI(strings.TrimSpace(value))
|
||||
if err != nil || parsed.Scheme != "https" || parsed.Host == "" || parsed.User != nil || parsed.RawQuery != "" || parsed.Fragment != "" || !strings.HasSuffix(parsed.Path, ".git") {
|
||||
return []string{field + " must be an HTTPS git repository URL"}
|
||||
}
|
||||
for _, reason := range unsafePluginStringReasons(value) {
|
||||
|
||||
Reference in New Issue
Block a user