Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 6 additions & 4 deletions .github/workflows/close_pylon_issue.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,14 @@ jobs:

- name: Close Pylon Ticket
if: steps.extract_ticket_id.outputs.pylon_ticket_id != ''
env:
PYLON_TICKET_ID: ${{ steps.extract_ticket_id.outputs.pylon_ticket_id }}
PYLON_API_KEY: ${{ secrets.PYLON_API_KEY }}
run: |
Comment on lines 24 to 29
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🌐 Web query:

Does GitHub Actions pull_request_target expose repository secrets to workflows triggered by forked pull requests, and what mitigations are recommended when processing untrusted PR body content?

💡 Result:

Yes, GitHub Actions workflows triggered by the pull_request_target event have access to repository secrets, even when triggered by pull requests from forked repositories. This is because pull_request_target runs in the context of the base (target) repository's default branch, granting it read access to repository secrets and a GITHUB_TOKEN with write permissions by default. However, this introduces significant security risks, known as "pwn requests," where a malicious PR author can exploit the workflow to execute untrusted code from the fork while having access to secrets. For example, if the workflow checks out the PR head SHA (e.g., github.event.pull_request.head.sha), runs builds, or processes untrusted inputs like the PR body, an attacker can inject code to exfiltrate secrets. Recommended mitigations when using pull_request_target, especially for processing untrusted PR body content: - Never check out, build, or run untrusted code from the PR (e.g., avoid actions/checkout with ref: ${{ github.event.pull_request.head.sha }}). Use only trusted base branch code. - Do not pass secrets as environment variables or inputs to steps that could execute untrusted code. The GITHUB_TOKEN is still accessible in memory even without explicit use. - Restrict to metadata-only operations (e.g., labeling, commenting based on PR title/body without executing code). - Add conditions to skip forks: if: github.event.pull_request.head.repo.full_name == github.repository. - Use label-based gating: Trigger only on specific labels (e.g., types: [labeled]) applied manually after review. - Split workflows: Use pull_request (unprivileged, no secrets) for building untrusted code, then workflow_run (privileged) for post-processing. - Set minimal permissions: permissions: { contents: read } or less. - For PR body processing: Sanitize inputs strictly; avoid dynamic code execution (e.g., no eval on body content). - Enable required reviewers for workflows from forks in repo settings. - Use GitHub Environments with required approvers for secret access. - Scan workflows with CodeQL for vulnerabilities. Prefer pull_request trigger for CI/testing on forks, as it has no secret access.

Citations:


🏁 Script executed:

cat .github/workflows/close_pylon_issue.yml

Repository: elementary-data/elementary

Length of output: 1364


Harden pull_request_target trust gating before using secrets.PYLON_API_KEY and add error handling to the curl call.

This workflow has two issues:

  1. Security: Line 25 only checks that an ID exists. On pull_request_target, PR body content is untrusted input from forked PRs; this lets external contributors drive a privileged API call (closing arbitrary Pylon tickets) by embedding a valid ticket ID. The condition must verify the request comes from a trusted source.

  2. Reliability: The curl command (lines 31-35) lacks --fail and timeout flags, so HTTP errors won't fail the step and requests could hang indefinitely. The step will report success even if the API call fails.

🔒 Suggested fixes
      - name: Close Pylon Ticket
-       if: steps.extract_ticket_id.outputs.pylon_ticket_id != ''
+       if: |
+         steps.extract_ticket_id.outputs.pylon_ticket_id != '' &&
+         (
+           github.event_name == 'issues' ||
+           github.event.pull_request.head.repo.full_name == github.repository
+         )
        env:
          PYLON_TICKET_ID: ${{ steps.extract_ticket_id.outputs.pylon_ticket_id }}
          PYLON_API_KEY: ${{ secrets.PYLON_API_KEY }}
        run: |
          echo "Closing Pylon Ticket ID: $PYLON_TICKET_ID"
          curl --fail --connect-timeout 10 --max-time 30 \
            --request PATCH \
            --url "https://api.usepylon.com/issues/$PYLON_TICKET_ID" \
            --header "Authorization: $PYLON_API_KEY" \
            --header 'Content-Type: application/json' \
            --data '{
              "state": "closed"
            }'
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
- name: Close Pylon Ticket
if: steps.extract_ticket_id.outputs.pylon_ticket_id != ''
env:
PYLON_TICKET_ID: ${{ steps.extract_ticket_id.outputs.pylon_ticket_id }}
PYLON_API_KEY: ${{ secrets.PYLON_API_KEY }}
run: |
- name: Close Pylon Ticket
if: |
steps.extract_ticket_id.outputs.pylon_ticket_id != '' &&
(
github.event_name == 'issues' ||
github.event.pull_request.head.repo.full_name == github.repository
)
env:
PYLON_TICKET_ID: ${{ steps.extract_ticket_id.outputs.pylon_ticket_id }}
PYLON_API_KEY: ${{ secrets.PYLON_API_KEY }}
run: |
echo "Closing Pylon Ticket ID: $PYLON_TICKET_ID"
curl --fail --connect-timeout 10 --max-time 30 \
--request PATCH \
--url "https://api.usepylon.com/issues/$PYLON_TICKET_ID" \
--header "Authorization: $PYLON_API_KEY" \
--header 'Content-Type: application/json' \
--data '{
"state": "closed"
}'
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/close_pylon_issue.yml around lines 24 - 29, The "Close
Pylon Ticket" step currently trusts an unvalidated PYLON_TICKET_ID and makes an
unreliable curl call; update the step condition to gate use of PYLON_API_KEY by
verifying the event is trusted (for pull_request_target ensure
github.event.pull_request.head.repo.full_name == github.repository or require
event_name not equal to "pull_request_target") so secrets are never used for
forked PRs, and harden the HTTP call that closes the ticket (the curl
invocation) by adding --fail, --silent/--show-error and a --max-time timeout and
explicitly check curl's exit status and HTTP response to fail the step on
non-2xx responses; reference the "Close Pylon Ticket" step, the PYLON_TICKET_ID
and PYLON_API_KEY env vars, and the curl command in the run block when making
these changes.

pylon_ticket_id=${{ steps.extract_ticket_id.outputs.pylon_ticket_id }}
echo "Closing Pylon Ticket ID: $pylon_ticket_id"
echo "Closing Pylon Ticket ID: $PYLON_TICKET_ID"
curl --request PATCH \
--url "https://api.usepylon.com/issues/$pylon_ticket_id" \
--header "Authorization: ${{ secrets.PYLON_API_KEY }}" \
--url "https://api.usepylon.com/issues/$PYLON_TICKET_ID" \
--header "Authorization: $PYLON_API_KEY" \
--header 'Content-Type: application/json' \
--data '{
"state": "closed"
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ on:
type: string
push:
tags:
- v[0-9].[0-9]+.[0-9]+
- v[0-9]+.[0-9]+.[0-9]+

env:
REGISTRY: ghcr.io
Expand Down
13 changes: 9 additions & 4 deletions .github/workflows/test-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,13 @@ jobs:

- name: Bump tag version
id: bump-tag
env:
PY_TAG: ${{ steps.get-tag.outputs.py }}
DBT_TAG: ${{ steps.get-tag.outputs.dbt }}
run: |
echo "py-bumped=$(echo ${{ steps.get-tag.outputs.py }} | awk -F. '/[0-9]+\./{$NF++;print}' OFS=.)" >> $GITHUB_OUTPUT
echo "py-bumped-number=$(echo ${{ steps.get-tag.outputs.py }} | awk -F. '/[0-9]+\./{$NF++;print}' OFS=. | cut -c 2-)" >> $GITHUB_OUTPUT
echo "dbt-bumped=$(echo ${{ steps.get-tag.outputs.dbt }} | awk -F. '/[0-9]+\./{$NF++;print}' OFS=.)" >> $GITHUB_OUTPUT
echo "py-bumped=$(echo "$PY_TAG" | awk -F. '/[0-9]+\./{$NF++;print}' OFS=.)" >> "$GITHUB_OUTPUT"
echo "py-bumped-number=$(echo "$PY_TAG" | awk -F. '/[0-9]+\./{$NF++;print}' OFS=. | cut -c 2-)" >> "$GITHUB_OUTPUT"
echo "dbt-bumped=$(echo "$DBT_TAG" | awk -F. '/[0-9]+\./{$NF++;print}' OFS=.)" >> "$GITHUB_OUTPUT"

- name: Confirm release tag
run: |
Expand Down Expand Up @@ -74,8 +77,10 @@ jobs:
contents: read
steps:
- name: recommend unbreaking change upgrade
env:
PY_BUMPED_NUMBER: ${{ needs.get-latest-release-tags.outputs.py-bumped-number }}
run: |
echo "Everything ran successfully, you can bump version to ${{ needs.get-latest-release-tags.outputs.py-bumped-number }}"
echo "Everything ran successfully, you can bump version to $PY_BUMPED_NUMBER"
- name: recommend breaking change upgrade
if: ${{ failure() }}
uses: actions/github-script@v8
Expand Down
10 changes: 5 additions & 5 deletions .github/workflows/test-warehouse.yml
Original file line number Diff line number Diff line change
Expand Up @@ -446,13 +446,13 @@ jobs:
SLACK_TOKEN: ${{ secrets.CI_SLACK_TOKEN }}
run: >
edr monitor send-report
-t "${{ inputs.warehouse-type }}"
--project-dir "${{ env.E2E_DBT_PROJECT_DIR }}"
--project-profile-target "${{ inputs.warehouse-type }}"
--slack-file-name "report_${{ inputs.warehouse-type }}_${{ env.BRANCH_NAME }}.html"
-t "$WAREHOUSE_TYPE"
--project-dir "$E2E_DBT_PROJECT_DIR"
--project-profile-target "$WAREHOUSE_TYPE"
--slack-file-name "report_${WAREHOUSE_TYPE}_${BRANCH_NAME}.html"
--slack-token "$SLACK_TOKEN"
--slack-channel-name oss-ci-tests
--bucket-file-path "ci_reports/report_${{ inputs.warehouse-type }}_${{ env.BRANCH_NAME }}.html"
--bucket-file-path "ci_reports/report_${WAREHOUSE_TYPE}_${BRANCH_NAME}.html"
--s3-bucket-name elementary-ci-artifacts
--update-bucket-website true

Expand Down
5 changes: 3 additions & 2 deletions .github/workflows/triage-labels.yml
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
name: Update Triage Label
on: issue_comment

permissions:
issues: write
permissions: {}

jobs:
triage_label:
if: contains(github.event.issue.labels.*.name, 'Awaiting Response')
runs-on: ubuntu-latest
permissions:
issues: write
steps:
- name: Update label
# actions/github-script v8, checked 2026-04-26.
Expand Down
Loading