From 80d3b020201e868150ecd51c4172311b71383706 Mon Sep 17 00:00:00 2001 From: Sphia Sadek Date: Wed, 15 Jul 2026 19:30:36 -0400 Subject: [PATCH] fix(review-gate): sanitize Idempotency-Key to API-allowed charset MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Task API validates Idempotency-Key against ^[a-zA-Z0-9_-]{1,128}$, but the gate built it as `review-$REPO-$PR_NUMBER-$HEAD_SHA` where $REPO is `owner/repo` — the "/" fails validation (and owner/repo + a 40-char SHA can exceed 128), so the webhook returned 400 VALIDATION_ERROR and no review was ever triggered on a green PR. Map disallowed chars to "-" and cap at 128; still per-(repo,PR,SHA) unique. Verified live: bad key → 400, sanitized key → 201 (task created). --- .github/workflows/review-gate.yml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/.github/workflows/review-gate.yml b/.github/workflows/review-gate.yml index 89b999b9..a63557c9 100644 --- a/.github/workflows/review-gate.yml +++ b/.github/workflows/review-gate.yml @@ -334,12 +334,18 @@ jobs: # openssl output is "(stdin)= " or "SHA2-256(stdin)= "; take last. SIG="$(printf '%s' "$POST_BODY" | openssl dgst -sha256 -hmac "$ABCA_WEBHOOK_SECRET" | awk '{print $NF}')" + # The Task API validates Idempotency-Key against ^[a-zA-Z0-9_-]{1,128}$. + # $REPO carries a "/" (owner/repo), so map every disallowed char to "-" + # and cap at 128. Still per-(repo,PR,SHA) unique, which is all we need. + IDEMPOTENCY_KEY="$(printf 'review-%s-%s-%s' "$REPO" "$PR_NUMBER" "$HEAD_SHA" \ + | tr -c 'a-zA-Z0-9_-' '-' | cut -c1-128)" + HTTP_CODE="$(curl -sS -o /tmp/abca-resp.txt -w '%{http_code}' \ -X POST "${ABCA_TASK_API_URL%/}/webhooks/tasks" \ -H 'Content-Type: application/json' \ -H "X-Webhook-Id: $ABCA_WEBHOOK_ID" \ -H "X-Webhook-Signature: sha256=$SIG" \ - -H "Idempotency-Key: review-$REPO-$PR_NUMBER-$HEAD_SHA" \ + -H "Idempotency-Key: $IDEMPOTENCY_KEY" \ --data-raw "$POST_BODY")" if [[ "$HTTP_CODE" =~ ^2 ]]; then