diff --git a/.github/workflows/contributor-check.yml b/.github/workflows/contributor-check.yml new file mode 100644 index 000000000000..7db44bf76caa --- /dev/null +++ b/.github/workflows/contributor-check.yml @@ -0,0 +1,146 @@ +name: Contributor Reputation Check + +on: + pull_request_target: + types: [opened] + issues: + types: [opened] + +permissions: + contents: read + issues: write + pull-requests: write + +jobs: + check: + runs-on: ubuntu-latest + if: >- + github.actor != 'dependabot[bot]' && + github.actor != 'github-actions[bot]' && + github.actor != 'copilot-swe-agent[bot]' + steps: + - name: Checkout AGT scripts + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + repository: microsoft/agent-governance-toolkit + sparse-checkout: scripts + path: agt + + - name: Setup Python + uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0 + with: + python-version: "3.12" + + - name: Determine author + id: author + run: | + if [ "${{ github.event_name }}" = "pull_request_target" ]; then + echo "username=${{ github.event.pull_request.user.login }}" >> "$GITHUB_OUTPUT" + echo "number=${{ github.event.pull_request.number }}" >> "$GITHUB_OUTPUT" + echo "type=pr" >> "$GITHUB_OUTPUT" + else + echo "username=${{ github.event.issue.user.login }}" >> "$GITHUB_OUTPUT" + echo "number=${{ github.event.issue.number }}" >> "$GITHUB_OUTPUT" + echo "type=issue" >> "$GITHUB_OUTPUT" + fi + + - name: Run profile check + id: profile + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + set +e + python agt/scripts/contributor_check.py \ + --username "${{ steps.author.outputs.username }}" \ + --json > /tmp/profile.json 2>/tmp/profile.log + set -e + risk=$(python -c "import json; print(json.load(open('/tmp/profile.json'))['risk'])" 2>/dev/null || echo "UNKNOWN") + echo "risk=$risk" >> "$GITHUB_OUTPUT" + + - name: Run credential audit + id: credential + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + set +e + python agt/scripts/credential_audit.py \ + --username "${{ steps.author.outputs.username }}" \ + --repo "${{ github.repository }}" \ + --json > /tmp/cred.json 2>/tmp/cred.log + set -e + risk=$(python -c "import json; print(json.load(open('/tmp/cred.json'))['risk'])" 2>/dev/null || echo "UNKNOWN") + echo "risk=$risk" >> "$GITHUB_OUTPUT" + + - name: Compute overall risk + id: overall + run: | + risk_to_num() { + case "$1" in HIGH) echo 3 ;; MEDIUM) echo 2 ;; LOW) echo 1 ;; *) echo 0 ;; esac + } + p=$(risk_to_num "${{ steps.profile.outputs.risk }}") + c=$(risk_to_num "${{ steps.credential.outputs.risk }}") + max=$p; [ "$c" -gt "$max" ] && max=$c + case "$max" in 3) r="HIGH" ;; 2) r="MEDIUM" ;; *) r="LOW" ;; esac + echo "risk=$r" >> "$GITHUB_OUTPUT" + + - name: Comment on MEDIUM or HIGH risk + if: steps.overall.outputs.risk == 'MEDIUM' || steps.overall.outputs.risk == 'HIGH' + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + username="${{ steps.author.outputs.username }}" + number="${{ steps.author.outputs.number }}" + type="${{ steps.author.outputs.type }}" + risk="${{ steps.overall.outputs.risk }}" + profile="${{ steps.profile.outputs.risk }}" + cred="${{ steps.credential.outputs.risk }}" + + if [ "$risk" = "HIGH" ]; then icon="🔴"; else icon="🟡"; fi + + body=" + $icon **Contributor Reputation Check: $risk risk** + + | Check | Risk | + |-------|------| + | Profile | $profile | + | Credential audit | $cred | + + Maintainers: please review this contributor before merging. + *Automated check powered by [AGT](https://github.com/microsoft/agent-governance-toolkit).*" + + if [ "$type" = "pr" ]; then + gh pr comment "$number" --body "$body" + else + gh issue comment "$number" --body "$body" + fi + + - name: Add risk label + if: steps.overall.outputs.risk == 'MEDIUM' || steps.overall.outputs.risk == 'HIGH' + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + number="${{ steps.author.outputs.number }}" + type="${{ steps.author.outputs.type }}" + risk="${{ steps.overall.outputs.risk }}" + + gh label create "needs-review:$risk" \ + --description "Contributor reputation check flagged $risk risk" \ + --color "FFA500" --force 2>/dev/null || true + + if [ "$type" = "pr" ]; then + gh pr edit "$number" --add-label "needs-review:$risk" + else + gh issue edit "$number" --add-label "needs-review:$risk" + fi + + - name: Job summary + if: always() + run: | + { + echo "## Contributor Check: \`${{ steps.author.outputs.username }}\`" + echo "| Check | Risk |" + echo "|-------|------|" + echo "| Profile | ${{ steps.profile.outputs.risk }} |" + echo "| Credential | ${{ steps.credential.outputs.risk }} |" + echo "| **Overall** | **${{ steps.overall.outputs.risk }}** |" + } >> "$GITHUB_STEP_SUMMARY"