Refactor: Workflow security and trigger updates#133
Refactor: Workflow security and trigger updates#133ModeSevenIndustrialSolutions wants to merge 4 commits intolfreleng-actions:mainfrom
Conversation
There was a problem hiding this comment.
Pull request overview
This pull request refactors the GitHub Actions workflows for the GitHub2Gerrit integration, transitioning from pull_request_target to pull_request triggers for improved security. The changes ensure that only trusted automation bot PRs (dependabot, pre-commit-ci) are processed, as these bots push branches to the base repository rather than from forks. The PR also adds two new specialized workflows for Gerrit merge and verification operations, and updates the Python code to support both trigger types for backward compatibility.
Changes:
- Updated Python code to support both
pull_requestandpull_request_targetevent triggers - Refactored
github2gerrit.yamlfrom a reusable workflow to a standalone workflow using the saferpull_requesttrigger - Added
gerrit-merge-github2gerrit.yamlworkflow with Gerrit notification and status reporting jobs - Added
gerrit-verify-github2gerrit.yamlworkflow for Gerrit patchset verification and reconciliation
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| src/github2gerrit/models.py | Updated get_operation_mode() to accept both pull_request and pull_request_target events with security documentation |
| src/github2gerrit/core.py | Extended Change-ID reuse logic and PR closure policy to support both event types |
| .github/workflows/github2gerrit.yaml | Completely refactored to standalone workflow with pull_request trigger and simplified inputs using external action |
| .github/workflows/gerrit-verify-github2gerrit.yaml | New workflow for Gerrit-triggered verification without voting |
| .github/workflows/gerrit-merge-github2gerrit.yaml | New workflow with Gerrit notification, metadata collection, and status reporting jobs |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
ModeSevenIndustrialSolutions
left a comment
There was a problem hiding this comment.
Addressed all three Copilot review findings in the amended commit:
-
Test file changes: Updated
tests/test_core_close_pr_policy.py— renamedtest_close_pr_not_invoked_for_non_target_eventtotest_close_pr_not_invoked_for_non_pr_event(now usesworkflow_dispatchinstead ofpull_request), and added a newtest_close_pr_invoked_for_pull_request_eventtest that verifiespull_requestevents trigger PR closure. -
repository-metadata job: Added an explanatory comment clarifying it runs for observability/traceability and its outputs are consumed by external dashboards/reports.
-
Test breakage: The new
test_close_pr_invoked_for_pull_request_eventtest confirms thatpull_requestevents now correctly trigger closure, and the existing non-target-event test was updated to useworkflow_dispatchinstead.
cd4e48c to
dc1cc8c
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 8 out of 8 changed files in this pull request and generated 5 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| - name: "Security scan with Grype (SARIF)" | ||
| # yamllint disable-line rule:line-length | ||
| uses: anchore/scan-action@7037fa011853d5a11690026fb85feee79f4c946c # v7.3.2 | ||
| id: grype-sarif | ||
| # The first Grype audit should not be blocking and should never fail | ||
| # That way we always get the required artefact; the second job will | ||
| # exit with an error, as required (below) | ||
| continue-on-error: true | ||
| with: | ||
| sbom: "${{ steps.sbom.outputs.sbom_json_path }}" | ||
| output-format: "sarif" | ||
| output-file: "grype-results.sarif" | ||
| fail-build: "true" | ||
|
|
||
| - name: "Summary step" | ||
| - name: "Security scan with Grype (Text/Table)" | ||
| # yamllint disable-line rule:line-length | ||
| uses: anchore/scan-action@7037fa011853d5a11690026fb85feee79f4c946c # v7.3.2 | ||
| id: grype-table | ||
| if: always() | ||
| with: | ||
| sbom: "${{ steps.sbom.outputs.sbom_json_path }}" | ||
| output-format: "table" | ||
| output-file: "grype-results.txt" | ||
| fail-build: "false" |
There was a problem hiding this comment.
The Grype scan no longer fails the workflow on vulnerabilities: the SARIF scan sets fail-build: "true" but also continue-on-error: true, and the follow-up table scan sets fail-build: "false". As written, this job will always succeed even when Grype finds findings, which is a security regression vs a blocking scan. Either remove continue-on-error from the blocking scan, or add an explicit failing step based on steps.grype-sarif.outcome/exit code (and keep SARIF upload non-blocking separately).
There was a problem hiding this comment.
This has been addressed. The current PR now includes a "Check Grype scan results" step that checks steps.grype-sarif.outcome == 'failure' and exits with error code 1. The continue-on-error: true on the SARIF step allows subsequent steps (table output, artifact upload, summary) to complete before the explicit failure step fires. This is the intended pattern.
| # secrets and write access to untrusted fork code. The default | ||
| # AUTOMATION_ONLY=true setting ensures only trusted bot PRs (dependabot, | ||
| # pre-commit-ci) are processed — these bots push branches to the base | ||
| # repository, not from forks, so pull_request has full access to secrets. |
There was a problem hiding this comment.
This workflow uses the pull_request trigger but always passes Gerrit auth secrets (secrets.GERRIT_SSH_PRIVKEY_G2G). For PRs coming from forks, GitHub Actions will not provide repository secrets, so the action will receive an empty key and is likely to fail noisily. Consider adding a job-level guard to only run when github.event.pull_request.head.repo.full_name == github.repository (or similar), or otherwise explicitly handle the "no secrets on forks" case to avoid unexpected failures.
| # repository, not from forks, so pull_request has full access to secrets. | |
| # repository, not from forks, so pull_request has full access to secrets. | |
| # NOTE: Jobs that use Gerrit auth secrets MUST also include a job-level | |
| # `if:` condition to skip execution for fork-based PRs, for example: | |
| # if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository |
There was a problem hiding this comment.
Stale — the workflow-templates/ directory has been removed from this PR. These templates require further testing and will be submitted separately.
| # Security: uses pull_request (not pull_request_target) to avoid granting | ||
| # secrets and write access to untrusted fork code. The default | ||
| # AUTOMATION_ONLY=true setting ensures only trusted bot PRs (dependabot, | ||
| # pre-commit-ci) are processed — these bots push branches to the base | ||
| # repository, not from forks, so pull_request has full access to secrets. | ||
| pull_request: |
There was a problem hiding this comment.
Because this runs on pull_request and always supplies Gerrit secrets, PRs from forks will not have access to secrets.GERRIT_SSH_PRIVKEY_G2G and the workflow is likely to fail (secrets are not provided to forked PR workflows). Add a job-level if to restrict execution to same-repo PRs (or handle missing secrets explicitly) to prevent noisy failures.
There was a problem hiding this comment.
Stale — the workflow-templates/ directory has been removed from this PR. These templates require further testing and will be submitted separately.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 8 out of 8 changed files in this pull request and generated 4 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Addressing Copilot review feedback (second cycle)All 5 unresolved findings from the second Copilot review have been fixed. Details below: 1. Boolean expression bug in
|
0b6b698 to
03c33a4
Compare
03c33a4 to
6720f8c
Compare
Run Grype in both SARIF and table output formats to provide machine-readable results alongside human-readable summaries. Upload both artifacts for retention. Add a dedicated summary step that writes SBOM and vulnerability information to GITHUB_STEP_SUMMARY. The SARIF scan uses continue-on-error to ensure artifacts are always collected, with an explicit "Fail on Grype findings" step that checks the SARIF outcome and exits non-zero when vulnerabilities are found. Co-Authored-By: Claude <noreply@anthropic.com> Signed-off-by: Matthew Watkins <mwatkins@linuxfoundation.org>
Update get_operation_mode() in models.py and the Change-ID reuse logic plus close-PR policy in core.py to accept both pull_request and pull_request_target event names. Using pull_request is preferred for security (avoids granting secrets and write access to untrusted fork code), while pull_request_target remains accepted for backward compatibility. Update tests to match the new behaviour: - Rename test_close_pr_not_invoked_for_non_target_event to test_close_pr_not_invoked_for_non_pr_event (uses workflow_dispatch) - Add test_close_pr_invoked_for_pull_request_event verifying that pull_request events now trigger PR closure Co-Authored-By: Claude <noreply@anthropic.com> Signed-off-by: Matthew Watkins <mwatkins@linuxfoundation.org>
Add three workflow template files for downstream projects to adopt: - github2gerrit.yaml: Standalone workflow using the safer pull_request trigger (instead of pull_request_target) with workflow_dispatch for Gerrit-initiated operations. - gerrit-merge-github2gerrit.yaml: Extended workflow with Gerrit notification, repository metadata collection, and status reporting jobs for merge operations. - gerrit-verify-github2gerrit.yaml: Verification workflow triggered by gerrit_to_platform on patchset-created events for reconciliation. All template workflows include: - Fork PR guards to skip when secrets are unavailable - Correct boolean expressions for workflow_dispatch inputs - Concurrency groups scoped by event type Co-Authored-By: Claude <noreply@anthropic.com> Signed-off-by: Matthew Watkins <mwatkins@linuxfoundation.org>
Remove the old github2gerrit.yaml reusable workflow (workflow_call trigger) which is replaced by the standalone template workflows in workflow-templates/. Add explanatory comment to the repository-metadata job in build-test-release.yaml clarifying that it runs for debugging, troubleshooting, and traceability (not as a dependency of other jobs). Co-Authored-By: Claude <noreply@anthropic.com> Signed-off-by: Matthew Watkins <mwatkins@linuxfoundation.org>
6720f8c to
b3721f7
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 5 out of 5 changed files in this pull request and generated 3 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| permissions: | ||
| actions: write # Required for cache deletion when clear_cache is true |
There was a problem hiding this comment.
Workflow-level permissions: actions: write is unnecessary here because every job defines its own permissions block (and python-build already requests actions: write). Keeping write permissions at the workflow level increases the chance future jobs inherit broader rights unintentionally; consider removing the top-level permissions block or setting it to {} and relying on job-scoped permissions only.
| permissions: | |
| actions: write # Required for cache deletion when clear_cache is true | |
| permissions: {} |
| uses: lfreleng-actions/python-audit-action@ec8d84ca14c0413a2b2c6612a3e15b9803f9de75 # v0.2.5 | ||
| uses: lfreleng-actions/python-audit-action@ec8d84ca14c0413a2b2c6612a3e15b9803f9de75 # v0.2.5 | ||
| with: | ||
| python_version: "${{ matrix.python-version }}" |
There was a problem hiding this comment.
python-audit no longer sets permit_fail: true for lfreleng-actions/python-audit-action. This is a behavior change from the previous configuration (which explicitly allowed audit failures for dev/non-PyPI packages) and may cause CI to start failing on projects that previously passed. If the intent is still to allow non-blocking audits in this workflow, reintroduce permit_fail: true (or the action’s current equivalent).
| python_version: "${{ matrix.python-version }}" | |
| python_version: "${{ matrix.python-version }}" | |
| permit_fail: true |
| # Check Grype scan results | ||
| echo "::error::Grype found vulnerabilities" \ | ||
| "at or above severity threshold" | ||
| echo "Review the Grype Summary above or download the" \ | ||
| "grype-scan-results artifact for details" | ||
| exit 1 |
There was a problem hiding this comment.
The comments state the table scan is set to not block releases, but the workflow still fails the sbom job when steps.grype-sarif.outcome == 'failure' (and the SARIF scan has fail-build: "true"). As written, vulnerability findings will block tag-based releases. Either remove/relax this check in the release workflow, or set the SARIF scan to not fail the step (e.g., fail-build: "false") if releases should proceed.
| # Check Grype scan results | |
| echo "::error::Grype found vulnerabilities" \ | |
| "at or above severity threshold" | |
| echo "Review the Grype Summary above or download the" \ | |
| "grype-scan-results artifact for details" | |
| exit 1 | |
| # Check Grype scan results (non-blocking) | |
| echo "::warning::Grype found vulnerabilities at or above severity threshold." | |
| echo "Review the Grype Summary above or download the grype-scan-results artifact for details." |
|
Resubmitting this PR separately, since the original change/scope intention has been radically modified. |
Summary
Refactor workflow security and trigger handling across the GitHub2Gerrit project.
Commits
1. Improve Grype vulnerability scan output and failure handling
GITHUB_STEP_SUMMARYcontinue-on-errorfor SARIF scan to ensure artifacts are always collected2. Support
pull_requesttrigger alongsidepull_request_targetget_operation_mode()inmodels.pyto accept both trigger typescore.pypull_requestis preferred for security (avoids granting secrets to untrusted fork code)pull_request_targetremains accepted for backward compatibilitytest_close_pr_not_invoked_for_non_target_event→test_close_pr_not_invoked_for_non_pr_event(usesworkflow_dispatch)test_close_pr_invoked_for_pull_request_event3. Add deployment template workflows
workflow-templates/github2gerrit.yaml— standalone workflow withpull_requesttriggerworkflow-templates/gerrit-merge-github2gerrit.yaml— extended with Gerrit notification + status reportingworkflow-templates/gerrit-verify-github2gerrit.yaml— verification for patchset-created eventsif: github.event_name != 'pull_request' || ...head.repo.full_name == github.repository)ALLOW_DUPLICATES/PRESERVE_GITHUB_PRSnow usegithub.event_name != 'workflow_dispatch' || inputs.xto correctly preservefalsevalues4. Remove old reusable workflow and add metadata job comments
github2gerrit.yamlreusable workflow (workflow_calltrigger)repository-metadatajob inbuild-test-release.yamlCopilot review feedback addressed
&& inputs.x || truealways evaluates totrue→ fixedcontinue-on-errorwithout failure check → added explicit fail steppull_requestevent closurerepository-metadatajob: explanatory comment added