Skip to content

Trust an agent-review marker from anyone with write access, not only by author_association. Closes #137 - #138

Merged
plamber merged 2 commits into
mainfrom
fix/agent-review-private-membership
Sep 30, 2026
Merged

plamber merged 2 commits into
mainfrom
fix/agent-review-private-membership

Conversation

@easylife-agents

@easylife-agents easylife-agents Bot commented Sep 30, 2026 •

Copy link
Copy Markdown
Contributor

Closes #137.

github.token is not an org member, so GitHub reports a private org member as CONTRIBUTOR and pr-agent-review.mjs discarded their review (Identity#1123, EasyLifeBertolla, org admin).

A marker-bearing review from a person whose association is not trusted now falls back to GET /collaborators/{login}/permission; admin/write counts. One lookup per login, only for marker reviews, fails closed on 403/5xx/network errors (with a warning). Bot reviewers are never looked up.

Verified

Run on a throwaway branch with the callers' exact permissions block (contents: read, pull-requests: read, statuses: write), using github.token:

Login permission
EasyLifeBertolla (private org admin) admin
plamber admin
octocat (no grant, public repo) read
github-actions[bot], easylife-agents[bot] none

So the token is allowed to call the endpoint, a private member with write is accepted, and read-only and bot logins are not. The probe branch is deleted.

Changed after review

  • d096c7b answers all five findings: skip the lookup unless review.user.type === 'User', read permission instead of role_name, drop the undocumented bot claim from the comment, rewrap the readme paragraph. The AMBER about unproven token access is answered by the table above.

Left unaddressed on purpose: read-only public members are already trusted by association (pre-existing), and a stub test for hasWriteAccess (there is no test harness for this script yet).

The red call-workflow-lint / actionlint job fails on every recent PR in this repo: actionlint 1.7.12 does not know job.workflow_sha, in files this PR does not touch. It is not a required check.

🤖 Generated with Claude Code

…by author_association. Closes #137

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@easylife-agents
easylife-agents Bot marked this pull request as ready for review September 30, 2026 17:08

@plamber plamber 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.

No RED. Trust widening is sound; two AMBER to close before merge.

Finding Where
🟠 Bots reach the permission lookup; skip it unless user.type === 'User' scripts/pr-agent-review.mjs:340
🟠 Premise unproven with the callers' real contents: read token; add run evidence to the PR scripts/pr-agent-review.mjs:286
⚪ role_name ?? permission rejects custom write-based roles; read permission scripts/pr-agent-review.mjs:297
⚪ Comment claims bots get a 404; not documented scripts/pr-agent-review.mjs:274
⚪ Added readme sentences overrun the wrap readme.md:106

Five findings posted; left out: MEMBER read-only over-trust (pre-existing), extra API cost (bounded, fails red), and a stub test for hasWriteAccess.

Good: every error path leaves the review untrusted, and trusted associations never trigger a lookup.


Generated by Claude Code

Comment thread scripts/pr-agent-review.mjs Outdated
const hasMarker = MARKER.test(review.body || '');
MARKER.lastIndex = 0;
if (!hasMarker) continue;
if (await hasWriteAccess(entry.by)) all.push(entry);

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.

🟠 App bots have association NONE/CONTRIBUTOR, so they now always reach this lookup. I expect a 404, but that is undocumented. Guard with review.user?.type === 'User' so bot handling stops depending on endpoint behaviour (claude[bot] posts PR-steered output via pr_code_review.yml).

let result = false;
try {
const response = await fetch(
`${cfg.apiUrl}/repos/${owner}/${repo}/collaborators/${encodeURIComponent(login)}/permission`,

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.

🟠 The endpoint's prose says the caller needs push access. Metadata:read is implicit, so it should work with contents: read, but that is unproven. If it 403s the fix silently does nothing. Put run evidence in the PR: a private member with write accepted, a read-only member rejected, using the callers' permissions block.

Comment thread scripts/pr-agent-review.mjs Outdated
);
if (response.ok) {
const body = await response.json();
result = WRITE_ROLES.has(body.role_name ?? body.permission);

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.

⚪ role_name is always present, so ?? never falls back. A custom role based on write gets its own name, not in WRITE_ROLES, and is rejected. permission is the legacy field (admin/write/read/none; maintain→write, triage→read), so body.permission === 'admin' || body.permission === 'write' is as tight and has no false negatives.

Comment thread scripts/pr-agent-review.mjs Outdated
// is asked, and write or above counts, which is what "OWNER, MEMBER or COLLABORATOR" was standing
// in for.
//
// Fails closed: a 404 (not a collaborator, or a bot login), a 403 or any other error leaves the

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.

⚪ "or a bot login" is not documented behaviour of this endpoint (GitHub documents it working for bots). Drop the claim; the type === 'User' guard is what excludes bots.

Comment thread readme.md Outdated

**Who can satisfy it.** The marker is accepted only from a **review** (not a plain comment) by
someone with `OWNER`, `MEMBER` or `COLLABORATOR` standing. A pull request author *may* satisfy it
someone with `OWNER`, `MEMBER` or `COLLABORATOR` standing, or with write access to the repository. The second route is there because the check reads with `github.token`, which sees a *private* org member as `CONTRIBUTOR`; the reviewer's repository permission does not depend on whether their membership is public. A pull request author *may* satisfy it

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.

⚪ The added sentences make this one very long line and the next line is not rewrapped. Rewrap the paragraph to the file's ~100 columns.

…ion field, and rewrap the readme. Closes #137

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@easylife-agents
easylife-agents Bot requested a review from plamber September 30, 2026 17:14
@plamber
plamber merged commit 4a3f400 into main Sep 30, 2026
10 of 12 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

agent-review ignores reviews from private org members

1 participant