#!/bin/sh # Validate an UNTRUSTED model id against the live catalog. # # This is the one gate that stands between an agent-written model string and an # unquoted `--model $MODEL` expansion in the pipelines, so it lives in a single # tested place (see scripts/ci/test-model-selection.sh) rather than inline in a # Woodpecker `commands:` block. Written in POSIX sh (run as `sh`), matching the # other scripts/ci/ helpers added alongside it. # # Echoes the id on stdout IFF it is BOTH: # 1. a single safe token — only [A-Za-z0-9._-] AND not starting with a dash, so # a value carrying a newline or a space, or one that looks like an option # (e.g. "--dangerously-skip-permissions", or # "claude-x\n--dangerously-skip-permissions"), can never pass and then # word-split into an injected CLI flag; and # 2. present in column 1 of the catalog read on stdin (fetch-models.sh --table # output, or a bare id-per-line list). # Otherwise echoes nothing. Always exits 0 so callers can use $(...) under set -e. # The two checks are independent: step 1 alone makes the value safe to pass to a # command line even if a caller supplies no catalog. # # Usage: printf '%s\n' "$TABLE" | valid-model.sh "$candidate" set -eu CAND="${1:-}" # 1. shape check — reject empty, a leading dash (option-like), or any char # outside the safe set. A real model id never starts with '-'. case "$CAND" in ""|-*|*[!A-Za-z0-9._-]*) exit 0 ;; esac # 2. membership check — must be a currently-available id (catalog on stdin). CATALOG="$(cat)" [ -n "$CATALOG" ] || exit 0 printf '%s\n' "$CATALOG" | cut -f1 | grep -qxF -- "$CAND" || exit 0 printf '%s\n' "$CAND"