fix: validate target and hostname inputs to prevent injection
Add input validation to address security concerns: - Validate Target field in BuildRequest against safe character pattern (must be "all" or match alphanumeric/dash/underscore/dot pattern) - Filter hostnames discovered from nix flake show output, skipping any with invalid characters before using them in build commands This prevents potential command injection via crafted NATS messages or malicious flake configurations. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -4,6 +4,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log/slog"
|
"log/slog"
|
||||||
|
"regexp"
|
||||||
"sort"
|
"sort"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
@@ -13,6 +14,10 @@ import (
|
|||||||
"git.t-juice.club/torjus/homelab-deploy/internal/nats"
|
"git.t-juice.club/torjus/homelab-deploy/internal/nats"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// hostnameRegex validates hostnames from flake output.
|
||||||
|
// Allows: alphanumeric, dashes, underscores, dots.
|
||||||
|
var hostnameRegex = regexp.MustCompile(`^[a-zA-Z0-9._-]+$`)
|
||||||
|
|
||||||
// BuilderConfig holds the configuration for the builder.
|
// BuilderConfig holds the configuration for the builder.
|
||||||
type BuilderConfig struct {
|
type BuilderConfig struct {
|
||||||
NATSUrl string
|
NATSUrl string
|
||||||
@@ -197,6 +202,16 @@ func (b *Builder) handleBuildRequest(subject string, data []byte) {
|
|||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
// Filter out hostnames with invalid characters (security: prevent injection)
|
||||||
|
validHosts := make([]string, 0, len(hosts))
|
||||||
|
for _, host := range hosts {
|
||||||
|
if hostnameRegex.MatchString(host) {
|
||||||
|
validHosts = append(validHosts, host)
|
||||||
|
} else {
|
||||||
|
b.logger.Warn("skipping hostname with invalid characters", "hostname", host)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
hosts = validHosts
|
||||||
// Sort hosts for consistent ordering
|
// Sort hosts for consistent ordering
|
||||||
sort.Strings(hosts)
|
sort.Strings(hosts)
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -45,6 +45,10 @@ func (r *BuildRequest) Validate() error {
|
|||||||
if r.Target == "" {
|
if r.Target == "" {
|
||||||
return fmt.Errorf("target is required")
|
return fmt.Errorf("target is required")
|
||||||
}
|
}
|
||||||
|
// Target must be "all" or a valid hostname (same format as revision/branch)
|
||||||
|
if r.Target != "all" && !revisionRegex.MatchString(r.Target) {
|
||||||
|
return fmt.Errorf("invalid target format: %q", r.Target)
|
||||||
|
}
|
||||||
if r.Branch != "" && !revisionRegex.MatchString(r.Branch) {
|
if r.Branch != "" && !revisionRegex.MatchString(r.Branch) {
|
||||||
return fmt.Errorf("invalid branch format: %q", r.Branch)
|
return fmt.Errorf("invalid branch format: %q", r.Branch)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user