Files
3x-ui/.github/workflows
Sanaei f46b1726cf fix(ci): close the write paths an audit found still open in the bot
Making the jobs read-only in the previous commit was not enough: two of
the mechanisms that grant write access were invisible in the workflow
file itself.

Every job now passes a `prompt:` input. Without one, claude-code-action
picks tag mode for a mention, and src/modes/tag/index.ts then appends
`--permission-mode acceptEdits`, its own allowedTools including
`Bash(git commit:*)` and a push wrapper, and calls setupBranch. So the
mention job could edit files and commit them no matter what its own
allowedTools said, and its system prompt claiming otherwise was simply
wrong. A `prompt:` selects agent mode, which adds nothing. It also
removes tag mode's hidden requirement that the comment contain the
trigger phrase, which would have made resolve-conflicts a no-op for a
comment that said only "resolve pr conflicts".

resolve-conflicts no longer hands git to the model. `Bash(git:*)` is a
prefix rule, so it permitted `git push origin HEAD:main`, `--force`,
`git remote set-url`, and shell execution through `git config alias.x
'!sh -c ...'` - the action ships scripts/git-push.sh precisely because
`git push:*` allows `--receive-pack='sh -c ...'`. The job now splits in
three: a step checks out the PR branch, merges the base and collects the
conflicted paths; the model gets Read/Glob/Grep/Edit and no shell at all;
a final step verifies and pushes. That step refuses to commit if a
conflict marker survives, if the model wrote /tmp/ABORT, or if anything
outside the conflicted set was touched, and it stages those paths
individually instead of `git add -A`. The PAT is now written to the push
URL only in that last step, after the model's session has ended, instead
of sitting in .git/config while untrusted branch content is read.

The bare `Write` grant in the three answering jobs becomes
`Edit(//tmp/**)`, since only prose kept it out of the checkout and out of
$GITHUB_ACTION_PATH, whose scripts run after the model step. Each prompt
now says to fall back to an inline --body if the write is refused, so a
denied write cannot silently cost a reply. mention gains the transcript
upload and the no-reply guard the other jobs already have, keyed to the
triggering comment's timestamp.

Restores the header note about the 21000-character expression cap, with
the current block sizes.
2026-07-25 22:03:10 +02:00
..