diff options
| author | Marc Cornellà <marc@mcornella.com> | 2025-09-27 20:00:50 +0200 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2025-09-27 20:00:50 +0200 |
| commit | 242e2faa51675494cbfa78a81f3ff47d81039863 (patch) | |
| tree | 77aa76eda5e1ebad168e219691f88aa09f876757 | |
| parent | 6d5482ef59d1a3ae3b40b4583317ebe802f81447 (diff) | |
| download | zsh-242e2faa51675494cbfa78a81f3ff47d81039863.tar.gz zsh-242e2faa51675494cbfa78a81f3ff47d81039863.tar.bz2 zsh-242e2faa51675494cbfa78a81f3ff47d81039863.zip | |
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
| -rw-r--r-- | .github/workflows/project.yml | 64 |
1 files 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 <<EOF + cat >> "$GITHUB_ENV" <<EOF PROJECT_ID=$(jq '.data.organization.projectV2.id' project_data.json) PLUGIN_FIELD_ID=$(jq '.data.organization.projectV2.fields.nodes[] | select(.name == "Plugin") | .id' project_data.json) THEME_FIELD_ID=$(jq '.data.organization.projectV2.fields.nodes[] | select(.name == "Theme") | .id' project_data.json) EOF - - name: Add to project env: + GH_TOKEN: ${{ steps.generate-token.outputs.token }} ISSUE_OR_PR_ID: ${{ github.event.issue.node_id || github.event.pull_request.node_id }} run: | item_id="$(gh api graphql -f query=' @@ -71,45 +69,51 @@ jobs: } } } - ' -f project=$PROJECT_ID -f content=$ISSUE_OR_PR_ID --jq '.data.addProjectV2ItemById.item.id')" + ' -f project="$PROJECT_ID" -f content="$ISSUE_OR_PR_ID" --jq '.data.addProjectV2ItemById.item.id')" echo "ITEM_ID=$item_id" >> $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 |
