-
Notifications
You must be signed in to change notification settings - Fork 0
chore: Implement automated screenshot diff reporting #182
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 1 commit
525f6d1
2d58322
5e03621
3b7ef55
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,100 @@ | ||
| name: screenshot-comparison-comment | ||
|
|
||
| on: | ||
| workflow_run: | ||
| workflows: ["screenshot-comparison"] | ||
| types: [completed] | ||
|
|
||
| jobs: | ||
| comment: | ||
| if: github.event.workflow_run.conclusion == 'success' | ||
| runs-on: ubuntu-latest | ||
| permissions: | ||
| contents: write | ||
| pull-requests: write | ||
| actions: read | ||
|
|
||
| steps: | ||
| - name: Download Diff Artifact | ||
| uses: dawidd6/action-download-artifact@v3 | ||
| with: | ||
| name: screenshot-diff-reports | ||
| run_id: ${{ github.event.workflow_run.id }} | ||
| path: diff-reports | ||
|
|
||
| - id: get-pull-request-number | ||
| run: echo "pull_request_number=$(cat diff-reports/pr/NR)" >> "$GITHUB_OUTPUT" | ||
|
|
||
| - name: Checkout Default Branch | ||
| uses: actions/checkout@v4 | ||
| with: | ||
| ref: ${{ github.event.repository.default_branch }} | ||
|
|
||
| - name: Push to Companion Branch | ||
| env: | ||
| BRANCH_NAME: companion_${{ github.event.workflow_run.head_branch }} | ||
| run: | | ||
| git checkout --orphan "$BRANCH_NAME" | ||
| git rm -rf . | ||
| find diff-reports -type f -name "*.png" -exec cp {} . \; | ||
|
|
||
| if [ -n "$(git status --porcelain)" ]; then | ||
| git config --global user.name "ScreenshotBot" | ||
| git config --global user.email "bot@github.com" | ||
| git add . | ||
| git commit -m "Add screenshot diffs" | ||
| git push origin "$BRANCH_NAME" -f | ||
| echo "exists=true" >> "$GITHUB_ENV" | ||
| fi | ||
|
Comment on lines
+28
to
+48
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Companion-branch push step has two correctness gaps.
🤖 Prompt for AI Agents |
||
|
|
||
| - name: Generate Report Body | ||
| if: env.exists == 'true' | ||
| id: generate_report | ||
| run: | | ||
| BRANCH="companion_${{ github.event.workflow_run.head_branch }}" | ||
| REPO="${{ github.repository }}" | ||
|
|
||
| echo "REPORT_BODY<<EOF" >> $GITHUB_OUTPUT | ||
| echo "### Roborazzi Snapshot Diff Report" >> $GITHUB_OUTPUT | ||
| echo "" >> $GITHUB_OUTPUT | ||
| echo "| File Name | Comparison |" >> $GITHUB_OUTPUT | ||
| echo "|---|---|" >> $GITHUB_OUTPUT | ||
|
|
||
| for file in $(ls *.png | grep _compare.png); do | ||
| URL="https://github.com/${REPO}/blob/${BRANCH}/${file}?raw=true" | ||
| DISPLAY_NAME=$(echo $file | sed -r 's/(.{20})/\1<br>/g') | ||
| echo "| $DISPLAY_NAME |  |" >> $GITHUB_OUTPUT | ||
| done | ||
| echo "EOF" >> $GITHUB_OUTPUT | ||
|
Comment on lines
+50
to
+68
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🌐 Web query:
💡 Result: GitHub Actions workflows triggered by the workflow_run event carry significant security risks, particularly for script injection vulnerabilities involving potentially untrusted data like head_branch. Official GitHub documentation warns that running untrusted code on workflow_run can lead to cache poisoning, unintended write access, and secret exposure. The workflow_run event provides access to the triggering workflow's details via github.event.workflow_run, including fields like head_branch, head_sha, event (e.g., 'pull_request'), ref, and head_repository. If the triggering workflow was executed from a pull request (especially from a fork via pull_request trigger), head_branch reflects the PR's source branch, which attackers can control to inject malicious payloads (e.g., branch names like zzz";echo${IFS}"hello";# or fooidbar). Script injection occurs when expressions like ${{ github.event.workflow_run.head_branch }} are interpolated directly into run: steps without sanitization. GitHub evaluates ${{ }} expressions before shell execution, substituting untrusted values into scripts, enabling command injection. This is exacerbated in privileged workflow_run workflows (with secrets/write permissions) triggered by unprivileged ones compromised via PRs. Examples from GitHub Security Lab and advisories (e.g., GHSL-2024-326) show branch name injection in workflows, analogous to head_branch. Tools like OSS-F Scorecard flag workflow_run usage due to chaining risks allowing PRs to trigger privileged execution. Mitigations from GitHub docs: - Treat github.event.* (especially ending in ref, head_ref, head_branch) as untrusted. - Use env: for intermediate variables: env: BRANCH: ${{ github.event.workflow_run.head_branch }} then run: echo "$BRANCH". - Prefer JavaScript actions for processing. - Add guards: if: github.event_name != 'pull_request' && github.repository == github.event.workflow_run.head_repository.full_name. - Pin branches: to trusted ones, limit permissions: { contents: read }. - Use branch filters and verify event origin. No specific CVE for workflow_run.head_branch, but pattern matches documented script injection risks in privileged triggers. Citations:
🏁 Script executed: # Locate the workflow file
git ls-files | grep -E '\.github/workflows.*screenshot.*\.yml'Repository: komodgn/meta-android Length of output: 159 🏁 Script executed: cat -n .github/workflows/screenshot-comparison-comment.yml | head -80Repository: komodgn/meta-android Length of output: 3374 Script injection vulnerability:
Fix - name: Generate Report Body
if: env.exists == 'true'
id: generate_report
+ env:
+ HEAD_BRANCH: ${{ github.event.workflow_run.head_branch }}
+ REPO: ${{ github.repository }}
run: |
- BRANCH="companion_${{ github.event.workflow_run.head_branch }}"
- REPO="${{ github.repository }}"
+ BRANCH="companion_${HEAD_BRANCH}"🤖 Prompt for AI Agents |
||
|
|
||
| - name: Find Old Comment | ||
| if: env.exists == 'true' | ||
| uses: peter-evans/find-comment@v3 | ||
| id: fc | ||
| with: | ||
| issue-number: ${{ steps.get-pull-request-number.outputs.pull_request_number }} | ||
| comment-author: 'github-actions[bot]' | ||
| body-includes: 'Roborazzi Snapshot Diff Report' | ||
|
|
||
| - name: Add or Update Comment | ||
| if: env.exists == 'true' | ||
| uses: peter-evans/create-or-update-comment@v4 | ||
| with: | ||
| comment-id: ${{ steps.fc.outputs.comment-id }} | ||
| issue-number: ${{ steps.get-pull-request-number.outputs.pull_request_number }} | ||
| body: ${{ steps.generate_report.outputs.REPORT_BODY }} | ||
| edit-mode: replace | ||
|
|
||
| - name: Cleanup outdated companion branches | ||
| shell: bash | ||
| run: | | ||
| git branch -r --format="%(refname:lstrip=3)" | grep companion_ | while read -r branch; do | ||
| last_commit_date_timestamp=$(git log -1 --format=%ct "origin/$branch") | ||
| now_timestamp=$(date +%s) | ||
|
|
||
| # 30 days | ||
| if [ $((now_timestamp - last_commit_date_timestamp)) -gt 2592000 ]; then | ||
| echo "Deleting outdated branch: $branch" | ||
| git push origin --delete "$branch" | ||
| fi | ||
| done | ||
|
Comment on lines
+88
to
+100
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Cleanup step won't see remote branches due to shallow checkout.
Either set Also consider adding 🤖 Prompt for AI Agents |
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,66 @@ | ||
| name: screenshot-comparison | ||
|
|
||
| on: | ||
| pull_request: | ||
| paths-ignore: | ||
| - '**.md' | ||
|
|
||
| concurrency: | ||
| group: ${{ github.workflow }}-${{ github.head_ref }} | ||
| cancel-in-progress: true | ||
|
|
||
| jobs: | ||
| screenshot-comparison: | ||
| runs-on: ubuntu-latest | ||
| permissions: | ||
| contents: read | ||
| actions: write | ||
| checks: read | ||
|
|
||
| steps: | ||
| - name: Checkout PR Branch | ||
| uses: actions/checkout@v4 | ||
|
|
||
| - name: Get base branch HEAD commit | ||
| id: get_base_branch_head | ||
| run: | | ||
| git fetch origin ${{ github.base_ref }} | ||
| echo "sha=$(git rev-parse origin/${{ github.base_ref }})" >> "$GITHUB_OUTPUT" | ||
|
|
||
| - name: Setup Android | ||
| uses: ./.github/actions/setup-android | ||
| env: | ||
| LOCAL_PROPERTIES: ${{ secrets.LOCAL_PROPERTIES }} | ||
|
|
||
| - name: Wait for base unit-test | ||
| uses: lewagon/wait-on-check-action@v1.3.4 | ||
| with: | ||
| ref: ${{ steps.get_base_branch_head.outputs.sha }} | ||
| check-name: 'unit-test' | ||
| repo-token: ${{ github.token }} | ||
| wait-interval: 10 | ||
|
|
||
| - name: Download Base Screenshot | ||
| uses: dawidd6/action-download-artifact@v3 | ||
| continue-on-error: true | ||
| with: | ||
| name: test-reports | ||
| workflow: unit-test.yml | ||
| commit: ${{ steps.get_base_branch_head.outputs.sha }} | ||
|
|
||
| - name: Run Comparison | ||
| run: ./gradlew compareRoborazziDebug --stacktrace | ||
|
Comment on lines
+43
to
+52
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Handle missing base-branch artifact explicitly.
Consider detecting the download outcome ( 🤖 Prompt for AI Agents |
||
|
|
||
| - name: Save PR number for comment | ||
| run: | | ||
| mkdir -p ./pr | ||
| echo ${{ github.event.number }} > ./pr/NR | ||
|
|
||
| - name: Upload Diff Results | ||
| if: always() | ||
| uses: actions/upload-artifact@v4 | ||
| with: | ||
| name: screenshot-diff-reports | ||
| path: | | ||
| **/build/outputs/roborazzi | ||
| ./pr/NR | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Validate
pull_request_numberread from the untrusted artifact.diff-reports/pr/NRoriginates from the PR-triggeredscreenshot-comparisonworkflow's artifact, which is attacker-controllable content in theworkflow_runthreat model. Its value is then passed topeter-evans/find-commentandpeter-evans/create-or-update-commentasissue-number(Lines 75, 84). A non-numeric or multi-line payload could cause the bot to comment on an arbitrary issue/PR in the repo, or fail in non-obvious ways.Sanitize before use, e.g.:
🤖 Prompt for AI Agents