Add server file manager workflow
This commit is contained in:
@@ -1421,6 +1421,104 @@ func ValidateFileOperationDispatchRequest(request domain.FileOperationDispatchRe
|
||||
return finish(violations)
|
||||
}
|
||||
|
||||
func ValidateServerFileListRequest(request domain.ServerFileListRequest) error {
|
||||
var violations []string
|
||||
violations = appendRequired(violations, "serverInstanceId", request.ServerInstanceID)
|
||||
violations = appendRequired(violations, "directoryKey", request.DirectoryKey)
|
||||
if !validLogicalFileKey(request.DirectoryKey) {
|
||||
violations = append(violations, "directoryKey is not allowed")
|
||||
}
|
||||
if request.Path != "" && !validLogicalFileKey(request.Path) {
|
||||
violations = append(violations, "path is not allowed")
|
||||
}
|
||||
if len([]rune(request.Query)) > 80 || containsUnsafeRuntimeSecret(request.Query) || looksLikeRawHostPath(request.Query) {
|
||||
violations = append(violations, "query is not allowed")
|
||||
}
|
||||
if request.IdempotencyKey != "" && (containsUnsafeRuntimeSecret(request.IdempotencyKey) || looksLikeRawHostPath(request.IdempotencyKey)) {
|
||||
violations = append(violations, "idempotencyKey is not allowed")
|
||||
}
|
||||
return finish(violations)
|
||||
}
|
||||
|
||||
func ValidateServerFileReadRequest(request domain.ServerFileReadRequest) error {
|
||||
return ValidateFileOperationDispatchRequest(domain.FileOperationDispatchRequest{ServerInstanceID: request.ServerInstanceID, PluginID: request.PluginID, Operation: domain.FileOperationRead, Key: request.Key, IdempotencyKey: request.IdempotencyKey})
|
||||
}
|
||||
|
||||
func ValidateServerFileWriteRequest(request domain.ServerFileWriteRequest) error {
|
||||
return ValidateFileOperationDispatchRequest(domain.FileOperationDispatchRequest{ServerInstanceID: request.ServerInstanceID, PluginID: request.PluginID, Operation: domain.FileOperationWrite, Key: request.Key, InputRef: request.InputRef, Content: request.Content, ExpectedConfigVersion: request.ExpectedVersion, ExpectedChecksum: request.ExpectedChecksum, IdempotencyKey: request.IdempotencyKey})
|
||||
}
|
||||
|
||||
func ValidateServerFileUploadRequest(request domain.ServerFileUploadRequest) error {
|
||||
request = domain.CopyServerFileUploadRequest(request)
|
||||
var violations []string
|
||||
violations = appendRequired(violations, "serverInstanceId", request.ServerInstanceID)
|
||||
violations = appendRequired(violations, "directoryKey", request.DirectoryKey)
|
||||
violations = appendRequired(violations, "filename", request.Filename)
|
||||
violations = appendRequired(violations, "checksum", request.Checksum)
|
||||
violations = appendRequired(violations, "idempotencyKey", request.IdempotencyKey)
|
||||
if !validLogicalFileKey(request.DirectoryKey) {
|
||||
violations = append(violations, "directoryKey is not allowed")
|
||||
}
|
||||
if request.RelativePath != "" && !validLogicalFileKey(request.RelativePath) {
|
||||
violations = append(violations, "relativePath is not allowed")
|
||||
}
|
||||
if !validUploadFilename(request.Filename) {
|
||||
violations = append(violations, "filename is not allowed")
|
||||
}
|
||||
if len(request.Payload) == 0 {
|
||||
violations = append(violations, "payload is required")
|
||||
}
|
||||
if int64(len(request.Payload)) > MaxArtifactBytes {
|
||||
violations = append(violations, fmt.Sprintf("payload must not exceed %d", MaxArtifactBytes))
|
||||
}
|
||||
if request.Checksum != "" {
|
||||
if !validSHA256Checksum(request.Checksum) {
|
||||
violations = append(violations, "checksum must be sha256:<hex>")
|
||||
} else if request.Checksum != BytesChecksum(request.Payload) {
|
||||
violations = append(violations, "checksum does not match payload")
|
||||
}
|
||||
}
|
||||
for _, value := range []string{request.ServerInstanceID, request.DirectoryKey, request.RelativePath, request.Filename, request.IdempotencyKey} {
|
||||
if containsUnsafeRuntimeSecret(value) || looksLikeRawHostPath(value) || strings.Contains(strings.ToLower(value), "unix://") {
|
||||
violations = append(violations, "request contains unsafe content")
|
||||
break
|
||||
}
|
||||
}
|
||||
return finish(violations)
|
||||
}
|
||||
|
||||
func ValidateServerFileDownloadRequest(request domain.ServerFileDownloadRequest) error {
|
||||
var violations []string
|
||||
violations = appendRequired(violations, "serverInstanceId", request.ServerInstanceID)
|
||||
violations = appendRequired(violations, "key", request.Key)
|
||||
violations = appendRequired(violations, "idempotencyKey", request.IdempotencyKey)
|
||||
if !validLogicalFileKey(request.Key) {
|
||||
violations = append(violations, "key is not allowed")
|
||||
}
|
||||
if containsUnsafeRuntimeSecret(request.IdempotencyKey) || looksLikeRawHostPath(request.IdempotencyKey) {
|
||||
violations = append(violations, "idempotencyKey is not allowed")
|
||||
}
|
||||
return finish(violations)
|
||||
}
|
||||
|
||||
func ValidateRunFileInputChunkRequest(request domain.RunFileInputChunkRequest) error {
|
||||
var violations []string
|
||||
violations = appendRequired(violations, "runEndpointId", request.RunEndpointID)
|
||||
violations = appendRequired(violations, "sessionToken", request.SessionToken)
|
||||
violations = appendRequired(violations, "jobId", request.JobID)
|
||||
violations = appendRequired(violations, "leaseToken", request.LeaseToken)
|
||||
if request.Attempt <= 0 {
|
||||
violations = append(violations, "attempt must be positive")
|
||||
}
|
||||
if request.Offset < 0 {
|
||||
violations = append(violations, "offset must not be negative")
|
||||
}
|
||||
if request.Length <= 0 || request.Length > MaxArtifactDownloadBytes {
|
||||
violations = append(violations, fmt.Sprintf("length must be between 1 and %d", MaxArtifactDownloadBytes))
|
||||
}
|
||||
return finish(violations)
|
||||
}
|
||||
|
||||
func appendPercentViolation(violations []string, field string, value float64) []string {
|
||||
if value < 0 || value > 100 {
|
||||
return append(violations, field+" must be between 0 and 100")
|
||||
@@ -1588,7 +1686,7 @@ func ValidateJob(job domain.Job) error {
|
||||
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.Capability == domain.JobCapabilityConfigWrite || job.Capability == domain.JobCapabilityFilesList || job.Capability == domain.JobCapabilityFilesRead || job.Capability == domain.JobCapabilityFilesWrite {
|
||||
if job.ServerInstanceID == "" {
|
||||
violations = append(violations, "serverInstanceId is required for scoped file jobs")
|
||||
}
|
||||
@@ -2305,13 +2403,27 @@ func validRemoteDatabaseEngine(engine string) bool {
|
||||
|
||||
func validFileOperationKind(operation domain.FileOperationKind) bool {
|
||||
switch operation {
|
||||
case domain.FileOperationRead, domain.FileOperationWrite:
|
||||
case domain.FileOperationList, domain.FileOperationRead, domain.FileOperationWrite:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func validUploadFilename(name string) bool {
|
||||
trimmed := strings.TrimSpace(name)
|
||||
if trimmed == "" || trimmed != name || len([]rune(name)) > 120 || strings.Contains(name, "/") || strings.Contains(name, `\`) || strings.Contains(name, "..") || strings.Contains(name, "://") || looksLikeRawHostPath(name) || containsUnsafeRuntimeSecret(name) {
|
||||
return false
|
||||
}
|
||||
for _, char := range name {
|
||||
if (char >= 'a' && char <= 'z') || (char >= 'A' && char <= 'Z') || (char >= '0' && char <= '9') || char == '_' || char == '-' || char == '.' || char == ' ' || char == '(' || char == ')' {
|
||||
continue
|
||||
}
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func validConfigFileKey(key string) bool {
|
||||
switch key {
|
||||
case "server.properties", "config/server.properties":
|
||||
|
||||
Reference in New Issue
Block a user