4b70e31cf4
Critical fixes:
- logs --job: validate ^[A-Za-z0-9_-]+$ + shellQuote (was %q backtick RCE)
- pprof: isLoopback treats empty host as bind-all (was :6060 bypass)
- backup restore: filepath.Rel containment check (was tar-slip via a/../..)
- WebAuthn reg auth deferred to P04 (requires session infra)
High fixes:
- txn rollback/show/apply: validate ^T-[0-9a-f]{16}$ + shellQuote
- nft diff --against: validate txn ID before filepath.Join
- drain stopAlloc: validate allocID ^[A-Za-z0-9_-]+$
- cluster_compat: shellQuote peer dir name
- podman image: shellQuote (was %q backtick injection)
- nft TrustedProbes: net.ParseIP/CIDR validation + split v4/v6 sets
- sudoers: validate --proxmox-user/--proxmox-role ^[a-zA-Z_][a-zA-Z0-9_-]{0,31}$
fixed path /etc/sudoers.d/orca; shellQuote pveum/useradd; validateSudoers
checks actual file
- nft country block: validate ^[A-Z]{2}$ (was len==2 only)
New file: internal/cli/validate.go (shared validators + shellQuote)
All 38 Go test packages pass. go vet + gofmt clean.
---ci---
project: orca
phase: 2
milestone: v0.13
status: complete
requirements:
covered: [150]
---/ci---
60 lines
2.4 KiB
Go
60 lines
2.4 KiB
Go
// Package cli: validate.go provides shared input-validation helpers for
|
|
// CLI command arguments that are interpolated into remote shell commands
|
|
// or filesystem paths (Phase 02 injection hardening, v0.13).
|
|
//
|
|
// These helpers enforce strict allowlists so that attacker-controlled
|
|
// values (job names, txn IDs, alloc IDs, country codes) cannot reach
|
|
// shell interpolation or path joins without matching a known-safe shape.
|
|
package cli
|
|
|
|
import (
|
|
"regexp"
|
|
"strings"
|
|
)
|
|
|
|
// safeNameRe matches the allowlist for shell-interpolated identifiers
|
|
// (job names, alloc IDs): ASCII letters, digits, underscore, hyphen.
|
|
// Used to prevent backtick/command-substitution and metacharacter
|
|
// injection into remote shell commands.
|
|
var safeNameRe = regexp.MustCompile(`^[A-Za-z0-9_-]+$`)
|
|
|
|
// txnIDRe matches the canonical orca transaction ID format: "T-" prefix
|
|
// followed by exactly 16 lowercase hex digits. Used to validate txn IDs
|
|
// before they are interpolated into filesystem paths or remote shell
|
|
// commands (`orca txn rollback`, `orca nft diff --against`).
|
|
var txnIDRe = regexp.MustCompile(`^T-[0-9a-f]{16}$`)
|
|
|
|
// countryCodeRe matches ISO-3166 alpha-2 country codes: exactly two
|
|
// uppercase ASCII letters. Used by `orca nft country block add` before
|
|
// codes are interpolated into the nft ruleset.
|
|
var countryCodeRe = regexp.MustCompile(`^[A-Z]{2}$`)
|
|
|
|
// validSafeName reports whether s is a safe shell-interpolation
|
|
// identifier (ASCII alphanumeric, underscore, hyphen only, non-empty).
|
|
func validSafeName(s string) bool {
|
|
return safeNameRe.MatchString(s)
|
|
}
|
|
|
|
// validTxnID reports whether s matches the canonical orca txn ID format
|
|
// (^T-[0-9a-f]{16}$).
|
|
func validTxnID(s string) bool {
|
|
return txnIDRe.MatchString(s)
|
|
}
|
|
|
|
// validCountryCode reports whether s is a valid ISO-3166 alpha-2 code
|
|
// (two uppercase letters).
|
|
func validCountryCode(s string) bool {
|
|
return countryCodeRe.MatchString(s)
|
|
}
|
|
|
|
// shellQuote single-quotes a string for safe shell interpolation over
|
|
// SSH exec. It escapes embedded single-quotes via the standard '\” idiom
|
|
// (POSIX shell). This is the cli-package copy of the helper duplicated
|
|
// across runtime/identity/stepca/sshpush to avoid import cycles; it
|
|
// hardens command interpolation against backtick/command-substitution
|
|
// injection (Go's %q does NOT escape backticks, and bash executes
|
|
// command substitution inside double quotes).
|
|
func shellQuote(s string) string {
|
|
return "'" + strings.ReplaceAll(s, "'", "'\\''") + "'"
|
|
}
|