Minimally scope permissions in GitHub Actions workflows#29
Draft
desrosj wants to merge 4 commits into
Draft
Conversation
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Align reusable-workflow callers with the audit requirement that job-level permissions keys immediately follow runs-on or uses.
Add scripts/verify-workflow-compliance.rb and composer workflow-compliance.
There was a problem hiding this comment.
Pull request overview
This PR tightens security on GitHub Actions workflows by removing implicit broad permissions, adding a top-level permissions: {} default with narrowly-scoped job-level grants, and adding timeout-minutes caps to every job. It also introduces a Ruby verification script (and a workflow-compliance composer script) that enforces these conventions across the workflow files.
Changes:
- Add top-level
permissions: {}and tighten/relocate job-levelpermissions:to the minimum scopes in all seven workflows. - Add
timeout-minutesto every job to bound runaway runs (replacing the 360-minute default). - Add
scripts/verify-workflow-compliance.rbplus a composerworkflow-complianceentry that checks for the audit comments,permissions: {}placement, presence of job permissions/timeouts, and correct YAML key ordering.
Reviewed changes
Copilot reviewed 9 out of 9 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
| .github/workflows/auto-translate.yml | Moves permissions after uses, adds 120m timeout, adds preamble. |
| .github/workflows/brand-plugin-test-playwright.yml | Top-level permissions: {}, tightens setup to {}, repositions reusable-caller permissions after uses, adds timeouts. |
| .github/workflows/codecoverage-main.yml | Replaces top-level contents: read with {}, adds job-level {} for get-repo-name, repositions write perms after uses for codecoverage, adds timeouts. |
| .github/workflows/i18n-crowdin-download.yml | Drops top-level write perms, sets contents: read after uses, adds 30m timeout. |
| .github/workflows/i18n-crowdin-upload.yml | Adds top-level {}, job-level contents: read after uses, adds 20m timeout. |
| .github/workflows/lint.yml | Adds top-level {}, job-level contents: read, 30m timeout. |
| .github/workflows/satis-webhook.yml | Adds top-level {}, job-level contents: read, 15m timeout. |
| scripts/verify-workflow-compliance.rb | New Ruby auditor enforcing the preamble, permissions: {} placement, job permissions/timeouts, and key ordering. |
| composer.json | Registers workflow-compliance composer script and its description. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This updates the GitHub Actions workflow files to:
Once this PR is merged, the Settings -> Actions -> Workflow permissions setting can be changed by a repo admin to "Read repository contents and packages permissions".
For more information, see PRESS11-470.
References
Use of AI
Cursor was used with (Claude Opus 4.7 and Composer 2.0 at varying points) to analyze the repository and make the initial changes.
When this PR is marked "ready for review" it means that I have manually reviewed all permissions and timeouts that were changed and made any necessary adjustments.
As a part of the analysis, the following summary was created:
Status
auto-translate.yml,brand-plugin-test-playwright.yml,codecoverage-main.yml,i18n-crowdin-download.yml,i18n-crowdin-upload.yml,lint.yml,satis-webhook.yml)add/scoped-workflow-permissions(created)51dfb765514267Top-level
permissions: {}permissions: {}directive (added in this run):auto-translate.ymlpermissions: {}directive (added in this run):brand-plugin-test-playwright.ymlpermissions: {}directive (added in this run):codecoverage-main.ymlpermissions: {}directive (added in this run):i18n-crowdin-download.ymlpermissions: {}directive (added in this run):i18n-crowdin-upload.ymlpermissions: {}directive (added in this run):lint.ymlpermissions: {}directive (added in this run):satis-webhook.ymlJob-level
permissions:additionspermissions:directivepermissions: {}[3]permissions:directivecontents: read[3]permissions:directivecontents: read[3]permissions:directivecontents: read[3]permissions:directivecontents: read[3]permissions:directive (existing job-level entries were retained;setupwas tightened — see corrections)permissions:directive (existingtranslatepermissions retained; comments aligned)Permissions corrections (previously incorrect)
codecoverage-main.yml:: (workflow-wide default): BEFORE top-levelcontents: read-> AFTER top-levelpermissions: {}plus explicit job grants -- Avoid granting read to jobs that never clone the repo (e.g.get-repo-name);codecoveragestill suppliescontents: writeandpull-requests: writefor the reusable workflow -- [3]brand-plugin-test-playwright.yml::setup: BEFOREcontents: read-> AFTERpermissions: {}-- Job only derives the branch name from environment variables and does not runactions/checkoutor call the GitHub API -- [3]i18n-crowdin-download.yml::call-crowdin-workflow: BEFORE top-levelcontents: writeandpull-requests: write(inherited by the job) -> AFTER job-levelcontents: read-- Matchesnewfold-labs/workflowsi18n-crowdin-download.yml, which checks out withpersist-credentials: falseand usesNEWFOLD_ACCESS_TOKENasGITHUB_TOKENfor Crowdin’s PR creation -- [3]timeout-minutesadditions1060gh-pages; needs headroom over the inner job’s coverage work.106060bluehostfor the develop-matrix variant.15302030120Notes / blockers
i18n-crowdin-download.ymlonly passesCROWDIN_PERSONAL_TOKENwhilenewfold-labs/workflows/.github/workflows/i18n-crowdin-download.yml@maindeclares a requiredNEWFOLD_ACCESS_TOKENsecret for Crowdin’s PR step; confirm org/repo secrets andsecretsmapping so dispatch does not fail at runtime.peter-evans/repository-dispatchinsatis-webhook.ymlusessecrets.WEBHOOK_TOKEN, notGITHUB_TOKEN; jobcontents: readis scoped toactions/checkoutonly.