From 87a345a530ddf7551a3c88e25735923436b042ec Mon Sep 17 00:00:00 2001 From: CyberMind-FR Date: Thu, 9 Jul 2026 05:56:57 +0200 Subject: [PATCH] fix(openclaw): reject leading-dash targets (flag-injection) + failure-record schema --- packages/secubox-openclaw/sbin/openclawctl | 12 ++++++++---- .../tests/test_openclawctl_guards.sh | 1 + 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/packages/secubox-openclaw/sbin/openclawctl b/packages/secubox-openclaw/sbin/openclawctl index 40a675f4..0be7c67f 100755 --- a/packages/secubox-openclaw/sbin/openclawctl +++ b/packages/secubox-openclaw/sbin/openclawctl @@ -15,7 +15,9 @@ SCANS_DIR="${SECUBOX_OPENCLAW_SCANS:-/var/lib/secubox/openclaw/scans}" err() { echo "[ERROR] $*" >&2; } # ---- injection guards (defense in depth; API validates too) ---- -_valid_target() { [[ "$1" =~ ^[A-Za-z0-9._:@/-]+$ ]]; } +# Require an alphanumeric first char so a leading '-' can never be parsed as a +# flag by dig/nmap/whois (flag-injection). Domains/IPs/emails/CIDRs all qualify. +_valid_target() { [[ "$1" =~ ^[A-Za-z0-9][A-Za-z0-9._:@/-]*$ ]]; } _valid_scanid() { [[ "$1" =~ ^[a-f0-9]{8}$ ]]; } _valid_type() { case "$1" in domain|ip|email|dns|whois|certs|ports) return 0;; *) return 1;; esac; } @@ -97,7 +99,7 @@ cmd_scan() { _valid_type "$type" || { err "bad type"; return 2; } _valid_target "$target"|| { err "bad target"; return 2; } _valid_scanid "$id" || { err "bad id"; return 2; } - lxc_running || { _scan_write "$id" "$(jq -nc --arg i "$id" --arg t "$type" --arg g "$target" '{id:$i,type:$t,target:$g,status:"failed",error:"container not running",results:null}')"; return 1; } + lxc_running || { local now; now="$(date -u +%FT%TZ)"; _scan_write "$id" "$(jq -nc --arg i "$id" --arg t "$type" --arg g "$target" --arg n "$now" '{id:$i,type:$t,target:$g,status:"failed",started_at:$n,finished_at:$n,results:null,error:"container not running"}')"; return 1; } local started; started="$(date -u +%FT%TZ)" local raw rc case "$type" in @@ -106,8 +108,10 @@ cmd_scan() { certs) raw="$(lxc_attach 'curl -s --max-time 20 "https://crt.sh/?q=$1&output=json"' "$target" 2>&1)"; rc=$? ;; ports) raw="$(lxc_attach 'nmap -Pn -T4 --top-ports 100 -oG - "$1"' "$target" 2>&1)"; rc=$? ;; ip) raw="$(lxc_attach 'nmap -Pn -T4 -sV --top-ports 200 "$1"' "$target" 2>&1)"; rc=$? ;; - domain) raw="$(lxc_attach 'echo "== DNS =="; dig +noall +answer ANY "$1"; echo "== WHOIS =="; whois -- "$1" 2>/dev/null | head -40; echo "== CERTS =="; curl -s --max-time 20 "https://crt.sh/?q=$1&output=json" | head -c 20000' "$target" 2>&1)"; rc=$? ;; - email) raw="$(lxc_attach 'd="${1#*@}"; echo "== MX =="; dig +short MX "$d"; echo "== SPF =="; dig +short TXT "$d" | grep -i spf' "$target" 2>&1)"; rc=$? ;; + # domain/email are OSINT aggregates: partial data is expected and the + # trailing head/grep would mask the real tool rc, so always report done. + domain) raw="$(lxc_attach 'echo "== DNS =="; dig +noall +answer ANY "$1"; echo "== WHOIS =="; whois -- "$1" 2>/dev/null | head -40; echo "== CERTS =="; curl -s --max-time 20 "https://crt.sh/?q=$1&output=json" | head -c 20000' "$target" 2>&1)"; rc=0 ;; + email) raw="$(lxc_attach 'd="${1#*@}"; echo "== MX =="; dig +short MX "$d"; echo "== SPF =="; dig +short TXT "$d" | grep -i spf' "$target" 2>&1)"; rc=0 ;; *) err "unhandled type"; return 2 ;; esac local status="completed"; [ "$rc" -ne 0 ] && status="failed" diff --git a/packages/secubox-openclaw/tests/test_openclawctl_guards.sh b/packages/secubox-openclaw/tests/test_openclawctl_guards.sh index b950ebae..d0653722 100755 --- a/packages/secubox-openclaw/tests/test_openclawctl_guards.sh +++ b/packages/secubox-openclaw/tests/test_openclawctl_guards.sh @@ -7,6 +7,7 @@ ok() { "$CTL" __guard "$1" "$2" >/dev/null 2>&1 && echo "PASS accept $1 '$2'" | no() { "$CTL" __guard "$1" "$2" >/dev/null 2>&1 && { echo "FAIL should-reject $1 '$2'"; fail=1; } || echo "PASS reject $1 '$2'"; } ok target "example.com"; ok target "192.168.1.10"; ok target "10.0.0.0/24"; ok target "a@b.com" no target 'a;rm -rf /'; no target 'a b'; no target "$(printf 'a\nb')" +no target '-f/etc/hostname'; no target '-iL/tmp/x' ok scanid "a1b2c3d4"; no scanid "XYZ"; no scanid "a1b2c3d4e5" ok type domain; ok type ip; ok type email; ok type dns; ok type whois; ok type certs; ok type ports; no type pwn exit $fail