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
70 lines
2.5 KiB
Go
70 lines
2.5 KiB
Go
// 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
|
|
}
|