Conversation
Co-authored-by: pancetta <7158893+pancetta@users.noreply.github.com>
| runs-on: ubuntu-latest | ||
| if: >- | ||
| (github.repository_owner == 'Parallel-in-Time') && | ||
| ((github.event_name == 'push') || | ||
| (github.event_name == 'schedule') || | ||
| ((github.event_name == 'pull_request_target') && | ||
| (contains(github.event.pull_request.labels.*.name, 'gitlab-mirror')) | ||
| ) | ||
| ) | ||
| steps: | ||
| - name: Query permissions of triggering actor | ||
| id: query_permission_triggering_actor | ||
| if: github.event_name == 'pull_request_target' | ||
| uses: actions-cool/check-user-permission@v2 | ||
| with: | ||
| username: ${{ github.triggering_actor }} | ||
| require: 'write' | ||
| token: ${{ secrets.GITHUB_TOKEN }} | ||
| - name: Interpret the queried result | ||
| if: github.event_name == 'pull_request_target' | ||
| run: | | ||
| echo "Current permission level is ${{ steps.query_permission_triggering_actor.outputs.user-permission }}" | ||
| echo "Job originally triggered by ${{ github.actor }}" | ||
| echo "Checking permission returned ${{ steps.query_permission_triggering_actor.outputs.require-result }}" | ||
| if ${{ steps.query_permission_triggering_actor.outputs.require-result }} | ||
| then | ||
| echo 'Permissions granted' | ||
| exit 0 | ||
| else | ||
| echo 'Not enough permissions. Please ask a member of Parallel-in-Time to rerun the job.' | ||
| exit 1 | ||
| fi | ||
| - name: Pass if workflow from push or schedule | ||
| if: >- | ||
| (github.event_name == 'push') || | ||
| (github.event_name == 'schedule') | ||
| run: exit 0 | ||
| # - name: Fail for other triggers | ||
| # if: >- | ||
| # (github.event_name != 'push') && | ||
| # (github.event_name != 'schedule') && | ||
| # (github.event_name != 'pull_request_target') | ||
| # run: exit 1 | ||
|
|
||
| mirror_to_gitlab: |
Check warning
Code scanning / CodeQL
Workflow does not contain permissions Medium
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI 2 months ago
To fix the problem, we should explicitly define a permissions block that scopes the GITHUB_TOKEN to the minimal rights needed. Since both check_permission and mirror_to_gitlab (and the elided get_artifacts_from_other_workflow job) only need to read repository contents and interact with pull requests (e.g., for statuses/comments via the external GitLab action), we can set minimal read access at the workflow root, then elevate to pull-requests: write only for the job that needs it, if any. However, the safest and simplest fix without risking breaking current behavior is to add a workflow-level permissions block granting contents: read and pull-requests: write, which is a common least-privilege baseline for CI that mirrors PRs and may report back to them.
Concretely, in .github/workflows/gitlab_ci.yml, we will insert a top-level permissions: section right after the name: and before on:. This will apply to all jobs (including check_permission, mirror_to_gitlab, and get_artifacts_from_other_workflow) since none defines its own permissions. The block will be:
permissions:
contents: read
pull-requests: writeNo additional imports or methods are required; GitHub Actions interprets this YAML key natively. This change documents and restricts the GITHUB_TOKEN while preserving typical functionality: reading the repository, checking out code, and allowing the GitLab mirroring action to update PRs if it currently does so.
| @@ -2,6 +2,10 @@ | ||
|
|
||
| name: Mirror to Gitlab to trigger CI | ||
|
|
||
| permissions: | ||
| contents: read | ||
| pull-requests: write | ||
|
|
||
| on: | ||
| push: | ||
| pull_request_target: |
| runs-on: ubuntu-latest | ||
| if: >- | ||
| (github.repository_owner == 'Parallel-in-Time') && | ||
| ((github.event_name == 'push') || | ||
| (github.event_name == 'schedule') || | ||
| ((github.event_name == 'pull_request_target') && | ||
| (contains(github.event.pull_request.labels.*.name, 'gitlab-mirror')) | ||
| ) | ||
| ) | ||
| needs: | ||
| - check_permission | ||
| steps: | ||
| - name: set proper sha | ||
| run: | | ||
| echo "${{ github.event_name }}" | ||
| if [ "${{ github.event_name }}" == 'push' ] || [ "${{ github.event_name }}" == 'schedule' ] | ||
| then | ||
| echo "USED_SHA=${{ github.sha }}" >> "$GITHUB_ENV" | ||
| fi | ||
| if [ "${{ github.event_name }}" == 'pull_request_target' ] | ||
| then | ||
| echo "USED_SHA=${{ github.event.pull_request.head.sha }}" >> "$GITHUB_ENV" | ||
| fi | ||
| - name: Checkout | ||
| uses: actions/checkout@v4 | ||
| with: | ||
| ref: "${{ env.USED_SHA }}" | ||
| persist-credentials: false | ||
| - name: check if merge is possible (merge is used for testing) | ||
| if: github.event_name == 'pull_request_target' | ||
| run: | | ||
| if $(git rev-parse --is-shallow-repository); then | ||
| git fetch --unshallow | ||
| else | ||
| git fetch | ||
| fi | ||
| echo "Checkout of ${{ github.base_ref }}" | ||
| git checkout "${{ github.base_ref }}" | ||
| echo "Git pull" | ||
| git pull | ||
| MIRROR_BRANCH="TEMPORARY_MERGE_PR_${{ github.event.number }}" | ||
| echo MIRROR_BRANCH="$MIRROR_BRANCH" >> $GITHUB_ENV | ||
| echo "Create new branch $MIRROR_BRANCH and check it out" | ||
| git checkout -b "$MIRROR_BRANCH" | ||
| echo "Setting git committer info, so that merge-commit can be created" | ||
| git config user.email "unused@example.com" | ||
| git config user.name "Sync bot" | ||
| echo "Merge the two parts of the Merge-Request to test the resulting version" | ||
| git merge "${{ github.event.pull_request.head.sha }}" | ||
| - name: Mirror and wait for Gitlab-CI | ||
| uses: jakob-fritz/github2lab_action@v0.8.1 | ||
| env: | ||
| MODE: 'all' # Either 'mirror', 'get_status', 'get_artifact', or 'all' | ||
| GITLAB_TOKEN: ${{ secrets.GITLAB_SECRET }} | ||
| FORCE_PUSH: "true" | ||
| GITLAB_HOSTNAME: "gitlab.jsc.fz-juelich.de" | ||
| GITLAB_PROJECT_ID: "6029" | ||
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | ||
| MIRROR_BRANCH: ${{ env.MIRROR_BRANCH }} | ||
| - name: Unzip downloaded artifacts | ||
| run: | | ||
| pwd | ||
| ls -lah | ||
| cd artifacts | ||
| find . -name "*.zip" -type f -exec unzip -o {} \; | ||
| ls -lah | ||
| rm *.zip | ||
| cd .. | ||
| ls -lah | ||
| - name: Uploading artifacts | ||
| uses: actions/upload-artifact@v4 | ||
| with: | ||
| name: Gitlab-Action_artifacts | ||
| path: | | ||
| ./artifacts/* | ||
|
|
||
| get_artifacts_from_other_workflow: |
Check warning
Code scanning / CodeQL
Workflow does not contain permissions Medium
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI 2 months ago
In general, to fix this issue you add a permissions: section either at the top level of the workflow (applies to all jobs) or per job, granting only the scopes actually needed (often contents: read as a baseline). This prevents the jobs from inheriting broader repository defaults.
For this workflow, the simplest fix without changing functionality is to add a top-level permissions: block (between on: and jobs:) that defines a minimal set of permissions. At a minimum, contents: read is needed for checkouts and for actions that read repository data; other capabilities such as managing pull requests or issues are not used directly in the snippet shown. The actions-cool/check-user-permission action and jakob-fritz/github2lab_action both operate using the GITHUB_TOKEN but do not obviously require write permissions here. Therefore we can safely set:
permissions:
contents: readThis applies to all three jobs (check_permission, mirror_to_gitlab, and get_artifacts_from_other_workflow). No additional imports or methods are needed; this is a pure YAML configuration change within .github/workflows/gitlab_ci.yml.
| @@ -9,6 +9,9 @@ | ||
| schedule: | ||
| - cron: '2 5 * * 1' | ||
|
|
||
| permissions: | ||
| contents: read | ||
|
|
||
| jobs: | ||
| check_permission: | ||
| runs-on: ubuntu-latest |
| runs-on: ubuntu-latest | ||
| needs: | ||
| - mirror_to_gitlab | ||
| steps: | ||
| - name: Download artifacts from this workflow | ||
| uses: actions/download-artifact@v4 | ||
| with: | ||
| merge-multiple: true | ||
| path: ./github_ci_artifacts | ||
| - name: Set env-var | ||
| id: get_id | ||
| uses: actions/github-script@v7 | ||
| env: | ||
| workflow_filename: 'ci_pipeline.yml' | ||
| with: | ||
| script: | | ||
| if (context.eventName == "pull_request_target") { | ||
| var used_sha = context.payload.pull_request.head.sha; | ||
| var used_event = "pull_request"; | ||
| } else { | ||
| var used_sha = context.sha; | ||
| var used_event = context.eventName; | ||
| } | ||
| const result = await github.request('GET /repos/{owner}/{repo}/actions/workflows/{workflow_id}/runs', { | ||
| owner: context.repo.owner, | ||
| repo: context.repo.repo, | ||
| workflow_id: process.env.workflow_filename, | ||
| headers: { | ||
| 'X-GitHub-Api-Version': '2022-11-28', | ||
| 'accept': 'application/vnd.github+json' | ||
| }, | ||
| head_sha: used_sha, | ||
| event: used_event | ||
| }) | ||
| console.log("SHA of commit " + used_sha); | ||
| console.log("Found runs of workflow: " + result.data.total_count); | ||
| if (result.data.total_count == 1) { | ||
| console.log("Found workflow has id: " + result.data.workflow_runs[0].id); | ||
| return result.data.workflow_runs[0].id | ||
| } else { | ||
| console.log("Logging all found workflow ids:"); | ||
| for (var i = 0; i < result.data.workflow_runs.length; i++) { | ||
| console.log(result.data.workflow_runs[i].id); | ||
| } | ||
| console.log("Returned workflow id is: " + result.data.workflow_runs[0].id); | ||
| return result.data.workflow_runs[0].id | ||
| } | ||
| - name: Wait for other workflow to finish | ||
| env: | ||
| RUN_ID: ${{ steps.get_id.outputs.result }} | ||
| POLL_TIMEOUT: 10 | ||
| run: | | ||
| ci_conclusion="pending" | ||
| echo "Querying status of workflow $RUN_ID for repo $GITHUB_REPOSITORY" | ||
| until [ "$ci_conclusion" != "pending" ] && [ "$ci_conclusion" != "in_progress" ] && [ "$ci_conclusion" != "null" ] | ||
| do | ||
| # Wait some seconds | ||
| sleep "$POLL_TIMEOUT" | ||
| # Get the current state of the pipeline and the url of the website | ||
| run_reply=$(curl --header "'X-GitHub-Api-Version': '2022-11-28', 'accept': 'application/vnd.github+json'" --silent "https://api.github.com/repos/$GITHUB_REPOSITORY/actions/runs/$RUN_ID") | ||
| ci_conclusion=$(jq -n "$run_reply" | jq -r .conclusion) | ||
| echo "Current pipeline status: ${ci_conclusion}" | ||
| http_status=$(jq -n "$run_reply" | jq -r .status) | ||
| if [ "$http_status" != 200 ] && [[ "$http_status" =~ ^[0-9]+$ ]]; then | ||
| echo "Request returned status: ${http_status}" | ||
| exit 1 | ||
| fi | ||
| done | ||
| # Set exit code for success or failure (everything non-success) | ||
| if [ "$ci_conclusion" = "success" ]; then | ||
| exit 0 | ||
| else | ||
| exit 1 | ||
| fi | ||
| - name: Download artifacts from other workflow | ||
| uses: actions/download-artifact@v4 | ||
| with: | ||
| merge-multiple: true | ||
| run-id: ${{ steps.get_id.outputs.result }} | ||
| github-token: ${{ secrets.ACTION_READ_TOKEN }} | ||
| path: ./github_ci_artifacts | ||
| - name: Uploading artifacts | ||
| uses: actions/upload-artifact@v4 | ||
| with: | ||
| name: Github_CI_artifacts | ||
| path: | | ||
| ./github_ci_artifacts/* |
Check warning
Code scanning / CodeQL
Workflow does not contain permissions Medium
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI 2 months ago
In general, the fix is to add an explicit permissions block either at the workflow root (to apply to all jobs) or per job, granting only the minimal permissions needed. For this workflow, contents: read is a safe baseline, and additional permissions can be granted only where clearly required. The mirror_to_gitlab job uses secrets.GITHUB_TOKEN in an action that likely needs to read repo contents and CI status but not push code, so contents: read is appropriate. The get_artifacts_from_other_workflow job uses actions/github-script to list workflow runs and curl plus an explicit token for runs/artifacts; for listing and reading artifacts, actions: read and contents: read are sufficient, and write-level scopes are not needed.
The single best minimally invasive change that satisfies CodeQL is to add a root-level permissions block after the on: key with restrictive settings, e.g. contents: read and actions: read. This will apply to all jobs (check_permission, mirror_to_gitlab, and get_artifacts_from_other_workflow) since none define their own permissions. No other functional changes are necessary because these jobs are already using the default GITHUB_TOKEN; we are only reducing its capabilities to the level needed for read-only operations against code and workflows.
Concretely, in .github/workflows/gitlab_ci.yml, insert:
permissions:
contents: read
actions: readbetween the on: block (ending at line 10) and the jobs: block (line 12). No imports or additional methods are required.
| @@ -9,6 +9,10 @@ | ||
| schedule: | ||
| - cron: '2 5 * * 1' | ||
|
|
||
| permissions: | ||
| contents: read | ||
| actions: read | ||
|
|
||
| jobs: | ||
| check_permission: | ||
| runs-on: ubuntu-latest |
Original prompt
💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.