From 6e34b13a0595b44a27d108882179f8add1775abd Mon Sep 17 00:00:00 2001 From: Agent Date: Sun, 29 Mar 2026 10:21:54 +0000 Subject: [PATCH] =?UTF-8?q?fix:=20dispatcher=20=E2=80=94=20address=20AI=20?= =?UTF-8?q?review=20feedback=20-=20Redact=20secrets=20in=20logs=20(=3D***)?= =?UTF-8?q?=20-=20Fix=20-e=20flags=20before=20service=20name=20in=20docker?= =?UTF-8?q?=20compose=20run=20-=20Use=20FORGE=5FOPS=5FREPO=20for=20cloning?= =?UTF-8?q?=20ops=20repo=20-=20Refresh=20ops=20repo=20in=20each=20poll=20l?= =?UTF-8?q?oop=20iteration=20-=20Use=20array-based=20command=20execution?= =?UTF-8?q?=20to=20prevent=20shell=20injection=20-=20Load=20vault=20secret?= =?UTF-8?q?s=20after=20env.sh=20for=20dispatcher=20access?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docker/edge/dispatcher.sh | 57 ++++++++++++++++++++++++++------------- 1 file changed, 38 insertions(+), 19 deletions(-) diff --git a/docker/edge/dispatcher.sh b/docker/edge/dispatcher.sh index 947e40e..66f438d 100755 --- a/docker/edge/dispatcher.sh +++ b/docker/edge/dispatcher.sh @@ -22,6 +22,20 @@ SCRIPT_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" # Source shared environment source "${SCRIPT_ROOT}/../lib/env.sh" +# Load vault secrets after env.sh (env.sh unsets them for agent security) +# Vault secrets must be available to the dispatcher +if [ -f "$FACTORY_ROOT/.env.vault.enc" ] && command -v sops &>/dev/null; then + set -a + eval "$(sops -d --output-type dotenv "$FACTORY_ROOT/.env.vault.enc" 2>/dev/null)" \ + || echo "Warning: failed to decrypt .env.vault.enc — vault secrets not loaded" >&2 + set +a +elif [ -f "$FACTORY_ROOT/.env.vault" ]; then + set -a + # shellcheck source=/dev/null + source "$FACTORY_ROOT/.env.vault" + set +a +fi + # Ops repo location (vault/actions directory) OPS_REPO_ROOT="${OPS_REPO_ROOT:-/home/debian/disinto-ops}" VAULT_ACTIONS_DIR="${OPS_REPO_ROOT}/vault/actions" @@ -35,7 +49,7 @@ log() { ensure_ops_repo() { if [ ! -d "${OPS_REPO_ROOT}/.git" ]; then log "Cloning ops repo from ${FORGE_OPS_REPO}..." - git clone "${FORGE_WEB}" "${OPS_REPO_ROOT}" + git clone "${FORGE_OPS_REPO}" "${OPS_REPO_ROOT}" else log "Pulling latest ops repo changes..." (cd "${OPS_REPO_ROOT}" && git pull --rebase) @@ -68,24 +82,29 @@ launch_runner() { return 1 fi - # Extract secrets (as space-separated list for env injection) - local secrets - secrets=$(jq -r '.secrets[]? // empty' "$action_file" 2>/dev/null | tr '\n' ' ') + # Extract secrets (array for safe handling) + local -a secrets=() + while IFS= read -r secret; do + [ -n "$secret" ] && secrets+=("$secret") + done < <(jq -r '.secrets[]? // empty' "$action_file" 2>/dev/null) - # Run the formula via docker compose with action ID as argument - # The runner container should be defined in docker-compose.yml - # Secrets are injected via -e flags - local compose_cmd="docker compose run --rm runner ${formula} ${id}" + # Build command array (safe from shell injection) + local -a cmd=(docker compose run --rm runner) - if [ -n "$secrets" ]; then - # Inject secrets as environment variables - for secret in $secrets; do - compose_cmd+=" -e ${secret}=${!secret}" - done - fi + # Add environment variables BEFORE service name + for secret in "${secrets[@]+"${secrets[@]}"}"; do + local secret_val="${!secret:-}" + cmd+=(-e "${secret}=***") # Redact value in the command array + done - log "Running: ${compose_cmd}" - eval "${compose_cmd}" + # Add formula and id as arguments (after service name) + cmd+=("$formula" "$id") + + # Log command skeleton (secrets are redacted) + log "Running: ${cmd[*]}" + + # Execute with array expansion (safe from shell injection) + "${cmd[@]}" log "Runner completed for action: ${id}" } @@ -95,10 +114,10 @@ main() { log "Starting dispatcher..." log "Polling ops repo: ${VAULT_ACTIONS_DIR}" - # Ensure ops repo is available - ensure_ops_repo - while true; do + # Refresh ops repo at the start of each poll cycle + ensure_ops_repo + # Check if actions directory exists if [ ! -d "${VAULT_ACTIONS_DIR}" ]; then log "Actions directory not found: ${VAULT_ACTIONS_DIR}"