Skip to content

Use collaborator permission level instead of author_association for integrity filtering #2862

@lpcox

Description

@lpcox

Problem

Integrity filtering currently determines a user's trust level based on the author_association field from GitHub API responses. This field is unreliable for org admins who haven't been explicitly added as collaborators on a specific repository.

How it works today

The Rust guard maps author_association to integrity levels in helpers.rs:960-980:

author_association Integrity Level
OWNER, MEMBER, COLLABORATOR approved (writer)
CONTRIBUTOR, FIRST_TIME_CONTRIBUTOR unapproved (reader)
FIRST_TIMER, NONE none

The bug

GitHub's author_association field reflects the user's direct relationship with the repository, not their effective permissions. An org admin who:

  • Has full admin access to a repo via org ownership
  • But hasn't been explicitly added as a collaborator

...gets author_association: "CONTRIBUTOR" (if they've contributed before) or "NONE" (if they haven't). This maps to unapproved or none integrity, causing their issues, PRs, and comments to be filtered out by the integrity filter.

This is a fundamental limitation of the GitHub API's author_association field — it doesn't know about org-level permissions.

Impact

  • Org admins' content is silently filtered from agentic workflow results
  • Users must work around this by explicitly adding themselves as collaborators, using min-integrity: none, or configuring trusted-users
  • The workarounds defeat the purpose of integrity filtering or require per-user configuration

Upstream issue

See github/gh-aw#23565

Proposed Solution

Supplement author_association with a call to the collaborator permission endpoint:

GET /repos/{owner}/{repo}/collaborators/{username}/permission

This endpoint returns the user's effective permission level, which correctly includes inherited org permissions:

{
  "permission": "admin",
  "user": {
    "login": "example-user",
    "id": 12345
  }
}

Permission-to-integrity mapping

Collaborator Permission Integrity Level
admin, maintain, write approved (writer)
read (triage) unapproved (reader)
none none

Implementation approach

Option A: Gateway-side enrichment (recommended)

Add a new enrichment call in the gateway proxy (internal/proxy/proxy.go) alongside the existing pull_request_read and issue_read enrichment paths:

  1. When the Rust guard needs to determine integrity for an issue/PR author, the gateway calls GET /repos/{owner}/{repo}/collaborators/{username}/permission using the enrichment token
  2. Pass the permission field to the guard alongside (or instead of) author_association
  3. The guard uses permission as the primary signal, falling back to author_association if the permission call fails (e.g., insufficient token scopes)

This fits naturally into the existing restBackendCaller pattern in proxy.go:274-341.

Option B: Guard-side callback

Extend the WASM guard's backend callback interface to support a new get_collaborator_permission call. The guard would call this from tool_rules.rs when labeling issues/PRs, similar to the existing get_pull_request_facts() and get_issue_author_info() callbacks in backend.rs.

Key considerations

  1. Token permissions: The GET /repos/{owner}/{repo}/collaborators/{username}/permission endpoint works with the standard GITHUB_TOKEN in Actions as long as the query is scoped to the same repository (see gh-aw#23565 comment). No additional token scopes needed.

  2. Caching: Permission lookups could be cached per (repo, username) pair for the duration of a session to avoid redundant API calls.

  3. Fallback: If the permission endpoint returns an error (403, 404), fall back to the existing author_association logic so the feature degrades gracefully.

  4. Existing precedent: The gh-aw compiler already uses github.rest.repos.getCollaboratorPermissionLevel() for /slash_command permission checks (see checkRepositoryPermission in gh-aw's check_membership.cjs).

Files that would need changes

  • internal/proxy/proxy.go — Add enrichment call for collaborator permission
  • guards/github-guard/rust-guard/src/labels/backend.rs — Add get_collaborator_permission callback and data structure
  • guards/github-guard/rust-guard/src/labels/helpers.rs — Add collaborator_permission_floor() mapping function
  • guards/github-guard/rust-guard/src/labels/tool_rules.rs — Use permission-based integrity as primary signal for issues/PRs

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions