mirror of
https://github.com/OpenRouterTeam/spawn.git
synced 2026-05-19 08:01:17 +00:00
fix(security): harden shell scripts - fix sed portability, curl HTTPS enforcement, token expiry (#1917)
- MEDIUM: Validate flyctl auth status before empty FLY_API_TOKEN fallback in provision.sh (fail fast instead of silent failure) - LOW: Fix sed -i portability in qa.sh (use sed -i.bak for macOS compat) - LOW: Increase FLY_API_TOKEN expiry from 2h to 8h in common.sh - LOW: Add --proto '=https' to all curl -L calls in digitalocean scripts (6 files) to prevent HTTP downgrade on redirects Fixes #1913 Agent: code-health Co-authored-by: B <6723574+louisgv@users.noreply.github.com> Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
9d7175bc1b
commit
4994c28594
9 changed files with 35 additions and 23 deletions
|
|
@ -197,8 +197,9 @@ if [[ "${RUN_MODE}" == "quality" ]]; then
|
|||
fi
|
||||
cat "$PROMPT_TEMPLATE" > "${PROMPT_FILE}"
|
||||
|
||||
sed -i "s|WORKTREE_BASE_PLACEHOLDER|${WORKTREE_BASE}|g" "${PROMPT_FILE}"
|
||||
sed -i "s|REPO_ROOT_PLACEHOLDER|${REPO_ROOT}|g" "${PROMPT_FILE}"
|
||||
sed -i.bak "s|WORKTREE_BASE_PLACEHOLDER|${WORKTREE_BASE}|g" "${PROMPT_FILE}"
|
||||
sed -i.bak "s|REPO_ROOT_PLACEHOLDER|${REPO_ROOT}|g" "${PROMPT_FILE}"
|
||||
rm -f "${PROMPT_FILE}.bak"
|
||||
|
||||
elif [[ "${RUN_MODE}" == "fixtures" ]]; then
|
||||
PROMPT_TEMPLATE="${SCRIPT_DIR}/qa-fixtures-prompt.md"
|
||||
|
|
@ -208,8 +209,9 @@ elif [[ "${RUN_MODE}" == "fixtures" ]]; then
|
|||
fi
|
||||
cat "$PROMPT_TEMPLATE" > "${PROMPT_FILE}"
|
||||
|
||||
sed -i "s|WORKTREE_BASE_PLACEHOLDER|${WORKTREE_BASE}|g" "${PROMPT_FILE}"
|
||||
sed -i "s|REPO_ROOT_PLACEHOLDER|${REPO_ROOT}|g" "${PROMPT_FILE}"
|
||||
sed -i.bak "s|WORKTREE_BASE_PLACEHOLDER|${WORKTREE_BASE}|g" "${PROMPT_FILE}"
|
||||
sed -i.bak "s|REPO_ROOT_PLACEHOLDER|${REPO_ROOT}|g" "${PROMPT_FILE}"
|
||||
rm -f "${PROMPT_FILE}.bak"
|
||||
|
||||
elif [[ "${RUN_MODE}" == "issue" ]]; then
|
||||
PROMPT_TEMPLATE="${SCRIPT_DIR}/qa-issue-prompt.md"
|
||||
|
|
@ -219,9 +221,10 @@ elif [[ "${RUN_MODE}" == "issue" ]]; then
|
|||
fi
|
||||
cat "$PROMPT_TEMPLATE" > "${PROMPT_FILE}"
|
||||
|
||||
sed -i "s|ISSUE_NUM_PLACEHOLDER|${ISSUE_NUM}|g" "${PROMPT_FILE}"
|
||||
sed -i "s|WORKTREE_BASE_PLACEHOLDER|${WORKTREE_BASE}|g" "${PROMPT_FILE}"
|
||||
sed -i "s|REPO_ROOT_PLACEHOLDER|${REPO_ROOT}|g" "${PROMPT_FILE}"
|
||||
sed -i.bak "s|ISSUE_NUM_PLACEHOLDER|${ISSUE_NUM}|g" "${PROMPT_FILE}"
|
||||
sed -i.bak "s|WORKTREE_BASE_PLACEHOLDER|${WORKTREE_BASE}|g" "${PROMPT_FILE}"
|
||||
sed -i.bak "s|REPO_ROOT_PLACEHOLDER|${REPO_ROOT}|g" "${PROMPT_FILE}"
|
||||
rm -f "${PROMPT_FILE}.bak"
|
||||
|
||||
elif [[ "${RUN_MODE}" == "e2e" ]]; then
|
||||
PROMPT_TEMPLATE="${SCRIPT_DIR}/qa-e2e-prompt.md"
|
||||
|
|
@ -231,8 +234,9 @@ elif [[ "${RUN_MODE}" == "e2e" ]]; then
|
|||
fi
|
||||
cat "$PROMPT_TEMPLATE" > "${PROMPT_FILE}"
|
||||
|
||||
sed -i "s|WORKTREE_BASE_PLACEHOLDER|${WORKTREE_BASE}|g" "${PROMPT_FILE}"
|
||||
sed -i "s|REPO_ROOT_PLACEHOLDER|${REPO_ROOT}|g" "${PROMPT_FILE}"
|
||||
sed -i.bak "s|WORKTREE_BASE_PLACEHOLDER|${WORKTREE_BASE}|g" "${PROMPT_FILE}"
|
||||
sed -i.bak "s|REPO_ROOT_PLACEHOLDER|${REPO_ROOT}|g" "${PROMPT_FILE}"
|
||||
rm -f "${PROMPT_FILE}.bak"
|
||||
|
||||
fi
|
||||
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ _MAX_RETRIES=3
|
|||
_ensure_bun() {
|
||||
if command -v bun &>/dev/null; then return 0; fi
|
||||
printf '\033[0;36mInstalling bun...\033[0m\n' >&2
|
||||
curl -fsSL --show-error https://bun.sh/install | bash >/dev/null || { printf '\033[0;31mFailed to install bun\033[0m\n' >&2; exit 1; }
|
||||
curl -fsSL --proto '=https' --show-error https://bun.sh/install | bash >/dev/null || { printf '\033[0;31mFailed to install bun\033[0m\n' >&2; exit 1; }
|
||||
export PATH="$HOME/.bun/bin:$PATH"
|
||||
command -v bun &>/dev/null || { printf '\033[0;31mbun not found after install\033[0m\n' >&2; exit 1; }
|
||||
}
|
||||
|
|
@ -71,7 +71,7 @@ fi
|
|||
# Remote — download bundled digitalocean.js from GitHub release
|
||||
DO_JS=$(mktemp)
|
||||
trap 'rm -f "$DO_JS"' EXIT
|
||||
curl -fsSL "https://github.com/OpenRouterTeam/spawn/releases/download/digitalocean-latest/digitalocean.js" -o "$DO_JS" \
|
||||
curl -fsSL --proto '=https' "https://github.com/OpenRouterTeam/spawn/releases/download/digitalocean-latest/digitalocean.js" -o "$DO_JS" \
|
||||
|| { printf '\033[0;31mFailed to download digitalocean.js\033[0m\n' >&2; exit 1; }
|
||||
|
||||
_run_with_restart bun run "$DO_JS" "$_AGENT_NAME" "$@"
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ _MAX_RETRIES=3
|
|||
_ensure_bun() {
|
||||
if command -v bun &>/dev/null; then return 0; fi
|
||||
printf '\033[0;36mInstalling bun...\033[0m\n' >&2
|
||||
curl -fsSL --show-error https://bun.sh/install | bash >/dev/null || { printf '\033[0;31mFailed to install bun\033[0m\n' >&2; exit 1; }
|
||||
curl -fsSL --proto '=https' --show-error https://bun.sh/install | bash >/dev/null || { printf '\033[0;31mFailed to install bun\033[0m\n' >&2; exit 1; }
|
||||
export PATH="$HOME/.bun/bin:$PATH"
|
||||
command -v bun &>/dev/null || { printf '\033[0;31mbun not found after install\033[0m\n' >&2; exit 1; }
|
||||
}
|
||||
|
|
@ -71,7 +71,7 @@ fi
|
|||
# Remote — download bundled digitalocean.js from GitHub release
|
||||
DO_JS=$(mktemp)
|
||||
trap 'rm -f "$DO_JS"' EXIT
|
||||
curl -fsSL "https://github.com/OpenRouterTeam/spawn/releases/download/digitalocean-latest/digitalocean.js" -o "$DO_JS" \
|
||||
curl -fsSL --proto '=https' "https://github.com/OpenRouterTeam/spawn/releases/download/digitalocean-latest/digitalocean.js" -o "$DO_JS" \
|
||||
|| { printf '\033[0;31mFailed to download digitalocean.js\033[0m\n' >&2; exit 1; }
|
||||
|
||||
_run_with_restart bun run "$DO_JS" "$_AGENT_NAME" "$@"
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ _MAX_RETRIES=3
|
|||
_ensure_bun() {
|
||||
if command -v bun &>/dev/null; then return 0; fi
|
||||
printf '\033[0;36mInstalling bun...\033[0m\n' >&2
|
||||
curl -fsSL --show-error https://bun.sh/install | bash >/dev/null || { printf '\033[0;31mFailed to install bun\033[0m\n' >&2; exit 1; }
|
||||
curl -fsSL --proto '=https' --show-error https://bun.sh/install | bash >/dev/null || { printf '\033[0;31mFailed to install bun\033[0m\n' >&2; exit 1; }
|
||||
export PATH="$HOME/.bun/bin:$PATH"
|
||||
command -v bun &>/dev/null || { printf '\033[0;31mbun not found after install\033[0m\n' >&2; exit 1; }
|
||||
}
|
||||
|
|
@ -71,7 +71,7 @@ fi
|
|||
# Remote — download bundled digitalocean.js from GitHub release
|
||||
DO_JS=$(mktemp)
|
||||
trap 'rm -f "$DO_JS"' EXIT
|
||||
curl -fsSL "https://github.com/OpenRouterTeam/spawn/releases/download/digitalocean-latest/digitalocean.js" -o "$DO_JS" \
|
||||
curl -fsSL --proto '=https' "https://github.com/OpenRouterTeam/spawn/releases/download/digitalocean-latest/digitalocean.js" -o "$DO_JS" \
|
||||
|| { printf '\033[0;31mFailed to download digitalocean.js\033[0m\n' >&2; exit 1; }
|
||||
|
||||
_run_with_restart bun run "$DO_JS" "$_AGENT_NAME" "$@"
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ _MAX_RETRIES=3
|
|||
_ensure_bun() {
|
||||
if command -v bun &>/dev/null; then return 0; fi
|
||||
printf '\033[0;36mInstalling bun...\033[0m\n' >&2
|
||||
curl -fsSL --show-error https://bun.sh/install | bash >/dev/null || { printf '\033[0;31mFailed to install bun\033[0m\n' >&2; exit 1; }
|
||||
curl -fsSL --proto '=https' --show-error https://bun.sh/install | bash >/dev/null || { printf '\033[0;31mFailed to install bun\033[0m\n' >&2; exit 1; }
|
||||
export PATH="$HOME/.bun/bin:$PATH"
|
||||
command -v bun &>/dev/null || { printf '\033[0;31mbun not found after install\033[0m\n' >&2; exit 1; }
|
||||
}
|
||||
|
|
@ -71,7 +71,7 @@ fi
|
|||
# Remote — download bundled digitalocean.js from GitHub release
|
||||
DO_JS=$(mktemp)
|
||||
trap 'rm -f "$DO_JS"' EXIT
|
||||
curl -fsSL "https://github.com/OpenRouterTeam/spawn/releases/download/digitalocean-latest/digitalocean.js" -o "$DO_JS" \
|
||||
curl -fsSL --proto '=https' "https://github.com/OpenRouterTeam/spawn/releases/download/digitalocean-latest/digitalocean.js" -o "$DO_JS" \
|
||||
|| { printf '\033[0;31mFailed to download digitalocean.js\033[0m\n' >&2; exit 1; }
|
||||
|
||||
_run_with_restart bun run "$DO_JS" "$_AGENT_NAME" "$@"
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ _MAX_RETRIES=3
|
|||
_ensure_bun() {
|
||||
if command -v bun &>/dev/null; then return 0; fi
|
||||
printf '\033[0;36mInstalling bun...\033[0m\n' >&2
|
||||
curl -fsSL --show-error https://bun.sh/install | bash >/dev/null || { printf '\033[0;31mFailed to install bun\033[0m\n' >&2; exit 1; }
|
||||
curl -fsSL --proto '=https' --show-error https://bun.sh/install | bash >/dev/null || { printf '\033[0;31mFailed to install bun\033[0m\n' >&2; exit 1; }
|
||||
export PATH="$HOME/.bun/bin:$PATH"
|
||||
command -v bun &>/dev/null || { printf '\033[0;31mbun not found after install\033[0m\n' >&2; exit 1; }
|
||||
}
|
||||
|
|
@ -71,7 +71,7 @@ fi
|
|||
# Remote — download bundled digitalocean.js from GitHub release
|
||||
DO_JS=$(mktemp)
|
||||
trap 'rm -f "$DO_JS"' EXIT
|
||||
curl -fsSL "https://github.com/OpenRouterTeam/spawn/releases/download/digitalocean-latest/digitalocean.js" -o "$DO_JS" \
|
||||
curl -fsSL --proto '=https' "https://github.com/OpenRouterTeam/spawn/releases/download/digitalocean-latest/digitalocean.js" -o "$DO_JS" \
|
||||
|| { printf '\033[0;31mFailed to download digitalocean.js\033[0m\n' >&2; exit 1; }
|
||||
|
||||
_run_with_restart bun run "$DO_JS" "$_AGENT_NAME" "$@"
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ _MAX_RETRIES=3
|
|||
_ensure_bun() {
|
||||
if command -v bun &>/dev/null; then return 0; fi
|
||||
printf '\033[0;36mInstalling bun...\033[0m\n' >&2
|
||||
curl -fsSL --show-error https://bun.sh/install | bash >/dev/null || { printf '\033[0;31mFailed to install bun\033[0m\n' >&2; exit 1; }
|
||||
curl -fsSL --proto '=https' --show-error https://bun.sh/install | bash >/dev/null || { printf '\033[0;31mFailed to install bun\033[0m\n' >&2; exit 1; }
|
||||
export PATH="$HOME/.bun/bin:$PATH"
|
||||
command -v bun &>/dev/null || { printf '\033[0;31mbun not found after install\033[0m\n' >&2; exit 1; }
|
||||
}
|
||||
|
|
@ -71,7 +71,7 @@ fi
|
|||
# Remote — download bundled digitalocean.js from GitHub release
|
||||
DO_JS=$(mktemp)
|
||||
trap 'rm -f "$DO_JS"' EXIT
|
||||
curl -fsSL "https://github.com/OpenRouterTeam/spawn/releases/download/digitalocean-latest/digitalocean.js" -o "$DO_JS" \
|
||||
curl -fsSL --proto '=https' "https://github.com/OpenRouterTeam/spawn/releases/download/digitalocean-latest/digitalocean.js" -o "$DO_JS" \
|
||||
|| { printf '\033[0;31mFailed to download digitalocean.js\033[0m\n' >&2; exit 1; }
|
||||
|
||||
_run_with_restart bun run "$DO_JS" "$_AGENT_NAME" "$@"
|
||||
|
|
|
|||
|
|
@ -83,7 +83,7 @@ require_env() {
|
|||
# Check / generate FLY_API_TOKEN
|
||||
if [ -z "${FLY_API_TOKEN:-}" ]; then
|
||||
log_info "FLY_API_TOKEN not set, generating via flyctl..."
|
||||
FLY_API_TOKEN=$(flyctl tokens create org personal --expiry 2h 2>/dev/null || true)
|
||||
FLY_API_TOKEN=$(flyctl tokens create org personal --expiry 8h 2>/dev/null || true)
|
||||
if [ -z "${FLY_API_TOKEN:-}" ]; then
|
||||
log_warn "Could not generate token. Falling back to flyctl stored credentials."
|
||||
# Validate flyctl is authenticated
|
||||
|
|
@ -93,7 +93,7 @@ require_env() {
|
|||
fi
|
||||
else
|
||||
export FLY_API_TOKEN
|
||||
log_ok "Generated FLY_API_TOKEN (expires in 2h)"
|
||||
log_ok "Generated FLY_API_TOKEN (expires in 8h)"
|
||||
fi
|
||||
fi
|
||||
|
||||
|
|
|
|||
|
|
@ -36,6 +36,14 @@ provision_agent() {
|
|||
# Environment for headless provisioning
|
||||
# FLY_API_TOKEN="" forces spawn to use flyctl stored credentials (see plan section 6)
|
||||
# MODEL_ID bypasses the interactive model selection prompt (required by openclaw)
|
||||
#
|
||||
# Validate flyctl is authenticated before proceeding with empty token fallback
|
||||
if [ -z "${FLY_API_TOKEN:-}" ]; then
|
||||
if ! flyctl auth whoami >/dev/null 2>&1; then
|
||||
log_err "FLY_API_TOKEN is empty and flyctl is not authenticated. Run: flyctl auth login"
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
(
|
||||
SPAWN_NON_INTERACTIVE=1 \
|
||||
SPAWN_SKIP_GITHUB_AUTH=1 \
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue