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
69 changes: 69 additions & 0 deletions .github/workflows/approve-external-pr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,11 @@ on:
description: "External PR number to replay (e.g. 30)"
required: true
type: string
acknowledge_critical_files:
description: "Set true only after reviewing the diff, to proceed even though the PR touches .github/workflows, .githooks, or scripts"
required: false
type: boolean
default: false

permissions:
contents: write # push the trusted/pr-N branch
Expand Down Expand Up @@ -79,6 +84,70 @@ jobs:
exit 1
fi

# ── 0.1. Authorize maintainer actor ─────────────────────────────────────
- name: Verify maintainer authorization
env:
ACTOR: ${{ github.actor }}
REPOSITORY: ${{ github.repository }}
GH_TOKEN: ${{ github.token }}
run: |
set -euo pipefail

echo "Verifying authorization for actor @$ACTOR..."

# Explicit maintainer bypass
if [ "$ACTOR" = "Daliys" ]; then
echo "Actor @$ACTOR is authorized maintainer."
exit 0
fi

# Check collaborator permission via GitHub API
PERM=$(gh api "repos/$REPOSITORY/collaborators/$ACTOR/permission" --jq '.permission' 2>/dev/null || echo "none")
case "$PERM" in
admin|write)
echo "Actor @$ACTOR authorized with permission: $PERM"
;;
*)
echo "::error::Actor @$ACTOR does not have maintainer authorization (permission: $PERM)."
exit 1
;;
esac

# ── 0.2. Content & Security Pre-scan ────────────────────────────────────
- name: Run security pre-scan on PR files
env:
REPOSITORY: ${{ github.repository }}
PR_NUMBER: ${{ inputs.pr_number }}
ACKNOWLEDGE: ${{ inputs.acknowledge_critical_files }}
GH_TOKEN: ${{ github.token }}
run: |
set -euo pipefail

echo "Scanning changed files in PR #$PR_NUMBER..."
FILES=$(gh api "repos/$REPOSITORY/pulls/$PR_NUMBER/files" --paginate --jq '.[].filename')

SUSPICIOUS_COUNT=0
while IFS= read -r file; do
[ -z "$file" ] && continue
case "$file" in
.github/workflows/*|.githooks/*|scripts/*)
echo "::warning::PR #$PR_NUMBER modifies critical CI/script file: $file"
SUSPICIOUS_COUNT=$((SUSPICIOUS_COUNT + 1))
;;
esac
done <<< "$FILES"

if [ "$SUSPICIOUS_COUNT" -gt 0 ]; then
echo "::notice::PR #$PR_NUMBER modifies $SUSPICIOUS_COUNT critical file(s)."
if [ "$ACKNOWLEDGE" != "true" ]; then
echo "::error::Refusing to replay PR #$PR_NUMBER: it modifies critical CI/script file(s) (see warnings above). Review the diff, then re-run this workflow with 'acknowledge_critical_files' set to true if it's safe to proceed."
exit 1
fi
echo "Critical file changes acknowledged by @${{ github.actor }} — proceeding."
else
echo "Content pre-scan passed cleanly: no critical CI/workflow files modified."
fi

# ── 1. Create trusted branch with the fork's commits ───────────────────
- name: Create trusted/pr-${{ inputs.pr_number }} branch
env:
Expand Down
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ All notable public changes to Nexus Unity are documented here.

## [Unreleased]

### Security
- Hardened external PR replay workflow (`approve-external-pr.yml`) with an in-workflow actor authorization gate and a PR content security pre-scan to prevent unauthorized execution and flag critical CI modifications on self-hosted runners (#144).
- The content pre-scan now blocks the replay when a PR touches `.github/workflows/*`, `.githooks/*`, or `scripts/*`, requiring the maintainer to re-run with `acknowledge_critical_files: true` after reviewing the diff, instead of only logging a warning (#144).

### Changed
- Consolidated duplicate internal Ollama-review and serialized-property write helpers.

Expand Down
Loading