From 8ec656c2018dce4ca0341d6b171e3cfff6cad892 Mon Sep 17 00:00:00 2001 From: tdurieux Date: Sun, 6 Sep 2026 09:16:57 +0200 Subject: [PATCH] fix: authorize access before returning cached GitHub metadata --- src/core/source/GitHubRepository.ts | 6 +++--- test/production-regressions.test.js | 10 ++++++++++ 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/src/core/source/GitHubRepository.ts b/src/core/source/GitHubRepository.ts index 2fa0564..599d381 100644 --- a/src/core/source/GitHubRepository.ts +++ b/src/core/source/GitHubRepository.ts @@ -259,9 +259,6 @@ export async function getRepositoryFromGitHub(opt: { }) : null; } - if (dbModel && !opt.force) { - return new GitHubRepository(dbModel); - } const oct = octokit(opt.accessToken); let r: RestEndpointMethodTypes["repos"]["get"]["response"]["data"]; // Recover the numeric GitHub repository id from `externalId` (stored as @@ -372,6 +369,9 @@ export async function getRepositoryFromGitHub(opt: { repo: opt.repo, }, }); + // Cached private data is usable only after this token passed repos.get. + if (dbModel && !opt.force) return new GitHubRepository(dbModel); + let pageSource: | RestEndpointMethodTypes["repos"]["getPages"]["response"]["data"]["source"] | undefined; diff --git a/test/production-regressions.test.js b/test/production-regressions.test.js index 3d369f7..24dc22c 100644 --- a/test/production-regressions.test.js +++ b/test/production-regressions.test.js @@ -65,6 +65,16 @@ describe("production regressions", function () { const error = await new Promise(resolve => passport._strategy("github")._verify("token", "", { id: "new-id", username: "recycled" }, resolve)); expect(error.message).to.equal("not_connected"); }); + it("checks GitHub authorization before returning shared cached metadata", async function () { + const CachedRepoModel = require("../src/core/model/repositories/repositories.model").default; + stub(db, "isConnected", true); + stub(CachedRepoModel, "findOne", async () => ({ name: "owner/private", branches: [{ readme: "secret" }] })); + stub(gh, "octokit", () => ({ repos: { get: async () => { throw Object.assign(new Error("Forbidden"), { status: 403 }); } } })); + try { + await require("../src/core/source/GitHubRepository").getRepositoryFromGitHub({ owner: "owner", repo: "private", accessToken: "unauthorized" }); + throw new Error("expected rejection"); + } catch (error) { expect(error.message).to.equal("token_expired"); } + }); it("omits an upstream length when later text is rewritten", async function () { const File = require("../src/core/AnonymizedFile").default; stub(config, "STREAMER_ENTRYPOINT", "");