From b6473004ac5a43a02263d78949fbfe7c552b7160 Mon Sep 17 00:00:00 2001 From: Sanaei Date: Tue, 28 Jul 2026 20:11:22 +0200 Subject: [PATCH] fix(ci): harden the conflict resolver against the branch it checks out resolve-conflicts is the one job that puts a pull request's own tree in the working directory while holding CLAUDE_CODE_OAUTH_TOKEN, CLAUDE_BOT_PAT and a write-scoped token, which is what CodeQL alert 101 (actions/untrusted-checkout) points at. Nothing in the job executes that tree and the trigger is gated on the repository owner, so the alert is not reachable as written, but two of its guards were weaker than they read. Git hooks were neutered only after gh pr checkout had already run, so the guard sat one step behind the checkout it exists to cover; it now precedes it. The conflicted paths are concatenated into the --allowedTools value handed to the model, so a path carrying a comma or a parenthesis would widen that allowlist. Only both-modified paths reach that code today, which means they already exist in the base repository, but the merge is now handed back to the maintainer unless every conflicted path is plain [A-Za-z0-9._/-]. The file's header comment block is dropped. --- .github/workflows/claude-bot.yml | 29 +++++++++-------------------- 1 file changed, 9 insertions(+), 20 deletions(-) diff --git a/.github/workflows/claude-bot.yml b/.github/workflows/claude-bot.yml index 1bf92dd1f..3433767db 100644 --- a/.github/workflows/claude-bot.yml +++ b/.github/workflows/claude-bot.yml @@ -1,24 +1,5 @@ name: Claude Bot -# Every prompt: / claude_args: block below interpolates ${{ }}, so GitHub parses -# the whole block scalar as ONE expression and caps it at 21000 characters. -# Going over does not fail a job - the entire workflow stops parsing and -# vanishes from Actions, with the run reported only as a workflow file issue. -# The two triage prompts are the ones to watch: roughly 15100 characters each. -# Put shared context in CLAUDE.md and docs/architecture.md, which are in the -# checkout, instead of pasting it here. -# -# CLAUDE_CODE_SUBPROCESS_ENV_SCRUB=0 on the two jobs that set -# allowed_non_write_users: the action otherwise turns subprocess isolation on -# for them, installs bubblewrap, and every Bash call then dies in the sandbox -# with "bwrap: Can't create file at /home/.mcp.json: Permission denied" before -# the command runs. The job still reports success, so the bot silently answers -# nothing - which is what the "Fail if ..." steps catch. -# -# Only resolve-conflicts may change code, and only the merge it is handed: the -# model there has no shell at all, and the commit and push are done by a -# workflow step from the event payload, never by the model. - on: issues: types: [opened] @@ -815,11 +796,11 @@ jobs: fi base=$(gh pr view "$PR" --json baseRefName --jq '.baseRefName') head=$(gh pr view "$PR" --json headRefName --jq '.headRefName') - gh pr checkout "$PR" git config core.hooksPath /dev/null git config core.quotePath false git config user.name "github-actions[bot]" git config user.email "41898282+github-actions[bot]@users.noreply.github.com" + gh pr checkout "$PR" git fetch origin "$base" if git merge --no-commit --no-ff "origin/${base}"; then git merge --abort 2>/dev/null || true @@ -838,6 +819,14 @@ jobs: git merge --abort 2>/dev/null || true hand_back "The merge of \`${base}\` failed without leaving a conflicted file, so it needs a human. Nothing was changed." fi + odd=$(printf '%s\n' "$files" | grep -vE '^[A-Za-z0-9._][A-Za-z0-9._/-]*$' || true) + if [ -n "$odd" ]; then + git merge --abort 2>/dev/null || true + hand_back "The merge of \`${base}\` conflicts over paths this job refuses to hand to its tooling: + $(printf '%s\n' "$odd" | sed 's/^/- /') + + Nothing was changed. Resolve those by hand." + fi rules="" while IFS= read -r f; do [ -z "$f" ] && continue