mirror of
https://github.com/avelino/awesome-go.git
synced 2026-04-11 02:11:43 +08:00
Fix pwn request vulnerability in PR quality checks workflow
The pull_request_target workflow checked out and executed Go scripts from the PR head, allowing attackers to inject arbitrary code via init() functions with access to a write-scoped GITHUB_TOKEN. This was confirmed exploited in the wild (ref: StepSecurity blog). Checkout now targets the base branch so only trusted scripts execute. PR head SHA is fetched as data-only for diffing via a new PR_HEAD_SHA env var. Write operations (comments, labels) are isolated in a separate report job that never checks out code. All job permissions follow least privilege — quality runs read-only, report holds the write token. fixed: #6083 Signed-off-by: Avelino <31996+avelino@users.noreply.github.com> Co-Authored-By: Thierry Abalea <thierry.abalea@shipfox.io>
This commit is contained in:
parent
470fa15543
commit
24273bf86b
25
.github/scripts/check-pr-diff/main.go
vendored
25
.github/scripts/check-pr-diff/main.go
vendored
@ -207,11 +207,15 @@ func getDiff() string {
|
|||||||
if base == "" {
|
if base == "" {
|
||||||
base = "main"
|
base = "main"
|
||||||
}
|
}
|
||||||
out, err := exec.Command("git", "diff", "origin/"+base+"...HEAD", "--", "README.md").Output()
|
head := os.Getenv("PR_HEAD_SHA")
|
||||||
|
if head == "" {
|
||||||
|
head = "HEAD"
|
||||||
|
}
|
||||||
|
out, err := exec.Command("git", "diff", "origin/"+base+"..."+head, "--", "README.md").Output()
|
||||||
if err == nil && len(out) > 0 {
|
if err == nil && len(out) > 0 {
|
||||||
return string(out)
|
return string(out)
|
||||||
}
|
}
|
||||||
out, err = exec.Command("git", "diff", "HEAD~1", "--", "README.md").Output()
|
out, err = exec.Command("git", "diff", head+"~1", "--", "README.md").Output()
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return string(out)
|
return string(out)
|
||||||
}
|
}
|
||||||
@ -223,11 +227,15 @@ func getChangedFiles() []string {
|
|||||||
if base == "" {
|
if base == "" {
|
||||||
base = "main"
|
base = "main"
|
||||||
}
|
}
|
||||||
out, err := exec.Command("git", "diff", "--name-only", "origin/"+base+"...HEAD").Output()
|
head := os.Getenv("PR_HEAD_SHA")
|
||||||
|
if head == "" {
|
||||||
|
head = "HEAD"
|
||||||
|
}
|
||||||
|
out, err := exec.Command("git", "diff", "--name-only", "origin/"+base+"..."+head).Output()
|
||||||
if err == nil && len(out) > 0 {
|
if err == nil && len(out) > 0 {
|
||||||
return splitLines(string(out))
|
return splitLines(string(out))
|
||||||
}
|
}
|
||||||
out, err = exec.Command("git", "diff", "--name-only", "HEAD~1").Output()
|
out, err = exec.Command("git", "diff", "--name-only", head+"~1").Output()
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return splitLines(string(out))
|
return splitLines(string(out))
|
||||||
}
|
}
|
||||||
@ -291,7 +299,14 @@ func extractRepoName(rawURL string) string {
|
|||||||
// --- README parsing ---
|
// --- README parsing ---
|
||||||
|
|
||||||
func getCategoryItemCount(readmePath, entryURL string) (category string, count int) {
|
func getCategoryItemCount(readmePath, entryURL string) (category string, count int) {
|
||||||
data, err := os.ReadFile(readmePath)
|
var data []byte
|
||||||
|
var err error
|
||||||
|
head := os.Getenv("PR_HEAD_SHA")
|
||||||
|
if head != "" {
|
||||||
|
data, err = exec.Command("git", "show", head+":README.md").Output()
|
||||||
|
} else {
|
||||||
|
data, err = os.ReadFile(readmePath)
|
||||||
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "unknown", -1
|
return "unknown", -1
|
||||||
}
|
}
|
||||||
|
|||||||
53
.github/workflows/pr-quality-check.yaml
vendored
53
.github/workflows/pr-quality-check.yaml
vendored
@ -5,8 +5,8 @@ on:
|
|||||||
types: [opened, edited, synchronize, reopened]
|
types: [opened, edited, synchronize, reopened]
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
pull-requests: write
|
contents: read
|
||||||
contents: write
|
pull-requests: read
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
detect:
|
detect:
|
||||||
@ -36,16 +36,30 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
environment: action
|
environment: action
|
||||||
container: golang:latest
|
container: golang:latest
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
pull-requests: read
|
||||||
|
outputs:
|
||||||
|
comment: ${{ steps.quality.outputs.comment }}
|
||||||
|
labels: ${{ steps.quality.outputs.labels }}
|
||||||
|
fail: ${{ steps.quality.outputs.fail }}
|
||||||
|
diff_comment: ${{ steps.diff.outputs.diff_comment }}
|
||||||
|
diff_fail: ${{ steps.diff.outputs.diff_fail }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@v6
|
||||||
with:
|
with:
|
||||||
ref: ${{ github.event.pull_request.head.sha }}
|
ref: ${{ github.event.pull_request.base.sha }}
|
||||||
|
persist-credentials: false
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
|
|
||||||
- name: Fetch base branch
|
- name: Fetch base branch and PR head
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
run: |
|
run: |
|
||||||
git config --global --add safe.directory "$GITHUB_WORKSPACE"
|
git config --global --add safe.directory "$GITHUB_WORKSPACE"
|
||||||
git fetch origin ${{ github.base_ref }}
|
AUTH="$(printf '%s' "x-access-token:${GITHUB_TOKEN}" | base64 -w0)"
|
||||||
|
git -c "http.https://github.com/.extraheader=AUTHORIZATION: basic ${AUTH}" fetch origin "${{ github.base_ref }}"
|
||||||
|
git -c "http.https://github.com/.extraheader=AUTHORIZATION: basic ${AUTH}" fetch origin "+refs/pull/${{ github.event.pull_request.number }}/head"
|
||||||
|
|
||||||
- name: Run quality checks
|
- name: Run quality checks
|
||||||
id: quality
|
id: quality
|
||||||
@ -59,28 +73,38 @@ jobs:
|
|||||||
continue-on-error: true
|
continue-on-error: true
|
||||||
env:
|
env:
|
||||||
GITHUB_BASE_REF: ${{ github.base_ref }}
|
GITHUB_BASE_REF: ${{ github.base_ref }}
|
||||||
|
PR_HEAD_SHA: ${{ github.event.pull_request.head.sha }}
|
||||||
run: go run ./.github/scripts/check-pr-diff/
|
run: go run ./.github/scripts/check-pr-diff/
|
||||||
|
|
||||||
|
report:
|
||||||
|
name: Post quality report
|
||||||
|
needs: [detect, quality]
|
||||||
|
if: always() && needs.detect.outputs.is_package_pr == 'true' && needs.quality.result != 'cancelled'
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
pull-requests: write
|
||||||
|
contents: write
|
||||||
|
steps:
|
||||||
- name: Post quality report comment
|
- name: Post quality report comment
|
||||||
uses: marocchino/sticky-pull-request-comment@v2
|
uses: marocchino/sticky-pull-request-comment@v2
|
||||||
with:
|
with:
|
||||||
header: pr-quality-check
|
header: pr-quality-check
|
||||||
message: |
|
message: |
|
||||||
${{ steps.quality.outputs.comment }}
|
${{ needs.quality.outputs.comment }}
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
${{ steps.diff.outputs.diff_comment }}
|
${{ needs.quality.outputs.diff_comment }}
|
||||||
|
|
||||||
- name: Sync labels
|
- name: Sync labels
|
||||||
|
if: needs.quality.outputs.labels != ''
|
||||||
uses: actions-ecosystem/action-add-labels@v1
|
uses: actions-ecosystem/action-add-labels@v1
|
||||||
if: ${{ steps.quality.outputs.labels != '' }}
|
|
||||||
with:
|
with:
|
||||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
labels: ${{ join(fromJson(steps.quality.outputs.labels), '\n') }}
|
labels: ${{ join(fromJson(needs.quality.outputs.labels), '\n') }}
|
||||||
|
|
||||||
- name: Fail if critical checks failed
|
- name: Fail if critical checks failed
|
||||||
if: ${{ steps.quality.outputs.fail == 'true' || steps.diff.outputs.diff_fail == 'true' }}
|
if: needs.quality.outputs.fail == 'true' || needs.quality.outputs.diff_fail == 'true'
|
||||||
run: |
|
run: |
|
||||||
echo "Quality or diff checks failed."
|
echo "Quality or diff checks failed."
|
||||||
exit 1
|
exit 1
|
||||||
@ -90,6 +114,8 @@ jobs:
|
|||||||
needs: detect
|
needs: detect
|
||||||
if: needs.detect.outputs.is_package_pr == 'false'
|
if: needs.detect.outputs.is_package_pr == 'false'
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
pull-requests: write
|
||||||
steps:
|
steps:
|
||||||
- name: Post skip notice
|
- name: Post skip notice
|
||||||
uses: marocchino/sticky-pull-request-comment@v2
|
uses: marocchino/sticky-pull-request-comment@v2
|
||||||
@ -104,9 +130,12 @@ jobs:
|
|||||||
|
|
||||||
auto-merge:
|
auto-merge:
|
||||||
name: Enable auto-merge
|
name: Enable auto-merge
|
||||||
needs: quality
|
needs: [quality, report]
|
||||||
if: always() && needs.quality.result == 'success'
|
if: always() && needs.quality.result == 'success' && needs.report.result == 'success'
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
pull-requests: write
|
||||||
steps:
|
steps:
|
||||||
- name: Enable auto-merge via squash
|
- name: Enable auto-merge via squash
|
||||||
env:
|
env:
|
||||||
|
|||||||
1
.gitignore
vendored
1
.gitignore
vendored
@ -1,6 +1,7 @@
|
|||||||
out/
|
out/
|
||||||
awesome-go
|
awesome-go
|
||||||
.cache/
|
.cache/
|
||||||
|
check-*
|
||||||
|
|
||||||
# Folders
|
# Folders
|
||||||
.idea
|
.idea
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user