fix(prd-1): skip actor check on Forgejo (no bot accounts, avoids read:user scope)

This commit is contained in:
Vlad Mocanu committed 2026-02-25 19:40:24 +02:00
1 parent 6eb2e41510
commit ead98f44f9
2 files changed
+72 -7

No files matched your search

+10 -3
View File
@@ -7,11 +7,21 @@
import type { Octokit } from "@octokit/rest"; import type { Octokit } from "@octokit/rest";
import type { GitHubContext } from "../context"; import type { GitHubContext } from "../context";
import { detectPlatform } from "../../platform/detector";
export async function checkHumanActor( export async function checkHumanActor(
octokit: Octokit, octokit: Octokit,
githubContext: GitHubContext, githubContext: GitHubContext,
) { ) {
// Forgejo doesn't have GitHub-style bot/app accounts, and the user API
// requires read:user scope which may not be available. Skip entirely.
if (detectPlatform() === "forgejo") {
console.log(
`Skipping human actor check on Forgejo for: ${githubContext.actor}`,
);
return;
}
// Fetch user information from GitHub API // Fetch user information from GitHub API
const { data: userData } = await octokit.users.getByUsername({ const { data: userData } = await octokit.users.getByUsername({
username: githubContext.actor, username: githubContext.actor,
@@ -21,9 +31,6 @@ export async function checkHumanActor(
console.log(`Actor type: ${actorType}`); console.log(`Actor type: ${actorType}`);
// On Forgejo, the user API doesn't return a `type` field (undefined).
// Treat undefined/empty type as a human user — Forgejo doesn't have
// GitHub-style bot/app accounts.
if (actorType && actorType !== "User") { if (actorType && actorType !== "User") {
const allowedBots = githubContext.inputs.allowedBots; const allowedBots = githubContext.inputs.allowedBots;
+62 -4
View File
@@ -1,6 +1,6 @@
#!/usr/bin/env bun #!/usr/bin/env bun
import { describe, test, expect } from "bun:test"; import { describe, test, expect, beforeEach, afterEach } from "bun:test";
import { checkHumanActor } from "../src/github/validation/actor"; import { checkHumanActor } from "../src/github/validation/actor";
import type { Octokit } from "@octokit/rest"; import type { Octokit } from "@octokit/rest";
import { createMockContext } from "./mockContext"; import { createMockContext } from "./mockContext";
@@ -94,8 +94,8 @@ describe("checkHumanActor", () => {
); );
}); });
test("should treat undefined actor type as human (Forgejo compatibility)", async () => { test("should treat undefined actor type as human (GitHub compatibility)", async () => {
// Forgejo's user API doesn't return a `type` field // Edge case: GitHub API returns no `type` field
const mockOctokit = { const mockOctokit = {
users: { users: {
getByUsername: async () => ({ getByUsername: async () => ({
@@ -104,10 +104,68 @@ describe("checkHumanActor", () => {
}, },
} as unknown as Octokit; } as unknown as Octokit;
const context = createMockContext(); const context = createMockContext();
context.actor = "forgejo-user"; context.actor = "unknown-user";
await expect( await expect(
checkHumanActor(mockOctokit, context), checkHumanActor(mockOctokit, context),
).resolves.toBeUndefined(); ).resolves.toBeUndefined();
}); });
describe("Forgejo platform", () => {
let savedPlatform: string | undefined;
let savedServerUrl: string | undefined;
beforeEach(() => {
savedPlatform = process.env.PLATFORM;
savedServerUrl = process.env.GITHUB_SERVER_URL;
process.env.PLATFORM = "forgejo";
});
afterEach(() => {
if (savedPlatform !== undefined) process.env.PLATFORM = savedPlatform;
else delete process.env.PLATFORM;
if (savedServerUrl !== undefined)
process.env.GITHUB_SERVER_URL = savedServerUrl;
else delete process.env.GITHUB_SERVER_URL;
});
test("should skip actor check entirely on Forgejo", async () => {
let apiCalled = false;
const mockOctokit = {
users: {
getByUsername: async () => {
apiCalled = true;
return { data: {} };
},
},
} as unknown as Octokit;
const context = createMockContext();
context.actor = "forgejo-user";
await expect(
checkHumanActor(mockOctokit, context),
).resolves.toBeUndefined();
expect(apiCalled).toBe(false);
});
test("should skip actor check for bot-like names on Forgejo", async () => {
let apiCalled = false;
const mockOctokit = {
users: {
getByUsername: async () => {
apiCalled = true;
return { data: { type: "Bot" } };
},
},
} as unknown as Octokit;
const context = createMockContext();
context.actor = "some-bot[bot]";
context.inputs.allowedBots = "";
await expect(
checkHumanActor(mockOctokit, context),
).resolves.toBeUndefined();
expect(apiCalled).toBe(false);
});
});
}); });