git-auto-commit-action: Limitations on PRs from forks no longer apply

In the readme section “Advanced Uses” > “Use in forks from public repositories” it says that “GitHub Action has to be enabled on the forked repository.” and “the Workflow will run on the forked repository”, however this doesn’t seem to be the case. The docs for pull_request_target and this GitHub blog post suggests that the main difference pull_request_target has compared to pull_request is that it runs from the base of the PR instead of the PR merge commit, i.e., any changes to the workflow file in the PR will not take effect until merged.

An example is, this workflow file is triggered by pull_request_target, and when you open a PR from a fork (with GitHub Actions disabled) it runs within the repository where the PR is (not the fork).

And this GitHub Action is able to commit to the fork while running in the non-fork repo. It’s able to commit with the default GITHUB_TOKEN and you can checkout the PR branch at the fork repository with:

- uses: actions/checkout@v3
  with:
    repository: ${{ github.event.pull_request.head.repo.full_name }}
    ref: ${{ github.head_ref }}

So it seems like the limitations on running in PRs from forks no longer apply! 🎉

About this issue

  • Original URL
  • State: closed
  • Created 2 years ago
  • Reactions: 1
  • Comments: 19 (9 by maintainers)

Commits related to this issue

Most upvoted comments

Thanks

I spent the day trying to create an autolint system to fix PRs of external contributors to my open-source repo, and tried various techniques without any good result.

https://github.com/slorber/lint-autofix-ci-demo https://github.com/slorber/lint-autofix-ci-demo/pull/1


I tried splitting the workflow 2 as suggested:

  • “pull_request” (untrusted linting, generate git diff patch)
  • “workflow_run” (trusted, apply patch and push)

It felt quite inconvenient to pass data (git diff) between the 2 workflows using artifacts. Apparently you could also use job outputs, but didn’t try yet: https://twitter.com/yarlob/status/1735350688052342809


I also tried to implement a workflow where the linting would happen when you comment the PR, but couldn’t push to the forked branch due to permission issues: https://github.com/slorber/lint-autofix-ci-demo/blob/7f9745c1782c247cef3a313b366f52b158ff84ed/.github/workflows/lint-autofix-comment.yml

Even with the “pull_request_target” unsafe workflow, I couldn’t get it to push to my own remote branch with permissions issues: https://github.com/slorber/lint-autofix-ci-demo/blob/7f9745c1782c247cef3a313b366f52b158ff84ed/.github/workflows/lint-autofix-unsecure-test.yml https://github.com/slorber/lint-autofix-ci-demo/actions/runs/7212924277/job/19651657295?pr=1

CleanShot 2023-12-14 at 19 28 48@2x

Who has been able to push to a fork branch here, even unsafely with pull_request_target?

Does it require a PAT?

Or can the default GitHub token + bot account be enough to push to contributors branches?

EDIT: I confirm a PAT seems to be required to push to fork branches

In fact I tried to do it the other way, where the workflow runs in the forked repository instead, and I couldn’t get that to work either.

Next time a user forks your project and enabled GitHub Actions and opened a Pull Request, the Workflow will run on the forked repository and will push commits to the same branch.

I forked the repo, enabled github actions (and all permissions), then opened a pull request, and still no workflow ran in the forked repo, only the base repo.

According to this section in the GitHub docs:

No pull request events occur on the forked repository.

This makes me think the “Workflow should run in forked repository” of your readme isn’t right somehow…


GitHub is sure making this frustrating. All I want to do is make a commit to a pull request, whether it be to the base or to the fork, as long as it gets added to the PR. It also needs to work regardless of whether the PR is opened from a branch or a fork.

I’m having trouble getting this to work, and I believe I’m doing everything the same, except for that I’m calling the workflow from another workflow, but I believe it’s still getting the right repo and ref and all that. Here are my workflows and logs. In this case I’m trying to do a pull request from greenelab/lwt-test:test into vincerubinetti/lab-website-template:main (I also tried main into main). I’m still getting the error:

remote: Permission to greenelab/lwt-test.git denied to github-actions[bot].
fatal: unable to access 'https://github.com/greenelab/lwt-test/': The requested URL returned error: 403
Error: Invalid status code: 128

Both repos also have Actions enabled, and the most permissive settings:

  • Allow all actions and reusable workflows
  • Fork pull request workflows from outside collaborators: Require approval for first-time contributors who are new to GitHub
  • Workflow permissions: read and write permissions

I happen to be an admin under both organizations, vincerubinetti and greenelab.

triggering workflow
name: on-pull-request
run-name: on pull request activity

on:
  pull_request_target:
    types:
      - opened
      - reopened
      - synchronize
      - closed

jobs:
  update-citations:
    uses: ./.github/workflows/update-citations.yaml
  build-preview:
    needs: update-citations
    uses: ./.github/workflows/build-preview.yaml
called workflow
name: update-citations
run-name: update citations

on:
  # run when called from another workflow
  workflow_call:
    outputs:
      changed:
        value: ${{ jobs.update-citations.outputs.changed }}

  # run if user manually requests it
  workflow_dispatch:

env:
  FORCE_COLOR: true
  GOOGLE_SCHOLAR_API_KEY: ${{ secrets.GOOGLE_SCHOLAR_API_KEY }}

jobs:
  update-citations:
    runs-on: ubuntu-latest
    timeout-minutes: 15

    steps:
      - name: Checkout branch contents
        uses: actions/checkout@v3
        with:
          repository: ${{ github.event.pull_request.head.repo.full_name }}
          ref: ${{ github.head_ref }}

      - name: Setup Python
        uses: actions/setup-python@v4
        with:
          python-version: "3.11"
          cache: "pip"
          cache-dependency-path: "**/requirements.txt"

      - name: Install Python packages
        run: |
          python -m pip install --upgrade --requirement ./_cite/requirements.txt

      - name: Build updated citations
        run: python _cite/cite.py
        timeout-minutes: 15

      - name: Check if citations changed
        uses: tj-actions/verify-changed-files@v13
        id: changed
        with:
          files: |
            _data/citations.yaml

      - name: Commit updated citations
        if: steps.changed.outputs.files_changed == 'true'
        uses: stefanzweifel/git-auto-commit-action@v4
        with:
          commit_message: "Update citations"

    outputs:
      changed: ${{ steps.changed.outputs.files_changed }}
logs

Setup Job

Operating System
Runner Image
Runner Image Provisioner
  2.0.108.1
GITHUB_TOKEN Permissions
  Actions: write
  Checks: write
  Contents: write
  Deployments: write
  Discussions: write
  Issues: write
  Metadata: read
  Packages: write
  Pages: write
  PullRequests: write
  RepositoryProjects: write
  SecurityEvents: write
  Statuses: write
Secret source: Actions
Prepare workflow directory
Prepare all required actions
Getting action download info
Download action repository 'actions/checkout@v3' (SHA:ac593985[6](https://github.com/vincerubinetti/lab-website-template/actions/runs/4117682412/jobs/7109283248#step:1:7)15ec2ede58e132d2e21d2b1cbd6127c)
Download action repository 'actions/setup-python@v4' (SHA:d27e3f3d7c64b4bbf8e4abfb9b63b83e846e0435)
Download action repository 'tj-actions/verify-changed-files@v13' (SHA:9ed3155b72ba709881c967f756[11](https://github.com/vincerubinetti/lab-website-template/actions/runs/4117682412/jobs/7109283248#step:1:13)fc5852f773b9)
Download action repository 'stefanzweifel/git-auto-commit-action@v4' (SHA:3ea6ae190baf489ba007f7c92608f33ce20ef04a)
Getting action download info
Download action repository 'tj-actions/glob@v16' (SHA:9923edc8efb605c[13](https://github.com/vincerubinetti/lab-website-template/actions/runs/4117682412/jobs/7109283248#step:1:16)1fb09a79699a819cde2cfe4)
Uses: vincerubinetti/lab-website-template/.github/workflows/update-citations.yaml@refs/heads/main (aaa73ec57a96be57e8098b75ac02e0[16](https://github.com/vincerubinetti/lab-website-template/actions/runs/4117682412/jobs/7109283248#step:1:19)4e[35](https://github.com/vincerubinetti/lab-website-template/actions/runs/4117682412/jobs/7109283248#step:1:39)bf9a)
Complete job name: update-citations / update-citations

Checkout branch contents

Run actions/checkout@v3
with:
    repository: greenelab/lwt-test
    ref: test
    token: ***
    ssh-strict: true
    persist-credentials: true
    clean: true
    fetch-depth: 1
    lfs: false
    submodules: false
    set-safe-directory: true
  env:
    FORCE_COLOR: true
    GOOGLE_SCHOLAR_API_KEY: 
Syncing repository: greenelab/lwt-test
Getting Git version info
  Working directory is '/home/runner/work/lab-website-template/lab-website-template'
  /usr/bin/git version
  git version 2.[3](https://github.com/vincerubinetti/lab-website-template/actions/runs/4117682412/jobs/7109283248#step:2:3)9.1
Temporarily overriding HOME='/home/runner/work/_temp/1b0c68[4](https://github.com/vincerubinetti/lab-website-template/actions/runs/4117682412/jobs/7109283248#step:2:4)e-682a-4fd7-88[5](https://github.com/vincerubinetti/lab-website-template/actions/runs/4117682412/jobs/7109283248#step:2:5)a-14[6](https://github.com/vincerubinetti/lab-website-template/actions/runs/4117682412/jobs/7109283248#step:2:6)24c45f58[7](https://github.com/vincerubinetti/lab-website-template/actions/runs/4117682412/jobs/7109283248#step:2:7)' before making global git config changes
Adding repository directory to the temporary git global config as a safe directory
/usr/bin/git config --global --add safe.directory /home/runner/work/lab-website-template/lab-website-template
Deleting the contents of '/home/runner/work/lab-website-template/lab-website-template'
Initializing the repository
  /usr/bin/git init /home/runner/work/lab-website-template/lab-website-template
  hint: Using 'master' as the name for the initial branch. This default branch name
  hint: is subject to change. To configure the initial branch name to use in all
  hint: of your new repositories, which will suppress this warning, call:
  hint: 
  hint: 	git config --global init.defaultBranch <name>
  hint: 
  hint: Names commonly chosen instead of 'master' are 'main', 'trunk' and
  hint: 'development'. The just-created branch can be renamed via this command:
  hint: 
  hint: 	git branch -m <name>
  Initialized empty Git repository in /home/runner/work/lab-website-template/lab-website-template/.git/
  /usr/bin/git remote add origin https://github.com/greenelab/lwt-test
Disabling automatic garbage collection
  /usr/bin/git config --local gc.auto 0
Setting up auth
  /usr/bin/git config --local --name-only --get-regexp core\.sshCommand
  /usr/bin/git submodule foreach --recursive sh -c "git config --local --name-only --get-regexp 'core\.sshCommand' && git config --local --unset-all 'core.sshCommand' || :"
  /usr/bin/git config --local --name-only --get-regexp http\.https\:\/\/github\.com\/\.extraheader
  /usr/bin/git submodule foreach --recursive sh -c "git config --local --name-only --get-regexp 'http\.https\:\/\/github\.com\/\.extraheader' && git config --local --unset-all 'http.https://github.com/.extraheader' || :"
  /usr/bin/git config --local http.https://github.com/.extraheader AUTHORIZATION: basic ***
Fetching the repository
  /usr/bin/git -c protocol.version=2 fetch --no-tags --prune --progress --no-recurse-submodules --depth=1 origin +refs/heads/test*:refs/remotes/origin/test* +refs/tags/test*:refs/tags/test*
  remote: Enumerating objects: 160, done.        
  remote: Counting objects:   0% (1/160)        
  Resolving deltas: 100% (5/5), done.
  From https://github.com/greenelab/lwt-test
   * [new branch]      test       -> origin/test
Determining the checkout info
  /usr/bin/git branch --list --remote origin/test
    origin/test
Checking out the ref
  /usr/bin/git checkout --progress --force -B test refs/remotes/origin/test
  Switched to a new branch 'test'
  branch 'test' set up to track 'origin/test'.
/usr/bin/git log -1 --format='%H'
'11c318f717d81c5ec98c15049803de635270[39](https://github.com/vincerubinetti/lab-website-template/actions/runs/4117682412/jobs/7109283248#step:2:42)6f'

Commit updated citations

Run stefanzweifel/git-auto-commit-action@v4
Started: bash /home/runner/work/_actions/stefanzweifel/git-auto-commit-action/v4/entrypoint.sh
INPUT_REPOSITORY value: .
INPUT_STATUS_OPTIONS: 
INPUT_FILE_PATTERN: .
INPUT_BRANCH value: test
From https://github.com/greenelab/lwt-test
 * [new branch]      main       -> origin/main
Already on 'test'
M	_cite/.cache/cache.db
M	_data/citations.yaml
Your branch is up to date with 'origin/test'.
INPUT_ADD_OPTIONS: 
INPUT_FILE_PATTERN: .
INPUT_COMMIT_OPTIONS: 
INPUT_COMMIT_USER_NAME: github-actions[bot]
INPUT_COMMIT_USER_EMAIL: 41898282+github-actions[bot]@users.noreply.github.com
INPUT_COMMIT_MESSAGE: Update citations
INPUT_COMMIT_AUTHOR: vincerubinetti <vincerubinetti@users.noreply.github.com>
[test 542c0d4] Update citations
 Author: vincerubinetti <vincerubinetti@users.noreply.github.com>
 2 files changed, 10 insertions(+)
INPUT_TAGGING_MESSAGE: 
No tagging message supplied. No tag will be added.
INPUT_PUSH_OPTIONS: 
remote: Permission to greenelab/lwt-test.git denied to github-actions[bot].
fatal: unable to access 'https://github.com/greenelab/lwt-test/': The requested URL returned error: [40](https://github.com/vincerubinetti/lab-website-template/actions/runs/4117682412/jobs/7109283248#step:2:43)3
Error: Invalid status code: 128
    at ChildProcess.<anonymous> (/home/runner/work/_actions/stefanzweifel/git-auto-commit-action/v4/index.js:17:19)
    at ChildProcess.emit (node:events:[52](https://github.com/vincerubinetti/lab-website-template/actions/runs/4117682412/jobs/7109283248#step:2:57)7:28)
    at maybeClose (node:internal/child_process:10[92](https://github.com/vincerubinetti/lab-website-template/actions/runs/4117682412/jobs/7109283248#step:2:97):16)
    at Process.ChildProcess._handle.onexit (node:internal/child_process:302:5) {
  code: 128
}
Error: Invalid status code: 128
    at ChildProcess.<anonymous> (/home/runner/work/_actions/stefanzweifel/git-auto-commit-action/v4/index.js:17:19)
    at ChildProcess.emit (node:events:527:28)
    at maybeClose (node:internal/child_process:[109](https://github.com/vincerubinetti/lab-website-template/actions/runs/4117682412/jobs/7109283248#step:2:114)2:16)
    at Process.ChildProcess._handle.onexit (node:internal/child_process:[302](https://github.com/vincerubinetti/lab-website-template/actions/runs/4117682412/jobs/7109283248#step:2:307):5)

Link to run (will dissappear in the future): https://github.com/vincerubinetti/lab-website-template/actions/runs/4118566556/jobs/7111241089

Log dump

The security measure is that GitHub Actions runs the version of the workflow from stefanzweifel/git-auto-commit-action-demo-app (main) and not wnxtrash/git-auto-commit-action-demo-app (main). So you have full control of the actions that are run and it is therefore trusted with a GITHUB_TOKEN with write permissions.

You’re totally right. That’s how this works. I’ve been too far away from the innerworkings of GitHub Actions in the last few months. 😅

And this actually also worked in my demo repository. https://github.com/stefanzweifel/git-auto-commit-action-demo-app/pull/34

Will update the README shortly with an example using repository: ${{ github.event.pull_request.head.repo.full_name }}.

The pull_request_target trigger only takes effect if it is present in the workflow file in the PR base branch, i.e., the workflow https://github.com/stefanzweifel/git-auto-commit-action-demo-app/blob/main/.github/workflows/format_php.yml not the PR branch. The security measure is that GitHub Actions runs the version of the workflow from stefanzweifel/git-auto-commit-action-demo-app (main) and not wnxtrash/git-auto-commit-action-demo-app (main). So you have full control of the actions that are run and it is therefore trusted with a GITHUB_TOKEN with write permissions.

Here’s an example of a workflow running with write permission even though the user who triggered it doesn’t have special repository or organisation permissions: https://github.com/sunpy/sunpy/runs/5952858318?check_suite_focus=true#step:1:14

@slorber

If it’s not logged in their UI, it doesn’t mean that users can’t steal the secret unfortunately, so I’m not sure it’s super safe.

I agree. pull_request_target isn’t safe, as a bad actor can steal them your secrets, if you have any. And apparently there’s no Action-only fix available.

Thanks for the link for autofix.ci. To make things clearer for others, I will update the README with a warning, stating that pull_request_target can be used to steal secrets and will point users to autofix.ci.

I have plans to publish a repository with example workflows in the coming months. The pull_request_target-example will also have a big warning pointing out the security issues.

Thanks @vincerubinetti for this detailed feedback with logs and workflows! Verifying and testing if and how this action still works with PRs is still on my todo list. Other projects nudged it down. Will try to take a closer look this weekend.

These logs help a lot!

Thanks for sharing this! Sounds lovely that it now seems to be possible to use this in PRs made by forks. Will do some verification and testing in my own repos and update the README.