Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions src/github/backfill.ts
Original file line number Diff line number Diff line change
Expand Up @@ -731,9 +731,9 @@ export function enrichInstallationHealth(health: InstallationHealthRecord) {
const requiredPermissions = {
...REQUIRED_INSTALLATION_PERMISSIONS,
...(missingPermissions.has("checks") ? OPTIONAL_CHECK_RUN_PERMISSION : {}),
// pull_requests is missing ONLY when the refresh required write (an acting autonomy) and it was not granted, so
// surface the write requirement in the remediation rather than the baseline read. (#audit-install-health)
...(missingPermissions.has("pull_requests") ? OPTIONAL_PR_WRITE_PERMISSION : {}),
// Persisted health stores only the missing permission name. If pull_requests is already granted at read level,
// a missing pull_requests entry can only mean an acting autonomy needs write; otherwise preserve baseline read.
...(missingPermissions.has("pull_requests") && permissionSatisfies(health.permissions.pull_requests, "read") ? OPTIONAL_PR_WRITE_PERMISSION : {}),
};
return {
...health,
Expand Down
21 changes: 21 additions & 0 deletions test/unit/backfill.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -457,6 +457,27 @@ describe("GitHub backfill", () => {
);
});

it("keeps baseline pull_requests:read remediation when the permission is absent (#audit-install-health least privilege)", () => {
const health = enrichInstallationHealth({
installationId: 127,
accountLogin: "JSONbored",
repositorySelection: "selected",
installedReposCount: 1,
registeredInstalledCount: 1,
status: "needs_attention",
missingPermissions: ["pull_requests"],
missingEvents: [],
permissions: { metadata: "read", issues: "write" },
events: ["issues", "issue_comment", "pull_request", "repository"],
checkedAt: "2026-06-05T00:00:00.000Z",
});

expect(health.requiredPermissions).toMatchObject({ pull_requests: "read" });
expect(health.permissionRemediation).toEqual(
expect.arrayContaining([expect.objectContaining({ permission: "pull_requests", requiredAccess: "read", ok: false, action: "Set repository permission pull_requests to read." })]),
);
});

it("requires Checks write only for repos with check runs enabled", async () => {
const env = createTestEnv({ GITHUB_APP_PRIVATE_KEY: await generatePrivateKeyPem() });
await seedRegisteredRepo(env);
Expand Down
Loading