Skip to content

fix(#5188): re-trigger review via label after fix-agent push#5551

Open
ggallen wants to merge 1 commit into
fullsend-ai:mainfrom
ggallen:fix/5188-relabel-review-on-fix-push
Open

fix(#5188): re-trigger review via label after fix-agent push#5551
ggallen wants to merge 1 commit into
fullsend-ai:mainfrom
ggallen:fix/5188-relabel-review-on-fix-push

Conversation

@ggallen

@ggallen ggallen commented Jul 23, 2026

Copy link
Copy Markdown
Member

What this fixes

After the fix agent pushes a commit to a PR, the review agent is never re-dispatched (#5188). pull_request_target.synchronize fires on that push, but the dispatch routing's actor-identity check is gated on PR_USER_LOGIN — the PR's original author, which for agent-authored PRs is always the code agent's bot account, regardless of who actually triggered this fix run. GitHub App bots have no collaborator role, so that check always fails closed.

The fix

post-fix.sh now removes then re-adds the ready-for-review label after a successful push, forcing a fresh labeled webhook event. That path has no actor-authorization gate at all — applying a label already requires write access, so no separate identity check is needed — mirroring post-code.sh's existing handling of the PR-open case. GitHub does not fire a new labeled event when a label already present is simply re-added, hence the remove-then-add sequence.

Why this approach, not identity recognition

This supersedes #5415, which attempted the same fix by extending an actor-identity-recognition function (is_org_bot()) across several dispatch-authorization gates. @ifireball closed that approach out during review: it reintroduced a design — recognizing bots by name for dispatch authorization — that issue #2669 had already evaluated and explicitly rejected in favor of label-based gating ("actors can be spoofed and the approach is fragile across workflow changes"). PR #2679 already implemented that decision and shipped it for the retro→triage handoff, closing #2636.

The label-based fix here has some concrete advantages the identity-based approach didn't:

  • No change needed to the CEL-based dispatch path (internal/harnessdispatch) — its IsAuthorized() already trusts label-added events unconditionally, so this fix is already consistent there with zero Go changes.
  • No forge-specific bot-identity logic — labels work identically on GitHub and GitLab; GitHub's [bot]-suffixed App-login convention has no GitLab equivalent at all, so an identity-matching approach would need an entirely separate mechanism per forge.
  • No new spoofing surface — nothing here depends on trusting a login string.

#2636 needs no further work — it's already fixed by #2679.

What's not in this PR

A separate, unrelated bug that #5415 also touched — the fix agent's own review-body content-attribution lookups (in reusable-fix.yml, reusable-dispatch.yml, and pre-fetch-prior-review.sh) missing the shared fullsend-ai-review[bot] identity — is tracked independently as #5550 (found by @waynesun09 during #5415's review). It's a content-attribution question ("whose review text do I trust"), not dispatch authorization, so it's unaffected by the labels-vs-identity discussion here and doesn't belong in this PR.

#5463 and #5480 (identity-hardening follow-ups filed during #5415's review) are no longer relevant to dispatch authorization now that this PR doesn't use is_org_bot() for that purpose; they may still be relevant once #5550 is addressed.

Test plan

  • bash internal/scaffold/fullsend-repo/scripts/post-fix-test.sh — new test cases cover the remove/add label sequence tolerating either call failing without the script exiting nonzero.
  • go build ./... and go test ./internal/scaffold/... — no regressions.

Fixes #5188. References #2636, #2669, #2679, #5463, #5480, #5550. Supersedes #5415.

cc @ifireball @waynesun09 — this replaces #5415 per the discussion there; see the closing comment on that PR for the full rationale.

@ggallen
ggallen requested a review from a team as a code owner July 23, 2026 22:04
@ggallen
ggallen requested review from ifireball and waynesun09 July 23, 2026 22:05
@qodo-code-review

Copy link
Copy Markdown

PR Summary by Qodo

Re-trigger review by relabeling ready-for-review after fix-agent push

🐞 Bug fix 🧪 Tests 🕐 20-40 Minutes

Grey Divider

AI Description

• Toggle ready-for-review after fix-agent pushes to force a fresh labeled webhook.
• Avoid brittle actor-identity authorization on pull_request_target.synchronize for bot-authored
 PRs.
• Add shell-level regression tests for relabel sequencing and failure-tolerance behavior.
Diagram

graph TD
  A["Fix agent workflow"] --> B["post-fix.sh"] --> C["gh pr edit: remove+add label"] --> D["GitHub PR labels"] --> E["issues.labeled event"] --> F["Dispatch router"] --> G["Review agent"]
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Expand actor-identity authorization (bot allowlist)
  • ➕ Keeps dispatching on pull_request_target.synchronize without extra label churn
  • ➕ No need to perform additional GitHub API calls post-push
  • ➖ Brittle/forge-specific: bot logins differ across GitHub App vs GitLab and can change
  • ➖ Reintroduces a spoofing/maintenance surface area the project has previously avoided
  • ➖ Touches multiple auth gates/paths and is harder to reason about end-to-end
2. Dispatch review directly from post-fix (workflow_dispatch/repository_dispatch)
  • ➕ Deterministic re-run without relying on label webhook semantics
  • ➕ Can encode explicit context (PR number, head SHA) in the dispatch payload
  • ➖ Adds/expands privileged dispatch mechanisms in a security-sensitive script
  • ➖ Requires additional workflow plumbing and secrets/permissions review
  • ➖ May diverge from existing label-gated control-plane conventions
3. Change synchronize authorization to key off trigger source (not PR author)
  • ➕ Fixes the root cause for synchronize-based dispatch without label manipulation
  • ➕ More semantically correct than PR author checks when agents push
  • ➖ Still requires robust, forge-aware identity provenance and careful threat modeling
  • ➖ Likely requires Go/CEL path changes across dispatch implementations
  • ➖ Higher blast radius than the label-toggle approach

Recommendation: Prefer the PR’s label-toggle approach: it aligns with existing label-gated dispatch behavior (already trusted by the labeled-event path), avoids fragile bot-identity recognition, and keeps the change localized to post-fix.sh with explicit failure tolerance so a successful fix push is not blocked by a relabel hiccup.

Files changed (2) +103 / -6

Bug fix (1) +30 / -6
post-fix.shToggle ready-for-review label after push to re-dispatch review +30/-6

Toggle ready-for-review label after push to re-dispatch review

• Adds a new post-push step that removes then re-adds the 'ready-for-review' label to force a fresh 'issues.labeled' webhook. The remove is best-effort (silent on failure) and the add emits a warning but does not fail the run, matching the goal of not blocking a successful fix push on dispatch retriggering.

internal/scaffold/fullsend-repo/scripts/post-fix.sh

Tests (1) +73 / -0
post-fix-test.shAdd regression tests for relabel re-trigger behavior +73/-0

Add regression tests for relabel re-trigger behavior

• Introduces a test helper that mirrors post-fix.sh’s remove-then-add 'ready-for-review' sequence. Adds cases covering remove failure, add failure, and both failing, ensuring the script never hard-fails and only warns when re-add fails.

internal/scaffold/fullsend-repo/scripts/post-fix-test.sh

@fullsend-ai-review

fullsend-ai-review Bot commented Jul 23, 2026

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 10:06 PM UTC · Completed 10:22 PM UTC
Commit: 6644a49 · View workflow run →

@github-actions

github-actions Bot commented Jul 23, 2026

Copy link
Copy Markdown

Site preview

Preview: https://33428969-site.fullsend-ai.workers.dev

Commit: de825b7bdcbbdb5a7771c4b70d5688609efbbe23

@codecov

codecov Bot commented Jul 23, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@qodo-code-review

qodo-code-review Bot commented Jul 23, 2026

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (0) 📘 Rule violations (0) 📜 Skill insights (0)

Context used
✅ Compliance rules (platform): 61 rules

Grey Divider


Action required

1. GH_TOKEN exported too late ✓ Resolved 🐞 Bug ≡ Correctness
Description
In post-fix.sh, the new gh pr edit ... --remove-label/--add-label relabel block runs before
export GH_TOKEN="${PUSH_TOKEN}", so on runners without preexisting gh auth the relabel calls
will fail and the review re-dispatch won’t occur. This undermines the intended fix for #5188 and is
hard to diagnose because stderr is suppressed on both calls.
Code

internal/scaffold/fullsend-repo/scripts/post-fix.sh[R354-366]

+if [ "${NO_PUSH}" = "false" ]; then
+  gh pr edit "${PR_NUMBER}" --repo "${REPO_FULL_NAME}" \
+    --remove-label "ready-for-review" 2>/dev/null || true
+  gh pr edit "${PR_NUMBER}" --repo "${REPO_FULL_NAME}" \
+    --add-label "ready-for-review" 2>/dev/null || \
+    echo "::warning::Failed to re-apply ready-for-review label to PR #${PR_NUMBER} — review will not be re-dispatched"
+fi
+
+# ---------------------------------------------------------------------------
+# 6. Process structured output (fix-result.json)
# ---------------------------------------------------------------------------
export GH_TOKEN="${PUSH_TOKEN}"
Relevance

⭐⭐⭐ High

Repo precedent: authenticate gh calls early; unauthenticated gh api failures were fixed
similarly.

PR-#2346

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The relabel block executes at lines 354–360, but GH_TOKEN is only exported at line 365, so those
gh calls don’t see the token. The repo’s analogous logic in post-code.sh exports/sets GH_TOKEN
before any gh operations, indicating the intended pattern.

internal/scaffold/fullsend-repo/scripts/post-fix.sh[354-366]
internal/scaffold/fullsend-repo/scripts/post-code.sh[403-407]
internal/scaffold/fullsend-repo/scripts/post-code.sh[87-88]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`post-fix.sh` invokes `gh pr edit` to remove/re-add `ready-for-review` before `GH_TOKEN` is exported. On a GitHub Actions runner, `gh` typically relies on `GH_TOKEN` for non-interactive auth; if it’s not set yet, the relabel step will fail and the review agent won’t be re-dispatched.

### Issue Context
This block was added specifically to force a new `labeled` webhook event after a successful fix push. If the relabel calls can’t authenticate, the workflow silently regresses back to “no re-dispatch after fix push”.

### Fix Focus Areas
- internal/scaffold/fullsend-repo/scripts/post-fix.sh[354-366]

### Suggested fix
Move `export GH_TOKEN="${PUSH_TOKEN}"` to *before* the relabel block, or explicitly scope the token per command:
- `GH_TOKEN="${PUSH_TOKEN}" gh pr edit ... --remove-label ...`
- `GH_TOKEN="${PUSH_TOKEN}" gh pr edit ... --add-label ...`

(Prefer the export earlier so later `gh` calls remain consistent.)

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

2. Remove-label errors fully hidden ✓ Resolved 🐞 Bug ☼ Reliability
Description
The relabel logic unconditionally ignores all --remove-label failures (2>/dev/null || true) even
though removal is required to force a new labeled event when the label already exists. If removal
fails for an unexpected reason (API/permission/transient failure) while the label remains present,
the subsequent add may be ineffective and the review won’t be re-dispatched, with no warning
indicating the remove step failed.
Code

internal/scaffold/fullsend-repo/scripts/post-fix.sh[R355-357]

+  gh pr edit "${PR_NUMBER}" --repo "${REPO_FULL_NAME}" \
+    --remove-label "ready-for-review" 2>/dev/null || true
+  gh pr edit "${PR_NUMBER}" --repo "${REPO_FULL_NAME}" \
Relevance

⭐⭐ Medium

Team sometimes prefers surfacing gh failures, but this PR explicitly treats remove failure as
silent/acceptable.

PR-#279
PR-#1487

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The script explicitly states removal is required to force a new labeled event, but the remove
command’s stderr is discarded and failures are always ignored. Since post-code.sh always adds
ready-for-review on PR creation, the label is often already present, making remove a critical step
of the intended re-trigger path.

internal/scaffold/fullsend-repo/scripts/post-fix.sh[351-357]
internal/scaffold/fullsend-repo/scripts/post-code.sh[535-544]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
The relabel mechanism depends on successfully removing `ready-for-review` first, but the script suppresses stderr and always swallows the remove command’s failure. That hides unexpected errors which can prevent the desired re-dispatch while leaving no signal in logs.

### Issue Context
The script’s own comment states removal is necessary to force a new `labeled` event when the label is already present. Also, `post-code.sh` applies the `ready-for-review` label on PR creation, so it’s common for the label to already exist when a fix run occurs.

### Fix Focus Areas
- internal/scaffold/fullsend-repo/scripts/post-fix.sh[351-359]

### Suggested fix
Keep the operation non-fatal, but distinguish expected vs unexpected failures:
- Capture stderr for the remove call, and if it fails with something other than "label not present" / 404, emit a `::warning::` noting that re-dispatch may not occur.
- Alternatively, check whether the label is currently present via `gh pr view --json labels` and only attempt removal when present; if removal fails, warn.

Do not make the script exit non-zero; just improve detection/observability for unexpected failures.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Qodo Logo

Comment thread internal/scaffold/fullsend-repo/scripts/post-fix.sh
Comment thread internal/scaffold/fullsend-repo/scripts/post-fix.sh Outdated
@fullsend-ai-review

fullsend-ai-review Bot commented Jul 23, 2026

Copy link
Copy Markdown

Review

Findings

Low

  • [gha-workflow-command-injection] internal/scaffold/fullsend-repo/scripts/post-fix.shPR_NUMBER is interpolated unsanitized into ::notice:: and ::warning:: GHA workflow commands in the relabel step. PR_NUMBER is a GitHub-assigned integer set by the workflow caller and is not attacker-controllable in practice, so injection risk is negligible. The error message body (SANITIZED_ERR) is properly sanitized via tr '\n' ' ' | sed 's/::/: /g'. Not a regression — the new code follows the same conventions as the rest of the file.
Previous run

Review

Findings

High

  • [api-contract] internal/scaffold/fullsend-repo/scripts/post-fix.sh:359 — The new step 5 (relabel re-trigger) calls gh pr edit before export GH_TOKEN="${PU..." (which only happens at the start of step 6, line ~367). The gh CLI will use whatever ambient GH_TOKEN the GitHub Actions runner inherits — in the per-org install model, this is github.token scoped to the config repository, not the target repository. The --remove-label and --add-label calls will fail with a permissions error. Because both failures are suppressed (|| true and || echo "::warning::..."), the feature silently does nothing — the review agent is never re-dispatched after a fix push, which is the entire purpose of this PR. In post-code.sh, export GH_TOKEN="${PU..." appears before the label application, so that script works correctly.
    Remediation: Move export GH_TOKEN="${PU..." from its current position (start of step 6) to before step 5, so the relabel gh calls authenticate with the app-minted token that has pull-requests:write on the target repo. This matches the pattern in post-code.sh.

Low

  • [gha-workflow-command-injection] internal/scaffold/fullsend-repo/scripts/post-fix.sh:362PR_NUMBER is interpolated into a ::warning:: GHA workflow command without sanitization for :: sequences or %0A/%0D encoded newlines. While PR_NUMBER is a GitHub-assigned integer and not attacker-controllable, this is inconsistent with defense-in-depth. Not a regression — matches pre-existing patterns elsewhere in the file.

Labels: PR fixes dispatch re-triggering for review agent after fix-agent push; modifies post-fix script in scaffold

fullsend-ai-review[bot]

This comment was marked as outdated.

@fullsend-ai-review fullsend-ai-review Bot added component/dispatch Workflow dispatch and triggers bug labels Jul 23, 2026

@waynesun09 waynesun09 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review squad pass (Claude x2 + Grok + Gemini, 4 agents). fullsend-ai-review[bot] already correctly flagged a HIGH bug (label re-trigger calls run before GH_TOKEN is exported to PUSH_TOKEN, so they silently use the wrong ambient token and the whole fix no-ops) — not re-reported here.

Five new findings this round:

  • HIGH: a second, even-quieter failure mode — if --remove-label fails for a transient reason (not just "label already absent"), the subsequent --add-label is idempotent and succeeds silently, so the warning never fires and there's zero log signal that re-dispatch didn't happen.
  • HIGH: the new tests (perform_relabel_retrigger) reimplement the label-call logic in an isolated stub rather than exercising the real script, so they pass green today despite the real script having the already-confirmed GH_TOKEN bug — the test suite cannot catch this class of bug.
  • MEDIUM (premature-decision): the core claim that GitHub doesn't refire the labeled webhook when re-adding an already-present label is asserted with no citation and no test; the cited precedents (post-code.sh, #2679) only ever add a label that was absent, so they don't actually validate this specific remove-then-add-when-present behavior.
  • MEDIUM: the cancel-in-progress concurrency guard on the fix workflow can interrupt the remove/add sequence mid-flight, dropping the label with no automatic recovery until the next fix push.
  • MEDIUM: the test stub doesn't validate argument construction, so an argument-order/quoting bug would pass silently.

Full detail inline. Given the compounding failure modes around the already-known GH_TOKEN bug, recommend addressing at least the two HIGH findings before merge.

Comment thread internal/scaffold/fullsend-repo/scripts/post-fix.sh
Comment thread internal/scaffold/fullsend-repo/scripts/post-fix-test.sh
Comment thread internal/scaffold/fullsend-repo/scripts/post-fix.sh
Comment thread internal/scaffold/fullsend-repo/scripts/post-fix.sh
Comment thread internal/scaffold/fullsend-repo/scripts/post-fix-test.sh
pull_request_target.synchronize fires when the fix agent pushes, but
its actor-identity authorization check is gated on PR_USER_LOGIN — the
PR's original author, which for agent-authored PRs is the code agent's
bot account regardless of who triggered this fix run. GitHub App bots
have no collaborator role, so that check always fails closed and
review is never re-dispatched after a fix-agent push (fullsend-ai#5188).

post-fix.sh now removes then re-adds the ready-for-review label after
a successful push, forcing a fresh labeled webhook event. That path
has no actor-authorization gate at all — label application itself
already requires write access, so it needs no separate identity
check — mirroring post-code.sh's identical handling of the PR-open
case. GitHub does not fire a new labeled event when a label already
present is re-added, hence the remove-then-add sequence.

This supersedes fullsend-ai#5415, which attempted the same fix via an
actor-identity-recognition function (is_org_bot()) extended across
several dispatch-authorization gates. That approach was closed after
review: it reintroduced a design (recognizing bots by name for
dispatch authorization) that issue fullsend-ai#2669 had already evaluated and
rejected in favor of label-based gating — "actors can be spoofed and
the approach is fragile across workflow changes" — a decision PR
fullsend-ai#2679 already implemented and shipped for the retro-to-triage handoff
(closing fullsend-ai#2636). The label-based fix here needs no changes to the
CEL-based dispatch path (internal/harnessdispatch), which already
trusts label-added events unconditionally, and needs no
forge-specific bot-identity logic, since labels work identically
across GitHub and GitLab.

A separate, unrelated bug that fullsend-ai#5415 also touched — the fix agent's
own review-body content-attribution lookups missing the shared
fullsend-ai-review[bot] identity — is tracked independently as fullsend-ai#5550,
since it is not a dispatch-authorization question and is unaffected
by this change.

Signed-off-by: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: Greg Allen <gallen@redhat.com>
@ggallen
ggallen force-pushed the fix/5188-relabel-review-on-fix-push branch from 6644a49 to de825b7 Compare July 24, 2026 01:21
@fullsend-ai-review

fullsend-ai-review Bot commented Jul 24, 2026

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 1:23 AM UTC · Completed 1:37 AM UTC
Commit: de825b7 · View workflow run →

@fullsend-ai-review
fullsend-ai-review Bot dismissed their stale review July 24, 2026 01:37

Superseded by updated review

@fullsend-ai-review fullsend-ai-review Bot added the ready-for-merge All reviewers approved — ready to merge label Jul 24, 2026
@rh-hemartin

Copy link
Copy Markdown
Member

Which is the relation with #5536?

@waynesun09 waynesun09 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review squad round 2 (Claude x2 + Grok + Gemini, 4 agents). All 6 round-1 findings verified genuinely fixed via direct re-inspection and mutation testing — GH_TOKEN export ordering, the sanitized ::notice:: on remove-label failure, the new structural ordering test, the honest unverified-assumption comment, the documented concurrency window, and the exact-match gh-invocation test assertions all hold up.

This round's mutation testing (deliberately mutating the fixed code to see if the new tests would catch it) surfaced:

  • CRITICAL (test-only): the new run_gh_token_ordering_test is a static grep-based line-position check, not execution-based — commenting out the GH_TOKEN export, or wrapping it in a subshell where export doesn't propagate, both preserve the detected line ordering and pass the test while leaving the real authentication bug in place.
  • MEDIUM: sanitization of the remove-label error message handles literal newlines and :: but misses percent-encoded %0A/%0D escapes, inconsistent with three sibling scripts in the same directory (post-retro.sh, install-precommit-tools.sh, extract-transcript-error.sh) that already strip these.
  • Two LOW items: missing \r stripping in the same sanitization, and the new structural test's greps are unscoped to the relabel section specifically (works today only because the matched strings happen to be unique in the file).

Since the CRITICAL is a gap in test rigor rather than the shipped fix itself, and this PR already has two approvals, I don't think this needs to block merge — but the test gap is worth closing in a fast follow-up given it directly concerns the regression test for the bug this very PR fixed. Full detail inline.


local remove_line token_export_line
remove_line="$(grep -n -- '--remove-label "ready-for-review"' "${post_fix_sh}" | head -1 | cut -d: -f1)"
token_export_line="$(grep -n 'export GH_TOKEN="\${PUSH_TOKEN}"' "${post_fix_sh}" | head -1 | cut -d: -f1)"

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[critical] test-coverage

Confirmed via direct mutation: run_gh_token_ordering_test is a pure line-position grep check, not an execution-based test. It can be satisfied while GH_TOKEN never actually reaches the gh calls:

  1. Commenting out the export (# export GH_TOKEN="${PUSH_TOKEN}") — grep doesn't care about #, so token_export_line still resolves to the same line, ordering check passes, and the test reports PASS: gh-token-ordering even though GH_TOKEN is never exported and every gh call runs unauthenticated (the exact class of bug this test exists to catch).
  2. Wrapping the export in a subshell (( export GH_TOKEN="${PUSH_TOKEN}" )) — same result: line position unchanged, test passes, but export inside ( ... ) never propagates to the parent shell, so downstream gh calls are still unauthenticated.

Both mutations reproduce the real-world failure this test was added to prevent, and both get a clean pass.

Suggested fix: make this execution-based rather than static. E.g., source or bash -c the relevant script region with PUSH_TOKEN set to a sentinel and assert [ "$GH_TOKEN" = "$PUSH_TOKEN" ] is true in the same shell right before the relabel block would run. At minimum, tighten the grep to ^export GH_TOKEN= (rejecting comments) and reject matches inside parens/braces — though execution-based verification is the robust fix, since a static grep will always have some blind spot to control-flow that doesn't change line position.

# Expected when the label wasn't present (e.g. a human-authored PR that
# never went through post-code.sh); still worth a breadcrumb since an
# unexpected API/permission failure looks identical from here.
SANITIZED_ERR="$(tr '\n' ' ' < "${REMOVE_LABEL_ERR}" | sed 's/::/: /g')"

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[medium] sanitization-gap

SANITIZED_ERR="$(tr '\n' ' ' < "${REMOVE_LABEL_ERR}" | sed 's/::/: /g')" handles literal \n and literal ::, but doesn't strip percent-encoded %0A/%0D (or lowercase %0a/%0d) sequences. Confirmed: printf '%0A::error::fake injected error%0A' | tr '\n' ' ' | sed 's/::/: /g'%0A: error: fake injected error%0A, i.e. the percent-encoded escapes pass through untouched.

This is inconsistent with the established pattern in this same directory — post-retro.sh, install-precommit-tools.sh, and extract-transcript-error.sh all already strip all four case variants of %0A/%0D as part of their GHA workflow-command sanitization, specifically because API error bodies can legitimately contain percent-encoded control characters that downstream consumers may reinterpret as real newlines/CRs — the same ::-prefix injection class this sanitization otherwise defends against. This PR's sanitization leaves that line incomplete relative to its own siblings.

Suggested fix: strip the same four variants used elsewhere, e.g. via SAFE:=${SAFE//%0A/} / ${SAFE//%0a/} / ${SAFE//%0D/} / ${SAFE//%0d/} before or alongside the existing tr/sed pipeline, and add a test case with a poison string like "HTTP 500%0A::error::spoofed%0D" to lock it in.

# Expected when the label wasn't present (e.g. a human-authored PR that
# never went through post-code.sh); still worth a breadcrumb since an
# unexpected API/permission failure looks identical from here.
SANITIZED_ERR="$(tr '\n' ' ' < "${REMOVE_LABEL_ERR}" | sed 's/::/: /g')"

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[low] sanitization-completeness

The pipeline strips \n but not \r. A crafted gh stderr containing a bare carriage return would pass through unmodified into the ::notice:: line. This doesn't create a new workflow-command injection (the runner's parser keys off \n as the line delimiter, and :: pairs are already neutralized by the adjacent fix), but some terminal/log renderers interpret bare \r as "return to column 0," which could let attacker-controlled stderr visually overwrite/obscure the ::notice:: prefix in a rendered log view. Minor readability/spoofing concern, not a security boundary.

Suggested fix: extend to tr -d '\r' | tr '\n' ' ' | sed 's/::/: /g' (or a broader control-character scrub) to make the sanitization conceptually complete, alongside the %0A/%0D fix above.

post_fix_sh="${script_dir}/post-fix.sh"

local remove_line token_export_line
remove_line="$(grep -n -- '--remove-label "ready-for-review"' "${post_fix_sh}" | head -1 | cut -d: -f1)"

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[low] test-robustness

Both greps match against the whole file, not scoped to the relabel section specifically — they work today only because each literal string (--remove-label "ready-for-review" and export GH_TOKEN="${PUSH_TOKEN}") happens to be unique in post-fix.sh right now (confirmed via grep -c, both count 1). If a future unrelated change adds a second occurrence of either string elsewhere in the file (e.g., an earlier idempotent cleanup step, or a narrower-scoped second GH_TOKEN export), head -1 would silently start comparing the wrong pair of lines and could pass even if the real relabel-step ordering regressed.

Suggested fix: scope the greps to the # 5. Re-trigger review section explicitly (e.g. extract the line range via awk '/^# 5\. Re-trigger review/,/^# 6\./' first, then grep within that slice) so the test stays correct if the file grows additional matches of either literal elsewhere.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug component/dispatch Workflow dispatch and triggers ready-for-merge All reviewers approved — ready to merge

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Review agent skipped on bot-authored PRs due to collaborator permission check failing for GitHub App accounts

3 participants