diff --git a/packages/docs/src/content/docs/extend/github-plugin.md b/packages/docs/src/content/docs/extend/github-plugin.md index 7a6e1f510..2aa5b1f40 100644 --- a/packages/docs/src/content/docs/extend/github-plugin.md +++ b/packages/docs/src/content/docs/extend/github-plugin.md @@ -90,6 +90,7 @@ Create and install a GitHub App before you verify GitHub workflows: - Issues: Read and write - Contents: Read and write - Pull requests: Read and write + - Workflows: Write - Metadata: Read 4. Install the app on the repository or organization Junior should access. 5. Copy the App ID, installation ID, bot name, and bot noreply email into your deployment environment. diff --git a/packages/junior-github/SETUP.md b/packages/junior-github/SETUP.md index 5b05e1c47..fb1eb92b8 100644 --- a/packages/junior-github/SETUP.md +++ b/packages/junior-github/SETUP.md @@ -14,6 +14,7 @@ In GitHub: - Contents: Read and write - Pull requests: Read and write - Actions: Read and write +- Workflows: Write - Metadata: Read 4. Create the app and generate a private key. diff --git a/packages/junior-github/plugin.yaml b/packages/junior-github/plugin.yaml index 854e280e0..08241d65b 100644 --- a/packages/junior-github/plugin.yaml +++ b/packages/junior-github/plugin.yaml @@ -1,16 +1,6 @@ name: github description: GitHub issue, pull request, and repository workflows via GitHub App -capabilities: - - actions.read - - actions.write - - issues.read - - issues.write - - contents.read - - contents.write - - pull-requests.read - - pull-requests.write - config-keys: - org - repo diff --git a/packages/junior/src/chat/plugins/auth/github-app-broker.ts b/packages/junior/src/chat/plugins/auth/github-app-broker.ts index ec881556b..a77a694cd 100644 --- a/packages/junior/src/chat/plugins/auth/github-app-broker.ts +++ b/packages/junior/src/chat/plugins/auth/github-app-broker.ts @@ -268,10 +268,9 @@ export function createGitHubAppBroker( ); } - const permissions = capabilitiesToPermissions( - manifest.capabilities, - provider, - ); + const permissions = manifest.capabilities?.length + ? capabilitiesToPermissions(manifest.capabilities, provider) + : undefined; function createLease(params: { installationId: number; @@ -319,11 +318,9 @@ export function createGitHubAppBroker( return { async issue(input: { reason: string }): Promise { const installationId = resolveInstallationId(); - const tokenRequestBody: { - permissions: Record; - } = { - permissions, - }; + const tokenRequestBody: Record = permissions + ? { permissions } + : {}; const appId = resolveAppId(appIdEnv); const appJwt = createAppJwt(appId, privateKeyEnv); diff --git a/packages/junior/tests/unit/plugins/github-app-broker.test.ts b/packages/junior/tests/unit/plugins/github-app-broker.test.ts index 09da0f696..f327659c1 100644 --- a/packages/junior/tests/unit/plugins/github-app-broker.test.ts +++ b/packages/junior/tests/unit/plugins/github-app-broker.test.ts @@ -21,16 +21,7 @@ const TEST_CREDENTIALS: GitHubAppCredentials = { const TEST_MANIFEST: PluginManifest = { name: "github", description: "GitHub issue management via GitHub App", - capabilities: [ - "github.actions.read", - "github.actions.write", - "github.issues.read", - "github.issues.write", - "github.contents.read", - "github.contents.write", - "github.pull-requests.read", - "github.pull-requests.write", - ], + capabilities: [], configKeys: ["github.org", "github.repo"], credentials: TEST_CREDENTIALS, target: { @@ -293,7 +284,7 @@ describe("github app credential broker", () => { expect(accessTokenCalls).toHaveLength(2); }); - it("requests the full plugin permission set when minting installation tokens", async () => { + it("omits permissions from token request when no capabilities are declared", async () => { setupValidEnv(); mockGitHubApi(); @@ -304,12 +295,25 @@ describe("github app credential broker", () => { const fetchCall = findAccessTokenCall(); const body = JSON.parse(fetchCall[1]?.body as string); - expect(body.permissions).toEqual({ - issues: "write", - contents: "write", - actions: "write", - pull_requests: "write", - }); + expect(body.permissions).toBeUndefined(); expect(body.repositories).toBeUndefined(); }); + + it("scopes token permissions from capabilities when declared", async () => { + setupValidEnv(); + mockGitHubApi(); + + const manifestWithCaps: PluginManifest = { + ...TEST_MANIFEST, + capabilities: ["github.issues.read", "github.issues.write"], + }; + const broker = createGitHubAppBroker(manifestWithCaps, TEST_CREDENTIALS); + await broker.issue({ + reason: "test:scoped-permissions", + }); + + const fetchCall = findAccessTokenCall(); + const body = JSON.parse(fetchCall[1]?.body as string); + expect(body.permissions).toEqual({ issues: "write" }); + }); });