From 9629c3253e0a56e07b2b0e5624762b4b0a2c1fc9 Mon Sep 17 00:00:00 2001 From: Pallav Vasa Date: Sat, 17 May 2025 11:37:06 +0000 Subject: [PATCH] fix: validate commands, improve remove container logic, standardize logs in gitops router --- gitops_router.sh | 137 ++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 123 insertions(+), 14 deletions(-) diff --git a/gitops_router.sh b/gitops_router.sh index eb1657c..84fb280 100644 --- a/gitops_router.sh +++ b/gitops_router.sh @@ -64,10 +64,10 @@ update() { [[ -f "$out" ]] && chmod 700 "$out" if curl -fsSL "$url" -o "$out"; then - log info "Downloaded $url → $out" + log INFO "Downloaded $url → $out" chmod "$mode" "$out" else - log error "Failed to download $url" + log ERROR "Failed to download $url" return 1 fi } @@ -78,36 +78,145 @@ clean_images() { local dangling dangling="$(podman images -f dangling=true -q)" if [[ -z "$dangling" ]]; then - log info "No dangling images to remove." + log INFO "No dangling images to remove." else - log warn "Removing dangling images..." + log WARN "Removing dangling images..." echo "$dangling" | xargs podman rmi - log info "Dangling images removed." + log INFO "Dangling images removed." + fi +} + +# ───────────────────────────────────────────── +# Remove host podman containers +remove_containers() { + local tokens=("$@") + local flags=() patterns=() containers=() + local valid='^[A-Za-z0-9._-]+$' + + # allow unmatched globs to disappear + shopt -s nullglob + + # separate flags (-f, etc.) from name patterns + for tok in "${tokens[@]}"; do + if [[ "$tok" == -* ]]; then + flags+=("$tok") + else + patterns+=("$tok") + fi + done + + # validate & expand each pattern + for pat in "${patterns[@]}"; do + if [[ ! "$pat" =~ $valid ]]; then + log ERROR "Invalid container name: '$pat'" + shopt -u nullglob + return 1 + fi + containers+=("$pat") + done + + shopt -u nullglob + + if ((${#containers[@]} == 0)); then + log WARN "No containers matched: ${patterns[*]}" + return 0 + fi + + # pass flags *then* containers to podman rm + podman rm "${flags[@]}" "${containers[@]}" +} + +# ───────────────────────────────────────────── +# validate_command [ …] +validate_command() { + local cmd="$1" + shift + local tokens=("$@") + local yaml="$HOME/access.yml" + + # 1) Is command allowed at all? + if [[ "$(yq e ".\"$PERSON\".commands | has(\"$cmd\")" "$yaml")" != "true" ]]; then + log ERROR "Unauthorized command: '$cmd'" + exit 1 + fi + + # 2) Load allowed args for this cmd (may be empty array) + mapfile -t allowed < <(yq e ".\"$PERSON\".commands.${cmd}[]" "$yaml") + + if [[ "${#allowed[@]}" -eq 0 ]]; then + log ERROR "No allowed arguments for command '$cmd' in $yaml" + exit 1 + fi + + # 3) Extract just the non-flag tokens + local args=() + for tok in "${tokens[@]}"; do + [[ "$tok" == -* ]] && continue + args+=("$tok") + done + + if [[ "$cmd" == "remove" ]]; then + # ─ remove: must have at least one arg + if ((${#args[@]} == 0)); then + log ERROR "Command '$cmd' requires at least one argument: ${allowed[*]}" + exit 1 + fi + # Validate each against allowed[] + for a in "${args[@]}"; do + local ok=false + for want in "${allowed[@]}"; do + [[ "$a" == "$want" ]] && ok=true && break + done + if ! $ok; then + log ERROR "Invalid argument '$a' for '$cmd'; allowed: ${allowed[*]}" + exit 1 + fi + done + else + # ─ all other cmds: must have exactly one arg + if ((${#args[@]} != 1)); then + log ERROR "Command '$cmd' requires exactly one argument: ${allowed[*]}" + exit 1 + fi + # And that single arg must be allowed + local a="${args[0]}" + local ok=false + for want in "${allowed[@]}"; do + [[ "$a" == "$want" ]] && ok=true && break + done + if ! $ok; then + log ERROR "Invalid argument '$a' for '$cmd'; allowed: ${allowed[*]}" + exit 1 + fi fi } # ───────────────────────────────────────────── # Entry & command parsing if [[ -z "${SSH_ORIGINAL_COMMAND:-}" ]]; then - log error "No SSH_ORIGINAL_COMMAND provided." + log ERROR "No SSH_ORIGINAL_COMMAND provided." exit 1 fi -log info "SSH_ORIGINAL_COMMAND: $SSH_ORIGINAL_COMMAND" -read -r cmd arg <<<"$SSH_ORIGINAL_COMMAND" +log INFO "SSH_ORIGINAL_COMMAND: $SSH_ORIGINAL_COMMAND" +read -ra parts <<<"$SSH_ORIGINAL_COMMAND" +cmd="${parts[0]}" +args=("${parts[@]:1}") + +validate_command "$cmd" "${args[@]}" # ───────────────────────────────────────────── # Dispatch case "$cmd" in build) - case "$arg" in + case "${args[0]}" in base) run build-base.sh ;; workspace) run build-workspace.sh ;; - *) log error "build: invalid arg '$arg'" ;; + *) log ERROR "build: invalid arg '${args[0]}'" ;; esac ;; update) - case "$arg" in + case "${args[0]}" in base) update build-base.sh .local/bin 500 ;; workspace) update build-workspace.sh .local/bin 500 ;; access) update access.yml . 400 ;; @@ -115,14 +224,14 @@ update) gitops_router) update gitops_router.sh .local/bin 500 ;; home_tar) update home.tar.gz . 500 media ;; gitconfig) update gitconfig.template . 500 ;; - *) log error "update: invalid arg '$arg'" ;; + *) log ERROR "update: invalid arg '${args[0]}'" ;; esac ;; clean) clean_images ;; status) podman images ;; -remove) podman rm "$arg" ;; +remove) remove_containers "${args[@]}" ;; *) - log error "Unknown command: '$cmd'" + log ERROR "Unknown command: '$cmd'" exit 127 ;; esac