fix: verify reconnects through supported GitHub routes and protect active jobs

This commit is contained in:
Thomas Durieux committed 2026-10-07 11:24:59 +00:00
commit e6073941b9
18 files changed
+533 -67

No files matched your search

+5 -4
View File
@@ -191,7 +191,7 @@ router.post("/connections/migrate", async (req, res) => {
const isRepo = type === "repository";
const model = isRepo ? await RepositoryModel.findOne({ repoId: id, owner: user.id }) : await PullRequestModel.findOne({ pullRequestId: id, owner: user.id });
if (!model || ["removed", "archived"].includes(model.status || "")) throw appError("repo_not_found", 404);
if (["preparing", "removing", "expiring"].includes(model.status || "")) throw appError("repository_busy", 409);
if (["preparing", "queue", "download", "removing", "expiring"].includes(model.status || "")) throw appError("repository_busy", 409);
const source = model.source as { repositoryName?: string; repositoryFullName?: string; commit?: string; pullRequestId?: number };
const name = source.repositoryName || source.repositoryFullName || "";
const repositoryId = model.githubAccess?.kind === "github-app" && connection === "github-app"
@@ -201,9 +201,10 @@ router.post("/connections/migrate", async (req, res) => {
// recreated at the same name. Replacing a source requires its own preview.
if (model.githubAccess?.kind === "github-app" && selected.binding.kind === "github-app" &&
model.githubAccess.repositoryId !== selected.binding.repositoryId) throw appError("connection_changed", 409);
const parts = name.split("/").map(encodeURIComponent).join("/");
const repositoryPath = repositoryId === undefined ? `/repos/${parts}` : `/repositories/${repositoryId}`;
await githubRequest(`${repositoryPath}/${isRepo ? `commits/${encodeURIComponent(source.commit || "")}` : `pulls/${source.pullRequestId}`}`, selected.token);
const parts = (selected.fullName || name).split("/").map(encodeURIComponent).join("/");
const repositoryPath = `/repos/${parts}`;
const token = selected.binding.publicRead ? await appUserToken(user.id) : selected.token;
await githubRequest(`${repositoryPath}/${isRepo ? `commits/${encodeURIComponent(source.commit || "")}` : `pulls/${source.pullRequestId}`}`, token);
if (preview === true) return res.json({ eligible: true, connection });
const filter = { _id: model._id, owner: user.id, status: model.status, source: model.source,
githubAccess: model.githubAccess ? model.githubAccess : { $exists: false } };
+6 -1
View File
@@ -112,10 +112,11 @@ router.get(
upstreamStatus,
upstreamBody: upstreamBody?.slice(0, 500),
url: config.STREAMER_ENTRYPOINT
? join(config.STREAMER_ENTRYPOINT, "api/zip")
? join(config.STREAMER_ENTRYPOINT, "api/download")
: undefined,
err: serializeError(err),
});
const streaming = res.headersSent;
handleError(
new AnonymousError(errCode, {
url: req.originalUrl,
@@ -124,6 +125,10 @@ router.get(
}),
res
);
// Once ZIP bytes have been sent, handleError cannot send JSON.
// Terminate the truncated response so the client reports failure
// instead of waiting forever for the ZIP's central directory.
if (streaming && !res.writableEnded) res.destroy();
});
reqStream.pipe(res);
res.on("close", () => {