diff --git a/.github/workflows/issue-triage.yml b/.github/workflows/issue-triage.yml index 6833470..bf816ae 100644 --- a/.github/workflows/issue-triage.yml +++ b/.github/workflows/issue-triage.yml @@ -16,9 +16,14 @@ name: Issue and PR Triage permissions: issues: write pull-requests: write - actions: write contents: read models: read + # A caller can only cap a reusable workflow's permissions, never raise them, + # so `actions: write` has to be granted here for the dispatch job downstream + # to work at all. The reusable workflow narrows it to that single job, so the + # `pull_request_target` job does not receive it. Removing it here breaks + # dispatching (see #271/#272); narrow it there instead. + actions: write jobs: issue-triage: diff --git a/.github/workflows/reusable-code-quality.yml b/.github/workflows/reusable-code-quality.yml index 5d1a81f..00290c9 100644 --- a/.github/workflows/reusable-code-quality.yml +++ b/.github/workflows/reusable-code-quality.yml @@ -122,7 +122,7 @@ jobs: show-progress: ${{ runner.debug == '1' && 'true' || 'false' }} - name: Setup node - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v6 + uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 with: node-version: 'lts/*' @@ -132,7 +132,9 @@ jobs: run: curl --fail --silent --show-error https://raw.githubusercontent.com/wp-cli/.github/refs/heads/main/.gherkin-lintrc -o "$RUNNER_TEMP/.gherkin-lintrc" - name: Run linter - run: npx --yes gherkin-lint -c "$RUNNER_TEMP/.gherkin-lintrc" + # Pinned: unpinned `npx` resolves and executes the newest publish at run + # time, and gherkin-lint has not been released since 2023. + run: npx --yes gherkin-lint@4.2.4 -c "$RUNNER_TEMP/.gherkin-lintrc" lint-spellcheck: name: Spell check diff --git a/.github/workflows/reusable-issue-triage.yml b/.github/workflows/reusable-issue-triage.yml index 31df4ba..02f051d 100644 --- a/.github/workflows/reusable-issue-triage.yml +++ b/.github/workflows/reusable-issue-triage.yml @@ -12,7 +12,6 @@ name: Issue and PR Triage permissions: issues: write pull-requests: write - actions: write contents: read models: read @@ -161,6 +160,14 @@ jobs: github.event_name == 'workflow_dispatch' && inputs.issue_number == '' runs-on: ubuntu-latest + # `actions: write` is only needed to dispatch triage runs, which happens + # here and nowhere else. Scoping it to this job keeps it out of the token + # handed to `triage-new-item`, which runs on `pull_request_target` and + # processes pull request content written by anyone who can open a PR. + permissions: + actions: write + issues: read + contents: read steps: - name: Find and dispatch triage for unlabeled items uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 diff --git a/.github/workflows/reusable-regenerate-readme.yml b/.github/workflows/reusable-regenerate-readme.yml index 1631b1f..24925a5 100644 --- a/.github/workflows/reusable-regenerate-readme.yml +++ b/.github/workflows/reusable-regenerate-readme.yml @@ -46,7 +46,10 @@ jobs: - name: Install WP-CLI run: | - curl -O https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli-nightly.phar + # `-f` matters most here: without it curl writes the error body to the + # file and still exits 0, so an outage silently installs an HTML page + # as /usr/local/bin/wp and the failure surfaces much later. + curl -fsSL -O https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli-nightly.phar sudo mv wp-cli-nightly.phar /usr/local/bin/wp sudo chmod +x /usr/local/bin/wp diff --git a/.github/workflows/sync-workflows.yml b/.github/workflows/sync-workflows.yml index fab9d94..3382f05 100644 --- a/.github/workflows/sync-workflows.yml +++ b/.github/workflows/sync-workflows.yml @@ -41,22 +41,26 @@ jobs: # run cut short here is completed by the next push or the daily schedule. timeout-minutes: 60 if: ${{ github.repository_owner == 'wp-cli' }} + # The sync authenticates with the ACTIONS_BOT token below, so GITHUB_TOKEN + # is never used to write anything and does not need `contents: write`. permissions: - contents: write + contents: read steps: - name: Sync files uses: adrianjost/files-sync-action@55d577a25b7106ebc3001353d981ecf8b0150df4 # v2.1.0 with: + # Patterns are regexes matched against repo-relative paths, so the + # dots are escaped to stop them matching any character. FILE_PATTERNS: | - ^.actrc - ^.editorconfig - ^.github/workflows/copilot-setup-steps.yml - ^.github/workflows/regenerate-readme.yml - ^.github/workflows/welcome-new-contributors.yml - ^.github/workflows/issue-triage.yml - ^.github/workflows/check-branch-alias.yml - ^.github/workflows/manage-labels.yml - ^AGENTS.md + ^\.actrc + ^\.editorconfig + ^\.github/workflows/copilot-setup-steps\.yml + ^\.github/workflows/regenerate-readme\.yml + ^\.github/workflows/welcome-new-contributors\.yml + ^\.github/workflows/issue-triage\.yml + ^\.github/workflows/check-branch-alias\.yml + ^\.github/workflows/manage-labels\.yml + ^AGENTS\.md TARGET_REPOS: | wp-cli/ai-command wp-cli/ability-command @@ -103,6 +107,9 @@ jobs: wp-cli/wp-config-transformer wp-cli/wp-super-cache-cli GITHUB_TOKEN: ${{ secrets.ACTIONS_BOT }} + # Only add and update; never delete a file in a target repo just + # because it is absent here. + SKIP_DELETE: true sync-dependabot: name: Sync Dependabot configuration @@ -113,13 +120,13 @@ jobs: timeout-minutes: 60 if: ${{ github.repository_owner == 'wp-cli' }} permissions: - contents: write + contents: read steps: - name: Sync files uses: adrianjost/files-sync-action@55d577a25b7106ebc3001353d981ecf8b0150df4 # v2.1.0 with: FILE_PATTERNS: | - ^.github/dependabot.yml + ^\.github/dependabot\.yml TARGET_REPOS: | wp-cli/ai-command wp-cli/ability-command @@ -168,3 +175,6 @@ jobs: wp-cli/wp-config-transformer wp-cli/wp-super-cache-cli GITHUB_TOKEN: ${{ secrets.ACTIONS_BOT }} + # Only add and update; never delete a file in a target repo just + # because it is absent here. + SKIP_DELETE: true diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000..794e63d --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,47 @@ +# Security Policy + +WP-CLI is used to administer WordPress sites in production, frequently with +elevated privileges, so we take security reports seriously. + +This is the organization-wide default policy for repositories in the +[`wp-cli` organization](https://github.com/wp-cli). A repository that publishes +its own `SECURITY.md` overrides this one. + +## Reporting a vulnerability + +**Please do not report security vulnerabilities through public GitHub issues, +pull requests, or discussions.** + +Report them through the WordPress bug bounty program on HackerOne, which covers +WP-CLI alongside WordPress core and related projects: + + + +Before you submit, please read the full guidance in the WP-CLI handbook: + + + +That handbook page is the authoritative description of what we treat as a +vulnerability, what to include in a report, and what to expect during +coordinated disclosure. A valid report may be eligible for a CVE and a bounty. + +## What makes a report actionable + +A vulnerability report needs to show how someone outside a trust boundary gains +something they could not otherwise obtain. Please state plainly: + +- who the attacker is and what access they begin with, +- what they gain that they should not have, +- the steps to reproduce it. + +Reports that do not demonstrate an actual exploit are likely to be declined. +Note that WP-CLI runs as a trusted local user by design: someone who can already +execute `wp` on a server can generally already act as that user, so that alone +is not a privilege boundary. + +## Build and release infrastructure + +Reports about this organization's shared CI configuration — the reusable +GitHub Actions workflows in [`wp-cli/.github`](https://github.com/wp-cli/.github) +and the credentials they use — are in scope and are best sent through the same +channel above.