diff --git a/internal/adapters/dns/powerdns.go b/internal/adapters/dns/powerdns.go index 7d47d645c..076882872 100644 --- a/internal/adapters/dns/powerdns.go +++ b/internal/adapters/dns/powerdns.go @@ -64,7 +64,8 @@ func (b *PowerDNSBackend) CreateZone(ctx context.Context, zoneName string, names } // Add an initial SOA record to satisfy PowerDNS requirements for Native zones - soaContent := fmt.Sprintf("%s hostmaster.%s 1 10800 3600 604800 3600", nameservers[0], zoneName) + ns := nameservers[0] // validated above + soaContent := fmt.Sprintf("%s hostmaster.%s 1 10800 3600 604800 3600", ns, zoneName) soaRecord := ports.RecordSet{ Name: zoneName, Type: "SOA", diff --git a/internal/core/services/instance.go b/internal/core/services/instance.go index 338003c38..6dcc15ce4 100644 --- a/internal/core/services/instance.go +++ b/internal/core/services/instance.go @@ -7,6 +7,7 @@ import ( "fmt" "io" "log/slog" + "math" "net" "strconv" "strings" @@ -1005,6 +1006,10 @@ func (s *InstanceService) completeResize(ctx context.Context, tenantID uuid.UUID // 2. Compute resize (now that quota is settled) newCpuNano := int64(newIT.VCPUs) * NanoCPUsPerVCPU + const maxMemoryMB = math.MaxInt64 / BytesPerMB + if int64(newIT.MemoryMB) > maxMemoryMB { + return fmt.Errorf("memory size overflows int64: %d MB", newIT.MemoryMB) + } newMemoryBytes := int64(newIT.MemoryMB) * BytesPerMB if err := s.compute.ResizeInstance(ctx, target, newCpuNano, newMemoryBytes); err != nil { platform.InstanceOperationsTotal.WithLabelValues("resize", "failure").Inc() @@ -1495,7 +1500,13 @@ func (s *InstanceService) findAvailableIP(ipNet *net.IPNet, usedIPs map[string]b ip := make(net.IP, len(ipNet.IP)) copy(ip, ipNet.IP) - for { + ones, _ := ipNet.Mask.Size() + maxIPs := uint64(1) << (32 - ones) + if maxIPs > 1<<16 { + maxIPs = 1 << 16 // cap at 65536 to prevent unbounded iteration + } + + for iterations := uint64(0); iterations < maxIPs; iterations++ { // Increment IP for i := len(ip) - 1; i >= 0; i-- { ip[i]++ @@ -1517,7 +1528,7 @@ func (s *InstanceService) findAvailableIP(ipNet *net.IPNet, usedIPs map[string]b return displayIP, nil } } - return "", fmt.Errorf("no available IPs in subnet") + return "", fmt.Errorf("no available IPs in subnet after %d attempts", maxIPs) } func (s *InstanceService) Exec(ctx context.Context, idOrName string, cmd []string) (string, error) { diff --git a/internal/repositories/docker/snapshot_path.go b/internal/repositories/docker/snapshot_path.go index 8d3191e9d..c1e8a2e90 100644 --- a/internal/repositories/docker/snapshot_path.go +++ b/internal/repositories/docker/snapshot_path.go @@ -2,6 +2,7 @@ package docker import ( "fmt" + "os" "path/filepath" "strings" ) @@ -53,6 +54,11 @@ func validateSnapshotPath(p string) (string, error) { return "", fmt.Errorf("snapshot path is not canonical: %q (cleaned %q)", p, cleaned) } + // Reject symlinks to prevent traversal through symlink-based escapes. + if info, err := os.Lstat(cleaned); err == nil && (info.Mode()&os.ModeSymlink) != 0 { + return "", fmt.Errorf("snapshot path is a symlink: %q", p) + } + // After Clean a leading `..` cannot survive on an absolute path, but reject // any embedded `..` segment defensively for older filepath behavior on // other platforms. diff --git a/internal/repositories/ovs/adapter.go b/internal/repositories/ovs/adapter.go index c331b266c..75741da56 100644 --- a/internal/repositories/ovs/adapter.go +++ b/internal/repositories/ovs/adapter.go @@ -5,6 +5,7 @@ import ( "context" "fmt" "log/slog" + "net" "os/exec" "regexp" "strings" @@ -145,6 +146,9 @@ func (a *OvsAdapter) CreateVXLANTunnel(ctx context.Context, bridge string, vni i if !bridgeNameRegex.MatchString(bridge) { return errors.New(errors.InvalidInput, invalidBridgeNameMsg) } + if net.ParseIP(remoteIP) == nil { + return errors.New(errors.InvalidInput, "invalid remote IP address") + } tunnelName := fmt.Sprintf("vxlan-%s", strings.ReplaceAll(remoteIP, ".", "-")) cmd := a.exec.CommandContext(ctx, a.ovsPath, @@ -163,6 +167,9 @@ func (a *OvsAdapter) CreateVXLANTunnel(ctx context.Context, bridge string, vni i } func (a *OvsAdapter) DeleteVXLANTunnel(ctx context.Context, bridge string, remoteIP string) error { + if net.ParseIP(remoteIP) == nil { + return errors.New(errors.InvalidInput, "invalid remote IP address") + } tunnelName := fmt.Sprintf("vxlan-%s", strings.ReplaceAll(remoteIP, ".", "-")) return a.DeletePort(ctx, bridge, tunnelName) }