GitHub Apps: Least Privilege for Repository Creation

黎 浩然/ 14 10 月, 2026/ 研究生/POSTGRADUATE, 计算机/COMPUTER/ 0 comments

A bot that only creates project repositories should not receive deletion and transfer powers just because those powers come in the same administrative bundle. Start with the smallest business action, select the narrowest permission that supports it, and verify both that the required work succeeds and that unrelated capabilities are absent. A new GitHub App permission makes that separation easier to express.

Contents
  1. What changed for repository creation
  2. Separate the action from the resource boundary
  3. Derive permissions from the endpoint inventory
  4. Find missing and excess capabilities locally
  5. Keep positive and negative acceptance cases
  6. When migration fails, locate the boundary first
  7. Narrow creation access still needs business guardrails
  8. Deliver a migration record that another maintainer can use
  9. Sources and publication note

This article turns that change into a practical review: trace requirements to permissions, find unnecessary capabilities, and check a migration without quietly broadening access. The runnable example inspects fictional operation sets locally. It requests no authorization and creates or deletes no repositories. 阅读中文版.

What changed for repository creation

In its October 13, 2026 update, GitHub introduced repository_creation:write for creating repositories without broader repository administration write access, which also covers management capabilities such as deletion and transfer. An app using the creation permission still needs an organization administrator to install it.

The useful change is a finer authorization boundary. Imagine an internal project portal whose responsibility ends after it creates a repository and returns its link. Project deletion follows a separate human-controlled process. Giving the creation service deletion powers ties together responsibilities that the team intended to keep separate. A dedicated creation permission offers a more direct way to describe the narrower job.

To decide whether the update matters to an existing service, ask why it requested administration access in the first place. If creation was the only reason, there is a specific migration opportunity. If it also performs other administrative work, replacing a permission name is insufficient. Inventory the operations first, then decide which responsibilities can be separated.

Separate the action from the resource boundary

A permission review needs at least three answers: what the bot must do, which resources it may touch, and which identity performs the action. “A GitHub App with write access” leaves the latter two unclear. Even a service with a narrow action can exceed its intended responsibility if its resource scope covers many unrelated repositories.

GitHub’s permission documentation distinguishes user access tokens, constrained by both the user and app permissions, from installation access tokens, whose permissions depend on the app. A successful manual call under a personal identity therefore does not validate the identity used by the production service.

1
Business action: create
2
Endpoint and permission
3
Installation resource scope
4
Verify the actual identity
Original review path: check each boundary instead of skipping directly from a permission name to success.

Treat these as intersecting review conditions: the action belongs to the service, the resource is in scope, the actual identity is permitted, and organizational policy allows the request. This is a way to avoid omissions in a review record, not a complete description of GitHub authorization. Resolve an individual endpoint against its documentation and a controlled acceptance test.

Derive permissions from the endpoint inventory

Write the business acceptance result before selecting permissions. For example: accept a validated project identifier, create a repository with the intended visibility, return its link on success, and leave a traceable error on failure. “Has administration access” is not an acceptance result. It describes a means, and potentially an unnecessarily broad one.

Then list the actual calls. The organization repository creation reference lists acceptable fine-grained permission alternatives, including repository creation write access. Multiple acceptable alternatives do not mean an integration should request all of them. Select the alternative needed for this action and inspect the other actions independently.

If the workflow subsequently writes template content, configures team access, or adds a repository to another installation, give each step its own requirement. Creating a repository does not establish that every later operation is authorized. Conversely, one failing follow-up step is not a reason to give the entire pipeline maximum privileges. The failing action and the corresponding permission decision should remain explainable.

Operation
Narrow
Broad
Create
Allow
Allow
Delete
Deny
Allow
Transfer
Deny
Allow
Original capability diagram: a deliberately simplified policy model, not a live GitHub authorization result.

Find missing and excess capabilities locally

The following Python example is not a GitHub authorization simulator. It uses three invented operation labels to demonstrate a review rule: required minus granted gives missing capabilities; granted minus allowed gives excess capabilities. A real mapping from permission to operations must come from the relevant API documentation and validation, not these three labels.

CREATE = "create_repository"
DELETE = "delete_repository"
TRANSFER = "transfer_repository"
KNOWN = {CREATE, DELETE, TRANSFER}

# Illustrative operation sets, not GitHub API permission names.
NARROW = {CREATE}
BROAD = {CREATE, DELETE, TRANSFER}

def audit(required, granted, allowed):
    required, granted, allowed = map(set, (required, granted, allowed))
    if (required | granted | allowed) - KNOWN:
        raise ValueError("unknown operation")
    return {
        "missing": sorted(required - granted),
        "excess": sorted(granted - allowed),
    }

required = allowed = {CREATE}
assert audit(required, NARROW, allowed) == {
    "missing": [], "excess": []}
assert audit(required, BROAD, allowed) == {
    "missing": [], "excess": [DELETE, TRANSFER]}
assert audit(required, set(), allowed) == {
    "missing": [CREATE], "excess": []}
assert audit(set(), set(), set()) == {
    "missing": [], "excess": []}
try:
    audit({"unknown_action"}, NARROW, allowed)
except ValueError:
    pass
else:
    raise AssertionError("unknown operations must be reviewed")
print("narrow: complete; broad: two excess operations; empty: missing create")

The assertions establish three different outcomes: the narrow set covers creation, the broad set includes two unwanted operations, and an empty grant cannot create. They also cover an empty requirement and an unknown operation. If the only test were “creation succeeds,” both the narrow and broad configurations would pass. The excess-capability check makes the difference visible.

The boundary of this check matters. It knows nothing about organization policy, token type, resource selection, or implicit server-side conditions. Rejecting unknown operations tells a maintainer to extend and review the mapping; it does not prove security. This can support configuration review or continuous integration, but green assertions cannot replace acceptance under the actual runtime identity.

Keep positive and negative acceptance cases

A positive case answers whether the business still works. In a dedicated test organization with test resources, use the migrated runtime identity to create a repository and inspect its name, visibility, and returned resource. This is an acceptance-test recommendation; no real GitHub account was connected or exercised for this article.

A negative case asks whether unnecessary capabilities remain. Begin with configuration and documented capability boundaries. If behavioral validation is necessary, use only explicitly authorized, disposable test resources. Deletion or transfer tests must never target business repositories. “Try deleting it” is not an appropriate production audit method: the purpose of the review is to reduce risk, not create damage to demonstrate a hypothesis.

A rejection also needs interpretation. If the test used a different identity, or a resource outside its scope, that failure alone does not demonstrate that the particular action permission was narrowed. Record the identity type, resource category, expected result, observed result, and API version supporting the expectation. Keep token contents out of that record.

When migration fails, locate the boundary first

A 403 after a change often encourages a quick return to the old permission bundle. Instead, confirm the intended endpoint, runtime identity, installation scope, and approved permission state. The permission documentation notes that installations retain their old permissions until owners approve changes. Updating a registration is therefore not evidence that every installation has completed migration.

Organize diagnosis around one failed request: which business action failed, which endpoint serves it, what identity was used, and which installation and resource scope apply. Retain only the response status and sanitized diagnostic information needed to locate the boundary. Do not log authentication headers. Once the relevant boundary is identified, adjust that decision rather than restoring an unrelated bundle of powers.

Repository creation also has a retry problem. A timeout does not establish that creation failed; an unexamined retry can mix duplicate-operation failures with authorization failures. The site’s idempotent retry article explains result reconciliation. Idempotency and authorization solve different problems and deserve separate validation.

Narrow creation access still needs business guardrails

Permission to create is not a guarantee that the service creates the right repository. A portal that forwards a project name cannot expect the authorization system to decide whether the project was approved, already exists, should be public, or exceeds a team’s repository budget. Those decisions belong to the business workflow and need explicit validation before the call.

Keep input constraints separate from permission review. Derive project identifiers from a defined naming rule. Take visibility from approved project configuration rather than unchecked free text. On a repeated submission, reconcile the existing outcome first. Give bulk creation a reviewable batch limit and stopping condition. There is no universal sensible batch size: choose one from the team’s actual workload rather than inventing a security constant.

These controls answer different questions. Narrow permissions restrict action categories; validation reduces incorrect targets; a quantity boundary limits how far a failed run spreads; result reconciliation prevents retry confusion. None replaces the others. Ending a review after confirming that a bot cannot delete would miss the possibility of repeatedly doing the wrong thing through an allowed action.

Deliver a migration record that another maintainer can use

A handover for the next maintainer should connect required operations, endpoints, minimum-permission evidence, installation scope, runtime identity, and acceptance results. It also identifies old capabilities that are no longer needed. If an operation still requires administrative access, explain the concrete dependency rather than reserving it for an unspecified future use.

Begin rollout within a limited test scope, establish that the new workflow meets its requirements, and then expand applicability deliberately. Define retirement conditions for the old configuration: positive cases pass, authorization state is confirmed, failures can be diagnosed, and the removed capability is no longer a dependency. A rollback can restore an approved previous workflow; it should not leave behind an unowned, permanent extra grant.

The engineering opportunity in this update is a responsibility narrow enough to explain and accept. Let the creation service create. Authorize later work according to its actual requirements. That leaves maintainers better able to answer two practical questions: why does this bot have this capability, and why can it reach this resource?

Sources and publication note

GitHub announcement, October 13, 2026; GitHub App permission selection; organization repository creation API. The operation sets, review workflow, and test recommendations are original engineering analysis, not GitHub’s complete permission model.

Backfilled on the evening of October 14, 2026, retaining the planned 11:30 Beijing article date. The Google News item was timestamped 07:17:55 Beijing that morning, before the planned slot. The article does not backdate information published after that slot.

Support

If the explanations or examples helped you solve a problem or save time, you’re welcome to support this site with a voluntary tip.

WeChat support QR code; click to enlarge
WeChat
Alipay support QR code; click to enlarge
Alipay
Buy Me a Coffee; support this site
Buy Me a Coffee

Click a QR code to enlarge. More options: support page。

Share this Post

Leave a Comment

您的邮箱地址不会被公开。 必填项已用 * 标注

*
*