From d56ed7ab5fea91f89b936da0fb69ea23f750496f Mon Sep 17 00:00:00 2001 From: Bob Killen Date: Fri, 9 Oct 2026 10:25:21 -0500 Subject: [PATCH] fix(owners): read a 0-byte OWNERS file as listing nobody GitHub returns content "" for an empty blob; the truthiness check on content rejected it as invalid. Check for undefined instead so the empty file reaches parseOwners. Fixes #404 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Signed-off-by: Bob Killen --- __tests__/utils/auth.test.ts | 1 + __tests__/utils/ownersAuth.test.ts | 7 +++++++ dist/index.js | 4 ++-- src/utils/auth.ts | 2 +- src/utils/owners.ts | 2 +- 5 files changed, 12 insertions(+), 4 deletions(-) diff --git a/__tests__/utils/auth.test.ts b/__tests__/utils/auth.test.ts index c021d51..82d60fe 100644 --- a/__tests__/utils/auth.test.ts +++ b/__tests__/utils/auth.test.ts @@ -483,6 +483,7 @@ describe('authorization policies', () => { ['a root OWNERS reviewer', { owners: 'reviewers:\n - alice\n' }, { anyone: true, collaborators: false, members: false, trusted: true }], ['a root OWNERS approver', { owners: 'approvers:\n - alice\n' }, { anyone: true, collaborators: false, members: false, trusted: true }], ['nobody in particular', { owners: 'approvers:\n - bob\n' }, { anyone: true, collaborators: false, members: false, trusted: false }], + ['nobody, with a 0-byte root OWNERS file', { owners: '' }, { anyone: true, collaborators: false, members: false, trusted: false }], ['nobody, with no root OWNERS file', {}, { anyone: true, collaborators: false, members: false, trusted: false }], ] as const)('%s', async (_, who, want) => { const fixture: { users?: readonly string[], member?: boolean, collaborator?: boolean, owners?: string } = who diff --git a/__tests__/utils/ownersAuth.test.ts b/__tests__/utils/ownersAuth.test.ts index 47712db..e78afde 100644 --- a/__tests__/utils/ownersAuth.test.ts +++ b/__tests__/utils/ownersAuth.test.ts @@ -297,6 +297,13 @@ describe('assertAuthorizedByOwnersOrMembership on a pull request', () => { ) }) + it('treats a 0-byte OWNERS file as listing nobody', async () => { + server.use(...prHandlers({ OWNERS: '' }, ['src/file1.txt'])) + await expect(authorize('approvers', 'alice')).rejects.toThrow( + 'alice is not an approver for any changed file', + ) + }) + it('requests the tree recursively and only fetches OWNERS blobs in ancestor directories', async () => { const observeTree = new utils.ObserveRequest() const observeDocsBlob = new utils.ObserveRequest() diff --git a/dist/index.js b/dist/index.js index 2755fe7..bf71401 100644 --- a/dist/index.js +++ b/dist/index.js @@ -41941,7 +41941,7 @@ function isOwnersPath(path) { } function decode(data, path) { const file = data; - if (!file.content || !file.encoding) { + if (file.content === undefined || !file.encoding) { throw new Error(`invalid OWNERS file returned from GitHub API for ${path}`); } return external_node_buffer_.Buffer.from(file.content, file.encoding).toString(); @@ -42528,7 +42528,7 @@ async function retrieveOwnersFile(octokit, context) { } throw new Error(`error checking for an OWNERS file at the root of the repository: ${e}`); } - if (!data.content || !data.encoding) { + if (data.content === undefined || !data.encoding) { throw new Error(`invalid OWNERS file returned from GitHub API: ${data}`); } const decoded = external_node_buffer_.Buffer.from(data.content, data.encoding).toString(); diff --git a/src/utils/auth.ts b/src/utils/auth.ts index b537230..6e887fa 100644 --- a/src/utils/auth.ts +++ b/src/utils/auth.ts @@ -490,7 +490,7 @@ async function retrieveOwnersFile( ) } - if (!data.content || !data.encoding) { + if (data.content === undefined || !data.encoding) { throw new Error(`invalid OWNERS file returned from GitHub API: ${data}`) } diff --git a/src/utils/owners.ts b/src/utils/owners.ts index 35eead4..52c8306 100644 --- a/src/utils/owners.ts +++ b/src/utils/owners.ts @@ -160,7 +160,7 @@ function isOwnersPath(path: string): boolean { function decode(data: unknown, path: string): string { const file = data as { content?: string, encoding?: string } - if (!file.content || !file.encoding) { + if (file.content === undefined || !file.encoding) { throw new Error(`invalid OWNERS file returned from GitHub API for ${path}`) } return Buffer.from(file.content, file.encoding as BufferEncoding).toString()