Pre-launch — Gluecron is in final validation. Public signups and git hosting for non-owner users open after launch review.
CodeIssuesPull RequestsActionsSecurityInsightsSettings
✨ AI
More
Blame · Line-by-line history

wiki-write-access.test.ts

Each line is annotated with the commit that last touched it. Click any SHA to jump to that commit and see the surrounding change.

wiki-write-access.test.tsBlame66 lines · 1 contributor
f0cd0beccantynz-alt1/**
2 * Wiki writes require write access to the repository.
3 *
4 * POST /:owner/:repo/wiki (create) and POST /:owner/:repo/wiki/:slug/edit were
5 * guarded by `requireAuth` alone — "is anyone signed in" — with no ownership
6 * or collaborator check. Any registered account could create pages on, and
7 * overwrite pages of, any repository's wiki. The edit path is destructive: it
8 * UPDATEs title/body in place and bumps `revision`, replacing the owner's
9 * content rather than appending to it.
10 *
11 * The private-repo gate added in app.tsx does NOT cover this: it only blocks
12 * PRIVATE repos, and the bypass worked on public ones.
13 *
14 * delete and revert did check, but as `user.id !== resolved.repo.ownerId`,
15 * which wrongly refuses collaborators and org members with legitimate write
16 * access. All four now route through resolveRepoAccess.
17 */
18
19import { describe, expect, it } from "bun:test";
20import { readFileSync } from "fs";
21
22const SRC = readFileSync("src/routes/wikis.tsx", "utf8");
23
24describe("every mutating wiki route is gated", () => {
25 it("has a write gate at all four POST handlers", () => {
26 const calls = SRC.match(/await denyWikiWrite\(/g) ?? [];
27 expect(calls.length).toBe(4);
28 });
29
30 it("no owner-only comparison survives", () => {
31 // The over-restrictive shape that locked out collaborators.
32 expect(SRC).not.toMatch(/user\.id !== resolved\.repo\.ownerId/);
33 });
34
35 it("gates create before touching the form or inserting", () => {
36 const create = SRC.slice(
37 SRC.indexOf('wikiRoutes.post("/:owner/:repo/wiki", requireAuth'),
38 SRC.indexOf("wikiRoutes.get(\"/:owner/:repo/wiki/:slug\", softAuth")
39 );
40 const gate = create.indexOf("denyWikiWrite");
41 const insert = create.indexOf("db\n .insert(wikiPages)");
42 expect(gate).toBeGreaterThan(-1);
43 if (insert > -1) expect(gate).toBeLessThan(insert);
44 });
45});
46
47describe("the gate itself", () => {
48 const helper = SRC.slice(
49 SRC.indexOf("async function denyWikiWrite"),
50 SRC.indexOf("async function denyWikiWrite") + 900
51 );
52
53 it("requires write, not merely read", () => {
54 // A read-level check would still let any viewer of a public repo edit.
55 expect(helper).toContain('satisfiesAccess(access, "write")');
56 });
57
58 it("derives visibility from the repo row rather than assuming public", () => {
59 expect(helper).toContain("isPublic: !repo.isPrivate");
60 });
61
62 it("returns a redirect for the caller rather than throwing", () => {
63 expect(helper).toContain("return c.redirect(redirectTo)");
64 expect(helper).toContain("return null");
65 });
66});