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
7 changes: 7 additions & 0 deletions src/api/routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1530,6 +1530,13 @@ export function createApp() {
app.get("/v1/app/self-dogfood/registration-pack", async (c) => {
const forbidden = await requireAppRole(c, ["maintainer", "owner", "operator"]);
if (forbidden) return forbidden;
const identity = await authenticateRequestIdentity(c);
const fullName = resolveSelfDogfoodRepoFullName(c.env);
const repo = await getRepository(c.env, fullName);
if (identity?.kind === "session") {
const repoForbidden = await requireSessionRepoAccess(c, identity, fullName, repo);
if (repoForbidden) return repoForbidden;
}
return c.json(await buildSelfDogfoodRegistrationPackResponse(c.env));
});

Expand Down
44 changes: 44 additions & 0 deletions test/unit/routes-self-dogfood-registration-pack.test.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import { describe, expect, it } from "vitest";
import { createApp } from "../../src/api/routes";
import { createSessionForGitHubUser } from "../../src/auth/security";
import { upsertInstallation, upsertRepositoryFromGitHub } from "../../src/db/repositories";
import { createTestEnv } from "../helpers/d1";

const SELF_DOGFOOD_PATH = "/v1/repos/JSONbored/gittensory/self-dogfood-registration-pack";
const APP_SELF_DOGFOOD_PATH = "/v1/app/self-dogfood/registration-pack";

function apiHeaders(env: Env): Record<string, string> {
return {
Expand All @@ -12,6 +14,23 @@ function apiHeaders(env: Env): Record<string, string> {
};
}

async function seedInstalledRepo(env: Env, installationId: number, owner: string, name: string): Promise<void> {
await upsertInstallation(env, {
installation: {
id: installationId,
account: { login: owner, id: installationId, type: "User" },
repository_selection: "selected",
permissions: { metadata: "read", contents: "read" },
events: ["repository"],
},
});
await upsertRepositoryFromGitHub(
env,
{ name, full_name: `${owner}/${name}`, private: false, owner: { login: owner } },
installationId,
);
}

describe("self-dogfood registration-pack route auth", () => {
it("rejects unauthenticated access to the repo-scoped route", async () => {
const app = createApp();
Expand Down Expand Up @@ -43,6 +62,31 @@ describe("self-dogfood registration-pack route auth", () => {
await expect(response.json()).resolves.toMatchObject({ error: "self_dogfood_repo_only", repoFullName: "JSONbored/gittensory" });
});

it("rejects app-route sessions scoped only to an unrelated installed repo", async () => {
const app = createApp();
const env = createTestEnv({ ADMIN_GITHUB_LOGINS: "" });
await seedInstalledRepo(env, 201, "JSONbored", "gittensory");
await seedInstalledRepo(env, 202, "unrelated-owner", "unrelated-repo");
const { token } = await createSessionForGitHubUser(env, { login: "unrelated-owner", id: 202 });
const response = await app.request(APP_SELF_DOGFOOD_PATH, { headers: { cookie: `gittensory_session=${token}` } }, env);
expect(response.status).toBe(403);
await expect(response.json()).resolves.toMatchObject({ error: "forbidden_repo" });
});

it("allows app-route sessions scoped to the configured self-dogfood repo", async () => {
const app = createApp();
const env = createTestEnv({ ADMIN_GITHUB_LOGINS: "" });
await seedInstalledRepo(env, 201, "JSONbored", "gittensory");
const { token } = await createSessionForGitHubUser(env, { login: "JSONbored", id: 201 });
const response = await app.request(APP_SELF_DOGFOOD_PATH, { headers: { cookie: `gittensory_session=${token}` } }, env);
expect(response.status).toBe(200);
await expect(response.json()).resolves.toMatchObject({
kind: "gittensory_self_dogfood_registration_pack",
repoFullName: "JSONbored/gittensory",
privateOnly: true,
});
});

it("allows static-token access to the configured self-dogfood repo", async () => {
const app = createApp();
const env = createTestEnv();
Expand Down