Volt CLI: source-available under AGPSL v5.0
Complete infrastructure platform CLI: - Container runtime (systemd-nspawn) - VoltVisor VMs (Neutron Stardust / QEMU) - Stellarium CAS (content-addressed storage) - ORAS Registry - GitOps integration - Landlock LSM security - Compose orchestration - Mesh networking Copyright (c) Armored Gates LLC. All rights reserved. Licensed under AGPSL v5.0
This commit is contained in:
69
pkg/validate/validate.go
Normal file
69
pkg/validate/validate.go
Normal file
@@ -0,0 +1,69 @@
|
||||
// Package validate provides shared input validation for all Volt components.
|
||||
// Every CLI command and API endpoint should validate user input through these
|
||||
// functions before using names in file paths, systemd units, or shell commands.
|
||||
package validate
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// nameRegex allows lowercase alphanumeric, hyphens, underscores, and dots.
|
||||
// Must start with a letter or digit. Max 64 chars.
|
||||
var nameRegex = regexp.MustCompile(`^[a-zA-Z0-9][a-zA-Z0-9._-]{0,63}$`)
|
||||
|
||||
// WorkloadName validates a workload/container/VM name.
|
||||
// Names are used in file paths, systemd unit names, and network identifiers,
|
||||
// so they must be strictly validated to prevent path traversal, injection, etc.
|
||||
//
|
||||
// Rules:
|
||||
// - 1-64 characters
|
||||
// - Alphanumeric, hyphens, underscores, dots only
|
||||
// - Must start with a letter or digit
|
||||
// - No path separators (/, \)
|
||||
// - No whitespace
|
||||
// - No shell metacharacters
|
||||
func WorkloadName(name string) error {
|
||||
if name == "" {
|
||||
return fmt.Errorf("name cannot be empty")
|
||||
}
|
||||
if len(name) > 64 {
|
||||
return fmt.Errorf("name too long (%d chars, max 64)", len(name))
|
||||
}
|
||||
if !nameRegex.MatchString(name) {
|
||||
return fmt.Errorf("invalid name %q: must be alphanumeric with hyphens, underscores, or dots, starting with a letter or digit", name)
|
||||
}
|
||||
// Extra safety: reject anything with path components
|
||||
if strings.Contains(name, "/") || strings.Contains(name, "\\") || strings.Contains(name, "..") {
|
||||
return fmt.Errorf("invalid name %q: path separators and '..' not allowed", name)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// BridgeName validates a network bridge name.
|
||||
// Linux interface names are max 15 chars, alphanumeric + hyphens.
|
||||
func BridgeName(name string) error {
|
||||
if name == "" {
|
||||
return fmt.Errorf("bridge name cannot be empty")
|
||||
}
|
||||
if len(name) > 15 {
|
||||
return fmt.Errorf("bridge name too long (%d chars, max 15 for Linux interfaces)", len(name))
|
||||
}
|
||||
if !regexp.MustCompile(`^[a-zA-Z][a-zA-Z0-9-]*$`).MatchString(name) {
|
||||
return fmt.Errorf("invalid bridge name %q: must start with a letter, alphanumeric and hyphens only", name)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// SafePath checks that a constructed path stays within the expected base directory.
|
||||
// Use this after filepath.Join to prevent traversal.
|
||||
func SafePath(base, constructed string) error {
|
||||
// Clean both paths for comparison
|
||||
cleanBase := strings.TrimRight(base, "/") + "/"
|
||||
cleanPath := constructed + "/"
|
||||
if !strings.HasPrefix(cleanPath, cleanBase) {
|
||||
return fmt.Errorf("path %q escapes base directory %q", constructed, base)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user