Files
openclaw/scripts/podman/common.sh
2026-08-25 04:06:53 -07:00

163 lines
4.1 KiB
Bash

#!/usr/bin/env bash
fail() {
echo "$*" >&2
exit 1
}
validate_single_line_value() {
local label="$1"
local value="$2"
if [[ "$value" == *$'\n'* || "$value" == *$'\r'* ]]; then
fail "Invalid $label: control characters are not allowed."
fi
}
validate_absolute_path() {
local label="$1"
local value="$2"
validate_single_line_value "$label" "$value"
[[ "$value" == /* ]] || fail "Invalid $label: expected an absolute path."
[[ "$value" != *"//"* ]] || fail "Invalid $label: repeated slashes are not allowed."
[[ "$value" != *"/./"* && "$value" != */. && "$value" != *"/../"* && "$value" != */.. ]] ||
fail "Invalid $label: dot path segments are not allowed."
}
validate_mount_source_path() {
local label="$1"
local value="$2"
validate_absolute_path "$label" "$value"
[[ "$value" != *:* ]] || fail "Invalid $label: ':' is not allowed in Podman bind-mount source paths."
}
ensure_safe_existing_dir() {
local label="$1"
local dir="$2"
validate_absolute_path "$label" "$dir"
[[ -d "$dir" ]] || fail "Missing $label: $dir"
[[ ! -L "$dir" ]] || fail "Unsafe $label: symlinks are not allowed ($dir)"
}
stat_uid() {
local path="$1"
if stat -f '%u' "$path" >/dev/null 2>&1; then
stat -f '%u' "$path"
else
stat -Lc '%u' "$path"
fi
}
stat_mode() {
local path="$1"
if stat -f '%Lp' "$path" >/dev/null 2>&1; then
stat -f '%Lp' "$path"
else
stat -Lc '%a' "$path"
fi
}
ensure_private_existing_dir_owned_by_user() {
local label="$1"
local dir="$2"
local uid=""
local mode=""
ensure_safe_existing_dir "$label" "$dir"
uid="$(stat_uid "$dir")"
[[ "$uid" == "$(id -u)" ]] || fail "Unsafe $label: not owned by current user ($dir)"
mode="$(stat_mode "$dir")"
(( (8#$mode & 0022) == 0 )) || fail "Unsafe $label: group/other writable ($dir)"
}
ensure_safe_write_file_path() {
local label="$1"
local file="$2"
local dir
validate_absolute_path "$label" "$file"
if [[ -e "$file" ]]; then
[[ ! -L "$file" ]] || fail "Unsafe $label: symlinks are not allowed ($file)"
[[ -f "$file" ]] || fail "Unsafe $label: expected a regular file ($file)"
fi
dir="$(dirname "$file")"
ensure_safe_existing_dir "${label} parent directory" "$dir"
}
write_file_atomically() {
local file="$1"
local mode="$2"
local dir=""
local tmp=""
ensure_safe_write_file_path "output file" "$file"
dir="$(dirname "$file")"
tmp="$(mktemp "$dir/.tmp.XXXXXX")"
cat >"$tmp"
chmod "$mode" "$tmp"
mv -f "$tmp" "$file"
}
validate_port() {
local label="$1"
local value="$2"
local numeric=""
[[ "$value" =~ ^[0-9]{1,5}$ ]] || fail "Invalid $label: must be numeric."
numeric=$((10#$value))
(( numeric >= 1 && numeric <= 65535 )) || fail "Invalid $label: out of range."
}
resolve_user_home() {
local user="$1"
local home=""
if command -v getent >/dev/null 2>&1; then
home="$(getent passwd "$user" 2>/dev/null | cut -d: -f6 || true)"
fi
if [[ -z "$home" && -f /etc/passwd ]]; then
home="$(awk -F: -v u="$user" '$1==u {print $6}' /etc/passwd 2>/dev/null || true)"
fi
if [[ -z "$home" ]]; then
home="/home/$user"
fi
printf '%s' "$home"
}
generate_token_hex_32() {
if command -v openssl >/dev/null 2>&1; then
openssl rand -hex 32
return 0
fi
if command -v python3 >/dev/null 2>&1; then
python3 - <<'PY'
import secrets
print(secrets.token_hex(32))
PY
return 0
fi
if command -v od >/dev/null 2>&1; then
od -An -N32 -tx1 /dev/urandom | tr -d " \n"
return 0
fi
echo "Missing dependency: need openssl or python3 (or od) to generate OPENCLAW_GATEWAY_TOKEN." >&2
exit 1
}
upsert_env_var() {
local file="$1"
local key="$2"
local value="$3"
local tmp
local dir
ensure_safe_write_file_path "env file" "$file"
dir="$(dirname "$file")"
tmp="$(mktemp "$dir/.env.tmp.XXXXXX")"
if [[ -f "$file" ]]; then
awk -v k="$key" -v v="$value" '
BEGIN { found = 0 }
$0 ~ ("^" k "=") { print k "=" v; found = 1; next }
{ print }
END { if (!found) print k "=" v }
' "$file" >"$tmp"
else
printf '%s=%s\n' "$key" "$value" >"$tmp"
fi
mv "$tmp" "$file"
chmod 600 "$file" 2>/dev/null || true
}