@sentry/junior-github 0.67.3 → 0.69.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/SETUP.md CHANGED
@@ -1,13 +1,13 @@
1
1
  # GitHub plugin setup
2
2
 
3
- This plugin exposes two skills — `github-code` (clone, source-code investigation, pull requests) and `github-issues` (issue workflows) both authenticated via host-issued GitHub App installation tokens.
3
+ This plugin exposes two skills — `github-code` (clone, source-code investigation, pull requests) and `github-issues` (issue workflows). Read operations use host-issued GitHub App installation tokens. Write operations use GitHub App user-to-server OAuth tokens so GitHub attributes the action to the requesting user with the app badge.
4
4
 
5
5
  ## 1) Create/install GitHub App
6
6
 
7
7
  In GitHub:
8
8
 
9
9
  1. Go to `Settings -> Developer settings -> GitHub Apps -> New GitHub App`.
10
- 2. Set app name and callback URL (any valid HTTPS URL is fine if you do not use web flow).
10
+ 2. Set app name and callback URL to `https://<junior-host>/api/oauth/callback/github`.
11
11
  3. Under repository permissions, grant:
12
12
 
13
13
  - Issues: Read and write
@@ -23,6 +23,8 @@ In GitHub:
23
23
  Install the app on target repos/orgs and collect:
24
24
 
25
25
  - `GITHUB_APP_ID`
26
+ - `GITHUB_APP_CLIENT_ID`
27
+ - `GITHUB_APP_CLIENT_SECRET`
26
28
  - `GITHUB_APP_PRIVATE_KEY` (PEM)
27
29
 
28
30
  ## 2) Configure host runtime
@@ -30,6 +32,8 @@ Install the app on target repos/orgs and collect:
30
32
  Set on the harness host (never in skill files):
31
33
 
32
34
  - `GITHUB_APP_ID`
35
+ - `GITHUB_APP_CLIENT_ID`
36
+ - `GITHUB_APP_CLIENT_SECRET`
33
37
  - `GITHUB_APP_PRIVATE_KEY`
34
38
  - `GITHUB_INSTALLATION_ID`
35
39
 
@@ -39,16 +43,15 @@ under different app installations across orgs/accounts.
39
43
 
40
44
  ### Vercel env setup (multiline-safe)
41
45
 
42
- `GITHUB_APP_PRIVATE_KEY` is accepted as:
43
-
44
- - Raw PEM (multiline)
45
- - Escaped-newline PEM (single-line with `\n`)
46
- - Base64-encoded PEM
46
+ `GITHUB_APP_PRIVATE_KEY` must be the PEM private key generated by GitHub for
47
+ this app.
47
48
 
48
49
  For Vercel, prefer CLI file input so newlines are preserved exactly:
49
50
 
50
51
  ```bash
51
52
  vercel env add GITHUB_APP_ID production
53
+ vercel env add GITHUB_APP_CLIENT_ID production
54
+ vercel env add GITHUB_APP_CLIENT_SECRET production
52
55
  vercel env add GITHUB_INSTALLATION_ID production
53
56
  vercel env add GITHUB_APP_PRIVATE_KEY production --sensitive < ./github-app-private-key.pem
54
57
  ```
@@ -61,10 +64,41 @@ vercel env update GITHUB_APP_PRIVATE_KEY production --sensitive < ./github-app-p
61
64
 
62
65
  Repeat for `preview` and `development` as needed. After env changes, redeploy so the new deployment picks up updated values.
63
66
 
67
+ ### Optional permission overrides
68
+
69
+ By default, `installation-read` grants read the app installation's current permission envelope once per process and scopes read-capable permissions down to `read` when requesting a token. To declare the app permission envelope in the plugin and avoid that installation lookup, pass `appPermissions` when registering the plugin:
70
+
71
+ ```ts
72
+ githubPlugin({
73
+ appPermissions: {
74
+ contents: "read",
75
+ issues: "write",
76
+ pull_requests: "write",
77
+ },
78
+ });
79
+ ```
80
+
81
+ Junior records these permissions as plugin capabilities. Installation-read token requests remain read-only by requesting read-capable configured permissions at `read` level and omitting GitHub permission fields that have no `read` value. GitHub remains the source of truth for whether a permission name or level exists.
82
+
83
+ GitHub App user-to-server tokens do not use OAuth scopes as their permission model. Their effective access is limited by the GitHub App's installed permissions, the app installation's repository access, and the requesting user's own GitHub access. GitHub returns an empty `scope` value for these tokens, so Junior cannot verify granted scopes from the token response.
84
+
85
+ If you pass `additionalUserScopes`, Junior includes those values in the authorization URL and records the requested scope string as a local reauthorization contract. This does not expand or prove GitHub API permissions — configure GitHub App permissions with `appPermissions` and in the GitHub App settings for provider-enforced access:
86
+
87
+ ```ts
88
+ githubPlugin({
89
+ additionalUserScopes: ["read:org", "workflow"],
90
+ });
91
+ ```
92
+
93
+ Use `additionalUserScopes` only when an integration flow requires specific GitHub OAuth scope parameters in the authorization URL. Do not rely on it to authorize repository, Actions, or workflow writes — those are enforced by GitHub App permissions and the requesting user's own access.
94
+
64
95
  ## 3) Runtime behavior
65
96
 
66
97
  - When either GitHub skill is active, authenticated `gh` and `git` commands cause the runtime to inject GitHub credentials automatically for the current turn.
67
- - Issued credentials are reused only within the current turn.
98
+ - The plugin classifies GitHub traffic from the forwarded HTTP request. Safe app-readable API methods, GraphQL `GET`/`HEAD`/`OPTIONS` requests, and `git-upload-pack` use the `installation-read` grant. `GET /user` uses the `user-read` grant so account identity checks use the requester token. Write-specific REST URLs, non-read GraphQL methods, other non-read API methods, and `git-receive-pack` use the `user-write` grant.
99
+ - `user-read` and `user-write` require the requester, or an explicitly delegated user subject from an allowed system run, to authorize the GitHub App through the private OAuth flow. Missing or expired user authorization pauses interactive turns, sends a private authorization link, and resumes after approval.
100
+ - Git commits use the requester as the commit author, Junior as committer, and a Junior `Co-Authored-By` trailer.
101
+ - Issued credentials are reused only within the current turn, credential leases are cached separately by plugin grant name, and upstream 403 permission denials clear the cached lease before the next retry.
68
102
  - Sandbox does not receive raw tokens via env; host applies Authorization header transforms for GitHub API calls.
69
103
 
70
104
  ## 4) CLI usage
@@ -84,16 +118,25 @@ git -C repo fetch --depth=50 origin
84
118
  git -C repo fetch --unshallow
85
119
  ```
86
120
 
87
- GitHub operations still require a GitHub skill to be active; the runtime injects credentials automatically when one is loaded:
121
+ Load the relevant GitHub skill for command guidance and repo context:
88
122
 
89
123
  ```bash
90
124
  gh issue create --repo owner/repo --title "Example issue" --body-file /vercel/sandbox/tmp/issue.md
91
125
  ```
92
126
 
93
127
  `gh` supports either direct `GITHUB_TOKEN` (for local debugging) or sandbox-level header injection.
94
- The runtime uses `github.issues.read` for read-only issue commands, `github.issues.write` for issue edits, comments, and labels, `github.contents.write` for pushes and merge operations, and `github.pull-requests.write` for PR mutations after the branch is already on the remote.
128
+ The plugin uses `installation-read` for read-only GitHub traffic and `user-write` for mutations. GitHub App permissions still need to cover the operation: issues for issue edits/comments/labels, contents for pushes and merges, pull requests for PR mutations, actions/workflows for workflow operations.
129
+
130
+ Committing and pushing code uses more than one GitHub surface:
131
+
132
+ - Creating the local Git commit does not call GitHub. Junior sets the requester as author and the GitHub App bot as committer in the sandbox.
133
+ - Pushing a branch with Git smart HTTP (`git push`) uses the `user-write` grant and requires the GitHub App to have `Contents: write` on the target repository. The requesting user must also have write access to that repository.
134
+ - REST Git database writes used by some `gh` flows also require `Contents: write`: create blob (`POST /git/blobs`), create tree (`POST /git/trees`), create commit (`POST /git/commits`), and create/update refs (`POST /git/refs`, `PATCH /git/refs/{ref}`). Changes to workflow files may also require `Workflows: write`.
135
+ - Opening the PR after the branch exists is separate: `gh pr create --head` needs pull-request write permission, but it should not create or push commits itself.
136
+
137
+ Fork creation is not part of the default PR path. `POST /repos/{owner}/{repo}/forks` uses the `user-write` grant, but GitHub requires `Administration: write` and `Contents: read`, and the app must be installed on both the source and destination accounts. Do not grant `Administration: write` for routine PR creation; push a branch explicitly and create the PR with `--head` instead.
95
138
 
96
- GitHub capability scoping is a safety rail, not a hard sandbox boundary. It helps prevent accidental write scope and wrong-repo mutations, and the host runtime still decides when to mint credentials. Credential injection is skill-scoped: load the relevant GitHub skill first, keep repo context explicit, and let the runtime choose the required capability for the command.
139
+ GitHub App permission scoping is a safety rail, not a hard sandbox boundary. It helps prevent accidental write scope and wrong-repo mutations, and the host runtime still decides when to mint credentials. Credential injection is provider-domain scoped for sandbox traffic to `api.github.com` and `github.com` during turns with a signed credential context. Keep repo context explicit, and let the plugin choose the required grant for the outbound request.
97
140
 
98
141
  Be careful with mixed-surface PR commands:
99
142
 
@@ -128,23 +171,24 @@ jr-rpc config set github.repo getsentry/junior
128
171
 
129
172
  1. Confirm host env vars are present in prod:
130
173
  - `GITHUB_APP_ID`
174
+ - `GITHUB_APP_CLIENT_ID`
175
+ - `GITHUB_APP_CLIENT_SECRET`
131
176
  - `GITHUB_APP_PRIVATE_KEY`
132
177
  - `GITHUB_INSTALLATION_ID`
133
178
  2. Confirm the GitHub App is installed on your test repo with the permissions above.
134
179
  3. Deploy `main` to prod.
135
180
  4. Exercise `github-issues` to create an issue in a safe test repo.
136
- 5. Verify the issue is authored by the GitHub App identity.
181
+ 5. Verify the issue is authored by the requesting GitHub user with the GitHub App badge.
137
182
  6. Exercise `github-issues` to update title/body, add/remove labels, and add a comment.
138
183
  7. Push a test branch and exercise `github-code` to create a draft PR using explicit repo targeting and `--head`.
139
- 8. Verify all mutations succeed and are attributed to the app.
184
+ 8. Verify all mutations succeed and are attributed to the requesting GitHub user with the app badge.
140
185
  9. Verify GitHub API calls succeed while this skill is active without writing tokens into sandbox env/files.
141
186
  10. Verify raw token values are never printed in output or logs.
142
187
  11. Check logs for:
143
188
 
144
- - `credential_issue_request`
145
- - `credential_issue_success`
146
- - `credential_inject_start`
147
- - `credential_inject_cleanup`
189
+ - `sandbox_egress_upstream_request`
190
+ - `sandbox_egress_credential_needed`
191
+ - `sandbox_egress_upstream_auth_rejected`
148
192
 
149
193
  12. Verify logs contain no token/private-key values.
150
194
  13. Negative test: target a repo without app installation and confirm explicit failure.
package/index.d.ts CHANGED
@@ -1,11 +1,51 @@
1
1
  import type { JuniorPluginRegistration } from "@sentry/junior-plugin-api";
2
2
 
3
+ export type GitHubAppPermissionLevel = "read" | "write" | "admin";
4
+
5
+ /** Configure the built-in GitHub plugin manifest and hooks. */
3
6
  export interface GitHubPluginOptions {
7
+ /**
8
+ * Extra OAuth `scope` values to request during GitHub App user authorization.
9
+ *
10
+ * GitHub App user tokens report empty scopes, so Junior treats this as a
11
+ * local reauthorization contract only. Effective access still comes from the
12
+ * app permissions, installation repositories, and requesting user's access.
13
+ */
14
+ additionalUserScopes?: string[];
15
+
16
+ /**
17
+ * GitHub App installation permissions Junior should request for app tokens.
18
+ *
19
+ * Keys may use GitHub permission names with underscores or hyphens. Junior
20
+ * records these as plugin capabilities and requests read-only installation
21
+ * tokens by scoping read-capable permissions down to `read`.
22
+ * GitHub remains the source of truth for whether a permission exists.
23
+ */
24
+ appPermissions?: Record<string, GitHubAppPermissionLevel>;
25
+
26
+ /** Environment variable containing the GitHub App id. */
27
+ appIdEnv?: string;
28
+
29
+ /** Environment variable containing Junior's Git committer email. */
4
30
  botEmailEnv?: string;
31
+
32
+ /** Environment variable containing Junior's Git committer name. */
5
33
  botNameEnv?: string;
34
+
35
+ /** Environment variable containing the GitHub App OAuth client id. */
36
+ clientIdEnv?: string;
37
+
38
+ /** Environment variable containing the GitHub App OAuth client secret. */
39
+ clientSecretEnv?: string;
40
+
41
+ /** Environment variable containing the GitHub App installation id. */
42
+ installationIdEnv?: string;
43
+
44
+ /** Environment variable containing the GitHub App private key. */
45
+ privateKeyEnv?: string;
6
46
  }
7
47
 
8
- /** Register GitHub manifest content and trusted commit attribution hooks. */
48
+ /** Register GitHub manifest content and runtime hooks. */
9
49
  export function githubPlugin(
10
50
  options?: GitHubPluginOptions,
11
51
  ): JuniorPluginRegistration;