Skip to content

Commit 6cf6791

Browse files
Copilotpelikhan
andauthored
Document safe-outputs GitHub App permission narrowing (#15297)
* Initial plan * Document GitHub App per-job permission narrowing and permission requirements Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> * Remove detailed permissions table as requested Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
1 parent 33ee040 commit 6cf6791

File tree

3 files changed

+29
-6
lines changed

3 files changed

+29
-6
lines changed

docs/src/content/docs/reference/frontmatter-full.md

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1784,7 +1784,10 @@ cache: []
17841784

17851785
# Safe output processing configuration that automatically creates GitHub issues,
17861786
# comments, and pull requests from AI workflow output without requiring write
1787-
# permissions in the main job
1787+
# permissions in the main job. When using GitHub App tokens (app:), permissions
1788+
# are automatically narrowed per-job to match only what's needed, and tokens are
1789+
# auto-revoked at job end. Multiple safe outputs in the same workflow receive the
1790+
# union of their required permissions.
17881791
# (optional)
17891792
safe-outputs:
17901793
# List of allowed domains for URI filtering in AI workflow output. URLs from other
@@ -3412,8 +3415,10 @@ safe-outputs:
34123415
github-token: "${{ secrets.GITHUB_TOKEN }}"
34133416

34143417
# GitHub App credentials for minting installation access tokens. When configured,
3415-
# a token will be generated using the app credentials and used for all safe output
3416-
# operations.
3418+
# tokens are automatically minted per-job with permissions narrowed to match the
3419+
# job's permissions block. Tokens are auto-revoked at job end. This enables safe
3420+
# use of a broadly-permissioned GitHub App because each job only receives the
3421+
# specific permissions it needs.
34173422
# (optional)
34183423
app:
34193424
# GitHub App ID. Should reference a variable (e.g., ${{ vars.APP_ID }}).

docs/src/content/docs/reference/safe-outputs.md

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -628,7 +628,7 @@ Exposes outputs: `status-update-id`, `project-id`, `status`.
628628

629629
### Pull Request Creation (`create-pull-request:`)
630630

631-
Creates PRs with code changes. Falls back to issue if creation fails (e.g., org settings block it). `expires` field (same-repo only) auto-closes after period: integers (days) or `2h`, `7d`, `2w`, `1m`, `1y` (hours < 24 treated as 1 day).
631+
Creates PRs with code changes. By default, falls back to creating an issue if PR creation fails (e.g., org settings block it). Set `fallback-as-issue: false` to disable this fallback and avoid requiring `issues: write` permission. `expires` field (same-repo only) auto-closes after period: integers (days) or `2h`, `7d`, `2w`, `1m`, `1y` (hours < 24 treated as 1 day).
632632

633633
```yaml wrap
634634
safe-outputs:
@@ -641,6 +641,7 @@ safe-outputs:
641641
if-no-changes: "warn" # "warn" (default), "error", or "ignore"
642642
target-repo: "owner/repo" # cross-repository
643643
base-branch: "vnext" # target branch for PR (default: github.ref_name)
644+
fallback-as-issue: false # disable issue fallback (default: true)
644645
```
645646

646647
The `base-branch` field specifies which branch the pull request should target. This is particularly useful for cross-repository PRs where you need to target non-default branches (e.g., `vnext`, `release/v1.0`, `staging`). When not specified, defaults to the workflow's branch (`github.ref_name`).
@@ -656,7 +657,7 @@ safe-outputs:
656657
```
657658

658659
> [!NOTE]
659-
> PR creation may fail if "Allow GitHub Actions to create and approve pull requests" is disabled in Organization Settings. Fallback creates issue with branch link.
660+
> PR creation may fail if "Allow GitHub Actions to create and approve pull requests" is disabled in Organization Settings. By default (`fallback-as-issue: true`), fallback creates an issue with branch link and requires `issues: write` permission. Set `fallback-as-issue: false` to disable fallback and only require `contents: write` + `pull-requests: write`.
660661

661662
### Close Pull Request (`close-pull-request:`)
662663

@@ -1336,6 +1337,20 @@ safe-outputs:
13361337
create-issue:
13371338
```
13381339

1340+
#### How GitHub App Tokens Work
1341+
1342+
When you configure `app:` for safe outputs, tokens are **automatically managed per-job** for enhanced security:
1343+
1344+
1. **Per-job token minting**: Each safe output job automatically mints its own token via `actions/create-github-app-token` with permissions explicitly scoped to that job's needs
1345+
2. **Permission narrowing**: Token permissions are narrowed to match the job's `permissions:` block - only the permissions required for the safe outputs in that job are granted
1346+
3. **Automatic revocation**: Tokens are explicitly revoked at job end via `DELETE /installation/token`, even if the job fails
1347+
4. **Safe shared configuration**: A broadly-permissioned GitHub App can be safely shared across workflows because tokens are narrowed per-job
1348+
1349+
> [!TIP]
1350+
> **Why this matters**: You can configure a single GitHub App at the organization level with broad permissions (e.g., `contents: write`, `issues: write`, `pull-requests: write`), and each workflow job will only receive the specific subset of permissions it needs. This provides least-privilege access without requiring per-workflow App configuration.
1351+
1352+
**Example**: If your workflow only uses `create-issue:`, the minted token will have `contents: read` + `issues: write`, even if your GitHub App has broader permissions configured.
1353+
13391354
### Maximum Patch Size (`max-patch-size:`)
13401355

13411356
Limits git patch size for PR operations (1-10,240 KB, default: 1024 KB):

docs/src/content/docs/reference/tokens.mdx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -232,7 +232,9 @@ gh aw secrets set APP_PRIVATE_KEY --value "$(cat path/to/private-key.pem)"
232232

233233
**How it works**:
234234

235-
At workflow start, a token is automatically minted with permissions matching your agent job's `permissions:` field. The token is passed to the GitHub MCP server and automatically revoked at workflow end (even on failure).
235+
At workflow start, a token is automatically minted with **permissions matching your job's `permissions:` field**. The token is passed to the GitHub MCP server and automatically revoked at workflow end (even on failure).
236+
237+
This is the same per-job narrowing behavior used by `safe-outputs.app:` - see the [Safe Outputs documentation](/gh-aw/reference/safe-outputs/#github-app-token-app) for a detailed explanation of how GitHub App tokens are narrowed per-job.
236238

237239
**Token precedence and fallback**:
238240

@@ -243,6 +245,7 @@ At workflow start, a token is automatically minted with permissions matching you
243245
- The compiler automatically sets `GITHUB_MCP_SERVER_TOKEN` and passes it as `GITHUB_PERSONAL_ACCESS_TOKEN` (local/Docker) or an `Authorization: Bearer` header (remote).
244246
- In most cases, you do not need to set this token separately. Use `GH_AW_GITHUB_TOKEN` instead.
245247
- **GitHub App advantages**: Short-lived tokens (auto-revoked at workflow end), no credential rotation needed, automatic permission calculation, better auditability.
248+
- **Per-job permission narrowing**: When using a GitHub App (via `tools.github.app:` or `safe-outputs.app:`), tokens are automatically narrowed to match the job's `permissions:` block. This means you can safely configure a broadly-permissioned GitHub App at the organization level, and each job will only receive the specific permissions it needs.
246249
- If using a GitHub App for both safe-outputs and GitHub MCP server, you can configure them independently for different permission levels.
247250
- Set the resource owner to the organization when the repository is organization-owned.
248251
- `GITHUB_TOKEN` is not supported in remote mode. Use `GH_AW_GITHUB_TOKEN` or a GitHub App instead.

0 commit comments

Comments
 (0)