package validator import ( "crypto/sha256" "encoding/hex" "fmt" "strings" "browser.local/platform/domain" ) const MaxArtifactChunkBytes = 1024 * 1024 func ValidateArtifactTransferOpen(open domain.ArtifactTransferOpen) error { var violations []string violations = appendRequired(violations, "runEndpointId", open.RunEndpointID) violations = appendRequired(violations, "sessionToken", open.SessionToken) violations = appendRequired(violations, "artifactId", open.ArtifactID) violations = appendRequired(violations, "ownerId", open.OwnerID) violations = appendRequired(violations, "checksum", open.Checksum) violations = appendRequired(violations, "idempotencyKey", open.IdempotencyKey) if open.Direction != domain.ArtifactTransferDirectionUpload { violations = append(violations, "direction must be upload") } if !validArtifactOwnerKind(open.OwnerKind) { violations = append(violations, "ownerKind is invalid") } if open.OwnerKind != domain.ArtifactOwnerKindJob && open.OwnerKind != domain.ArtifactOwnerKindServerInstance { violations = append(violations, "ownerKind must be job or server-instance for run uploads") } if open.SizeBytes <= 0 { violations = append(violations, "sizeBytes must be positive") } if open.ChunkSizeBytes <= 0 { violations = append(violations, "chunkSizeBytes must be positive") } if open.ChunkSizeBytes > MaxArtifactChunkBytes { violations = append(violations, fmt.Sprintf("chunkSizeBytes must not exceed %d", MaxArtifactChunkBytes)) } if open.Checksum != "" && !validSHA256Checksum(open.Checksum) { violations = append(violations, "checksum must be sha256:") } return finish(violations) } func ValidateArtifactChunkUpload(chunk domain.ArtifactChunkUpload) error { var violations []string violations = appendRequired(violations, "runEndpointId", chunk.RunEndpointID) violations = appendRequired(violations, "sessionToken", chunk.SessionToken) violations = appendRequired(violations, "transferId", chunk.TransferID) violations = appendRequired(violations, "artifactId", chunk.ArtifactID) violations = appendRequired(violations, "checksum", chunk.Checksum) if chunk.ChunkIndex < 0 { violations = append(violations, "chunkIndex must not be negative") } if chunk.Offset < 0 { violations = append(violations, "offset must not be negative") } if chunk.SizeBytes <= 0 { violations = append(violations, "sizeBytes must be positive") } if chunk.SizeBytes > MaxArtifactChunkBytes { violations = append(violations, fmt.Sprintf("sizeBytes must not exceed %d", MaxArtifactChunkBytes)) } if len(chunk.Payload) == 0 { violations = append(violations, "payload must not be empty") } if chunk.SizeBytes > 0 && len(chunk.Payload) != chunk.SizeBytes { violations = append(violations, "sizeBytes must match payload size") } if chunk.Checksum != "" { if !validSHA256Checksum(chunk.Checksum) { violations = append(violations, "checksum must be sha256:") } else if chunk.Checksum != BytesChecksum(chunk.Payload) { violations = append(violations, "checksum does not match payload") } } return finish(violations) } func ValidateArtifactTransferStatusQuery(query domain.ArtifactTransferStatusQuery) error { var violations []string violations = appendRequired(violations, "runEndpointId", query.RunEndpointID) violations = appendRequired(violations, "sessionToken", query.SessionToken) violations = appendRequired(violations, "transferId", query.TransferID) violations = appendRequired(violations, "artifactId", query.ArtifactID) return finish(violations) } func ValidateArtifactTransferComplete(complete domain.ArtifactTransferComplete) error { var violations []string violations = appendRequired(violations, "runEndpointId", complete.RunEndpointID) violations = appendRequired(violations, "sessionToken", complete.SessionToken) violations = appendRequired(violations, "transferId", complete.TransferID) violations = appendRequired(violations, "artifactId", complete.ArtifactID) violations = appendRequired(violations, "checksum", complete.Checksum) if complete.SizeBytes <= 0 { violations = append(violations, "sizeBytes must be positive") } if complete.Checksum != "" && !validSHA256Checksum(complete.Checksum) { violations = append(violations, "checksum must be sha256:") } return finish(violations) } func BytesChecksum(payload []byte) string { sum := sha256.Sum256(payload) return "sha256:" + hex.EncodeToString(sum[:]) } func validSHA256Checksum(value string) bool { if !strings.HasPrefix(value, "sha256:") { return false } hexValue := strings.TrimPrefix(value, "sha256:") if len(hexValue) != sha256.Size*2 { return false } _, err := hex.DecodeString(hexValue) return err == nil }