From 242e2faa51675494cbfa78a81f3ff47d81039863 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc=20Cornell=C3=A0?= Date: Sat, 27 Sep 2025 20:00:50 +0200 Subject: [PATCH] ci: improve security in project.yml workflow (#13329) There is no inherent security vulnerability in the workflow, but there were certain practices that increased latent risk. In this commit, we: - Explicitly bind app token for each step that needs it, instead of setting it for all steps after "Store app token" - Refactor "classify" step, to not rely on files passed around, and instead uses only awk script. - Remove all instances of template injection within `run` scripts. There was nothing dangerous, but the practice is unsafe. - Sanitize all unwanted characters from PR plugin and theme names. References: W2M1-06 W2M1-07 --- .github/workflows/project.yml | 64 +++++++++++++++++++---------------- 1 file changed, 34 insertions(+), 30 deletions(-) diff --git a/.github/workflows/project.yml b/.github/workflows/project.yml index ba971db15..e6da2cbe5 100644 --- a/.github/workflows/project.yml +++ b/.github/workflows/project.yml @@ -20,17 +20,15 @@ jobs: uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.1 with: egress-policy: audit - - name: Authenticate as @ohmyzsh id: generate-token uses: actions/create-github-app-token@67018539274d69449ef7c02e8e71183d1719ab42 # v2.1.4 with: app-id: ${{ secrets.OHMYZSH_APP_ID }} private-key: ${{ secrets.OHMYZSH_APP_PRIVATE_KEY }} - - name: Store app token - run: echo "GH_TOKEN=${{ steps.generate-token.outputs.token }}" >> "$GITHUB_ENV" - name: Read project data env: + GH_TOKEN: ${{ steps.generate-token.outputs.token }} ORGANIZATION: ohmyzsh PROJECT_NUMBER: "1" run: | @@ -53,14 +51,14 @@ jobs: }' -f org=$ORGANIZATION -F number=$PROJECT_NUMBER > project_data.json # Parse project data - cat >> $GITHUB_ENV <> "$GITHUB_ENV" <> $GITHUB_ENV - - name: Classify Pull Request if: github.event_name == 'pull_request_target' + env: + GH_TOKEN: ${{ steps.generate-token.outputs.token }} + PR_NUMBER: ${{ github.event.pull_request.number }} run: | - touch plugins.list themes.list - - gh pr view ${{ github.event.pull_request.number }} \ - --repo ${{ github.repository }} \ + # Get the list of modified files in the PR, and extract plugins and themes + gh pr view "$PR_NUMBER" \ + --repo "$GITHUB_REPOSITORY" \ --json files --jq '.files.[].path' | awk -F/ ' + BEGIN { + plugins = 0 + themes = 0 + } /^plugins\// { - plugins[$2] = 1 + if (plugin == $2) next + plugin = $2 + plugins++ } /^themes\// { gsub(/\.zsh-theme$/, "", $2) - themes[$2] = 1 + if (theme == $2) next + theme = $2 + themes++ } END { - for (plugin in plugins) { - print plugin >> "plugins.list" + # plugin and theme are values controlled by the PR author + # so we should sanitize them before using anywhere else + if (plugins == 1) { + gsub(/[^a-zA-Z0-9._-]/, "", plugin) + print "PLUGIN=" plugin } - for (theme in themes) { - print theme >> "themes.list" + if (themes == 1) { + gsub(/[^a-zA-Z0-9._-]/, "", theme) + print "THEME=" theme } } - ' - # If only one plugin is modified, add it to the plugin field - if [[ $(wc -l < plugins.list) = 1 ]]; then - echo "PLUGIN=$(cat plugins.list)" >> $GITHUB_ENV - fi - # If only one theme is modified, add it to the theme field - if [[ $(wc -l < themes.list) = 1 ]]; then - echo "THEME=$(cat themes.list)" >> $GITHUB_ENV - fi - + ' >> "$GITHUB_ENV" - name: Fill Pull Request fields in project if: github.event_name == 'pull_request_target' + env: + GH_TOKEN: ${{ steps.generate-token.outputs.token }} run: | gh api graphql -f query=' mutation ( @@ -145,7 +149,7 @@ jobs: } } } - ' -f project=$PROJECT_ID -f item=$ITEM_ID \ - -f plugin_field=$PLUGIN_FIELD_ID -f plugin_value=$PLUGIN \ - -f theme_field=$THEME_FIELD_ID -f theme_value=$THEME \ + ' -f project="$PROJECT_ID" -f item="$ITEM_ID" \ + -f plugin_field="$PLUGIN_FIELD_ID" -f plugin_value="$PLUGIN" \ + -f theme_field="$THEME_FIELD_ID" -f theme_value="$THEME" \ --silent