Skip to content

ci: harden GitHub Actions workflow permissions#620

Open
kilbot wants to merge 2 commits into
mainfrom
security/harden-workflow-permissions
Open

ci: harden GitHub Actions workflow permissions#620
kilbot wants to merge 2 commits into
mainfrom
security/harden-workflow-permissions

Conversation

@kilbot

@kilbot kilbot commented Jun 17, 2026

Copy link
Copy Markdown
Contributor

What

Add an explicit, least-privilege top-level permissions: block to three GitHub Actions workflows that previously had none.

Why

CodeQL rule actions/missing-workflow-permissions (CWE-275) fired on these files because, lacking a top-level permissions: key, each workflow's GITHUB_TOKEN inherits the repository's default token scope (potentially read/write across many scopes). The fix is to declare the minimal scope each workflow actually needs, so the default token is locked down regardless of the repo-level default.

This came out of the 2026-06-17 org security triage.

Changes

Each block was chosen by inspecting what the workflow's jobs actually do with the GITHUB_TOKEN:

Workflow Permissions added Rationale
.github/workflows/build.yml contents: read workflow_dispatch EAS build. Only checks out code; build/submit auth uses external secrets (EXPO_TOKEN, RXDB_LICENSE_KEY, UNIWIND_AUTH_TOKEN). No step uses GITHUB_TOKEN to write.
.github/workflows/update-translations.yml contents: read Pushes a branch and opens a PR, but does so via a GitHub App token (actions/create-github-app-tokensteps.app-token.outputs.token), not GITHUB_TOKEN. The default token is used only for the read-only checkout.
.github/workflows/add-to-roadmap.yml contents: read Adds the issue to Projects v2 board #4 and sets its status via GraphQL, all authenticated with a GitHub App token (PROJECT_BOT_*). GITHUB_TOKEN cannot write org projects and is used only for read-only checkout.

Notes

  • These are least-privilege but sufficient: every write path in these workflows is carried by a dedicated GitHub App token, so the GITHUB_TOKEN itself never needs more than contents: read.
  • No workflow logic was changed — only a top-level permissions: block was inserted (after on:, before jobs:), preserving existing indentation. Behavior is unchanged.
  • All three files validated as parseable YAML after the edit.

@coderabbitai

coderabbitai Bot commented Jun 17, 2026

Copy link
Copy Markdown
Contributor

Warning

Review limit reached

@kilbot, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 16 minutes and 23 seconds. Learn how PR review limits work.

To continue reviewing without waiting, enable usage-based billing in the billing tab.

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based credits.

🚦 How do rate limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan refill rate.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, the refill rate gradually slows as usage increases. The highest same-day bursts are limited more strictly.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 0a6c98f4-2b3f-4af6-abe3-024ada53e49c

📥 Commits

Reviewing files that changed from the base of the PR and between ed50701 and 1e17467.

📒 Files selected for processing (3)
  • .github/workflows/add-to-roadmap.yml
  • .github/workflows/build.yml
  • .github/workflows/update-translations.yml
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch security/harden-workflow-permissions

Comment @coderabbitai help to get the list of available commands and usage tips.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: d48468b7db

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

types: [translation-release]

permissions:
contents: read

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Restore a write credential for translation branch pushes

When a repository_dispatch actually changes the translation version, actions/checkout leaves origin configured with the default GITHUB_TOKEN; after this permission is reduced to read-only, the later git push -u origin "$BRANCH" on line 42 will try to push with that read-only token and be rejected. The App token in GH_TOKEN is only used by gh pr create, so either checkout/push needs to use steps.app-token.outputs.token or this workflow still needs a write-capable contents token.

Useful? React with 👍 / 👎.

@kilbot

kilbot commented Jun 17, 2026

Copy link
Copy Markdown
Contributor Author

Adversarial review caught an under-scope: update-translations.yml runs git push using the credential persisted by actions/checkout (which has no token: override → default GITHUB_TOKEN). The app token is only wired to gh pr create, not the git remote. So contents: read would 403 the push and break the translation-release flow. Bumped this one file to contents: write; build.yml and add-to-roadmap.yml remain contents: read (correct — their writes go through external/app tokens).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant