Skip to content

Feature/challenge 3 - Employee manager #1350

Feature/challenge 3 - Employee manager

Feature/challenge 3 - Employee manager #1350

Workflow file for this run

name: PR Tests
permissions:
contents: read
on:
pull_request:
branches:
- main
types:
- opened
- synchronize
- reopened
jobs:
validate-submission-security:
outputs:
validation_needed: ${{ steps.validate-dirs.outputs.validation_needed }}
validation_passed: ${{ steps.validate-dirs.outputs.validation_passed }}
manual_approval_needed: ${{ steps.validate-dirs.outputs.manual_approval_needed }}
changed_challenges: ${{ steps.validate-dirs.outputs.changed_challenges }}
runs-on: ubuntu-latest
name: Validate Submission Security
steps:
- name: Checkout repository
uses: actions/checkout@v3
with:
ref: ${{ github.event.pull_request.head.sha }}
fetch-depth: 0
- name: Get PR author username
id: pr-info
run: |
USERNAME="${{ github.event.pull_request.user.login }}"
USERNAME_LOWER=$(echo "$USERNAME" | tr '[:upper:]' '[:lower:]')
echo "pr_username=$USERNAME_LOWER" >> $GITHUB_OUTPUT
echo "PR submitted by: $USERNAME (normalized to: $USERNAME_LOWER)"
- name: Check modified submission directories
id: validate-dirs
run: |
USERNAME="${{ steps.pr-info.outputs.pr_username }}"
echo "Checking submission directories for PR by: $USERNAME"
# Get list of changed files in this PR
git fetch origin main
CHANGED_FILES=$(git diff --name-only origin/main...HEAD)
echo "Changed files:"
echo "$CHANGED_FILES"
# Check if any files outside of submissions directories are modified
# Allow both regular challenge submissions and package challenge submissions
NON_SUBMISSION_FILES=$(echo "$CHANGED_FILES" | grep -v -E "(challenge-[0-9]+/submissions/|packages/[^/]+/challenge-[^/]+/submissions/)" || true)
# Check for manual approval in PR labels or comments
MANUAL_APPROVAL_GRANTED="false"
# Check if PR has manual approval label
if [[ "${{ contains(github.event.pull_request.labels.*.name, 'manual-approval-granted') }}" == "true" ]]; then
MANUAL_APPROVAL_GRANTED="true"
echo "✅ Manual approval granted via label"
fi
if [ -n "$NON_SUBMISSION_FILES" ] && [ "$MANUAL_APPROVAL_GRANTED" != "true" ]; then
echo "⚠️ WARNING: User '$USERNAME' modified files outside of submission directories:"
echo "$NON_SUBMISSION_FILES"
echo "🔍 This PR requires manual approval from maintainers"
echo "✅ Maintainers can approve by adding the 'manual-approval-granted' label"
echo "manual_approval_needed=true" >> $GITHUB_OUTPUT
else
echo "manual_approval_needed=false" >> $GITHUB_OUTPUT
fi
# Extract challenges that were modified (either submissions or other files)
# Include both regular challenges and package challenges
REGULAR_CHALLENGES=$(echo "$CHANGED_FILES" | grep -E "^challenge-[0-9]+/" | sed 's|/.*||' || true)
PACKAGE_CHALLENGES=$(echo "$CHANGED_FILES" | grep -E "^packages/[^/]+/challenge-[^/]+/" | sed -E 's|^(packages/[^/]+/challenge-[^/]+)/.*|\1|' || true)
CHANGED_CHALLENGES=$(echo -e "$REGULAR_CHALLENGES\n$PACKAGE_CHALLENGES" | grep -v '^$' | sort -u || true)
# Extract submission directories that were modified
# Include both regular challenge submissions and package challenge submissions
REGULAR_SUBMISSION_DIRS=$(echo "$CHANGED_FILES" | grep -E "challenge-[0-9]+/submissions/" | cut -d'/' -f3 || true)
PACKAGE_SUBMISSION_DIRS=$(echo "$CHANGED_FILES" | grep -E "packages/[^/]+/challenge-[^/]+/submissions/" | cut -d'/' -f5 || true)
MODIFIED_SUBMISSION_DIRS=$(echo -e "$REGULAR_SUBMISSION_DIRS\n$PACKAGE_SUBMISSION_DIRS" | grep -v '^$' | sort -u || true)
if [ -n "$CHANGED_CHALLENGES" ]; then
echo "Changed challenges: $CHANGED_CHALLENGES"
# Convert to JSON array format for matrix
CHALLENGES_JSON=$(echo "$CHANGED_CHALLENGES" | tr ' ' '\n' | sed 's/^/"/;s/$/"/' | tr '\n' ',' | sed 's/,$//')
echo "changed_challenges=[$CHALLENGES_JSON]" >> $GITHUB_OUTPUT
else
echo "No challenges modified in this PR"
echo "changed_challenges=[]" >> $GITHUB_OUTPUT
fi
if [ -z "$MODIFIED_SUBMISSION_DIRS" ]; then
echo "No submission directories modified in this PR"
# Still need to check if manual approval is required for non-submission files
if [ -n "$NON_SUBMISSION_FILES" ] && [ "$MANUAL_APPROVAL_GRANTED" != "true" ]; then
echo "⚠️ PR contains non-submission file changes that require manual approval"
echo "validation_needed=true" >> $GITHUB_OUTPUT
echo "validation_passed=false" >> $GITHUB_OUTPUT
echo "manual_approval_needed=true" >> $GITHUB_OUTPUT
else
echo "✅ No submission files modified and no manual approval needed"
echo "validation_needed=false" >> $GITHUB_OUTPUT
echo "validation_passed=true" >> $GITHUB_OUTPUT
echo "manual_approval_needed=false" >> $GITHUB_OUTPUT
fi
exit 0
fi
echo "Modified submission directories:"
echo "$MODIFIED_SUBMISSION_DIRS"
# Validate each modified submission directory (case-insensitive comparison)
INVALID_DIRS=""
for DIR in $MODIFIED_SUBMISSION_DIRS; do
DIR_LOWER=$(echo "$DIR" | tr '[:upper:]' '[:lower:]')
USERNAME_LOWER=$(echo "$USERNAME" | tr '[:upper:]' '[:lower:]')
echo "Comparing: '$DIR' (normalized: '$DIR_LOWER') vs username '$USERNAME' (normalized: '$USERNAME_LOWER')"
if [ "$DIR_LOWER" != "$USERNAME_LOWER" ]; then
INVALID_DIRS="$INVALID_DIRS $DIR"
fi
done
# Validate that all changed files are within the user's own submission directories
USERNAME_LOWER=$(echo "$USERNAME" | tr '[:upper:]' '[:lower:]')
INVALID_FILES=""
for FILE in $CHANGED_FILES; do
# Check regular challenge submissions
if [[ "$FILE" =~ ^challenge-[0-9]+/submissions/([^/]+)/ ]]; then
SUBMISSION_USER="${BASH_REMATCH[1]}"
SUBMISSION_USER_LOWER=$(echo "$SUBMISSION_USER" | tr '[:upper:]' '[:lower:]')
if [ "$SUBMISSION_USER_LOWER" != "$USERNAME_LOWER" ]; then
INVALID_FILES="$INVALID_FILES $FILE"
fi
fi
# Check package challenge submissions
if [[ "$FILE" =~ ^packages/[^/]+/challenge-[^/]+/submissions/([^/]+)/ ]]; then
SUBMISSION_USER="${BASH_REMATCH[1]}"
SUBMISSION_USER_LOWER=$(echo "$SUBMISSION_USER" | tr '[:upper:]' '[:lower:]')
if [ "$SUBMISSION_USER_LOWER" != "$USERNAME_LOWER" ]; then
INVALID_FILES="$INVALID_FILES $FILE"
fi
fi
done
# Check for strict security violations (modifying other users' submissions)
# Skip this check if manual approval is granted
if [ -n "$INVALID_DIRS" ] || [ -n "$INVALID_FILES" ]; then
if [ "$MANUAL_APPROVAL_GRANTED" == "true" ]; then
echo "✅ Manual approval granted - bypassing submission directory security checks"
echo "⚠️ WARNING: User '$USERNAME' modified submission directories for other users:$INVALID_DIRS"
if [ -n "$INVALID_FILES" ]; then
echo "⚠️ WARNING: User '$USERNAME' modified files in other users' submission directories:"
echo "$INVALID_FILES"
fi
echo "🔓 Manual approval allows bypassing normal security restrictions"
else
if [ -n "$INVALID_DIRS" ]; then
echo "❌ STRICT SECURITY VIOLATION: User '$USERNAME' attempted to modify submission directories for other users:$INVALID_DIRS"
fi
if [ -n "$INVALID_FILES" ]; then
echo "❌ STRICT SECURITY VIOLATION: User '$USERNAME' attempted to modify files in other users' submission directories:"
echo "$INVALID_FILES"
fi
echo "✅ You can only modify submissions in directories named after your GitHub username: $USERNAME"
echo "✅ Allowed directories: challenge-*/submissions/$USERNAME or packages/*/challenge-*/submissions/$USERNAME"
echo "validation_passed=false" >> $GITHUB_OUTPUT
echo "validation_needed=true" >> $GITHUB_OUTPUT
exit 1
fi
fi
# Check if manual approval is needed and not granted
if [ -n "$NON_SUBMISSION_FILES" ] && [ "$MANUAL_APPROVAL_GRANTED" != "true" ]; then
echo "⚠️ PR contains non-submission file changes that require manual approval"
echo "validation_passed=false" >> $GITHUB_OUTPUT
echo "validation_needed=true" >> $GITHUB_OUTPUT
else
if [ "$MANUAL_APPROVAL_GRANTED" == "true" ]; then
echo "✅ Manual approval granted - security validation bypassed"
echo "🔓 All security checks bypassed due to manual approval"
else
echo "✅ Security validation passed"
fi
echo "validation_passed=true" >> $GITHUB_OUTPUT
echo "validation_needed=false" >> $GITHUB_OUTPUT
fi
manual-approval-status:
runs-on: ubuntu-latest
needs: validate-submission-security
if: needs.validate-submission-security.outputs.manual_approval_needed == 'true'
steps:
- name: Manual Approval Required
run: |
echo "🚨 MANUAL APPROVAL REQUIRED 🚨"
echo ""
echo "🔍 This PR modifies files outside of submission directories"
echo "📝 Files modified outside challenge-*/submissions/ or packages/*/challenge-*/submissions/ need admin review"
echo ""
echo "📢 @RezaSi - Please review and approve this PR by:"
echo " 1. ✅ Reviewing the changes carefully"
echo " 2. 🏷️ Adding the 'manual-approval-granted' label"
echo " 3. 🔄 Re-running this workflow (close/reopen PR)"
echo ""
echo "⚠️ SECURITY: Only approve changes you trust!"
echo "❌ This workflow will block until manual approval is granted"
no-challenges-changed:
runs-on: ubuntu-latest
needs: validate-submission-security
if: needs.validate-submission-security.outputs.validation_passed == 'true' && needs.validate-submission-security.outputs.changed_challenges == '[]'
steps:
- name: No Tests Needed
run: |
echo "✅ PR validation passed but no challenge directories were modified"
echo "🎯 This PR doesn't contain any challenge submissions to test"
echo "✨ All security checks completed successfully"
test-submissions:
runs-on: ubuntu-latest
needs: validate-submission-security
if: needs.validate-submission-security.outputs.validation_passed == 'true' && needs.validate-submission-security.outputs.changed_challenges != '[]'
strategy:
matrix:
challenge: ${{ fromJson(needs.validate-submission-security.outputs.changed_challenges) }}
steps:
- name: Checkout repository
uses: actions/checkout@v3
with:
ref: ${{ github.event.pull_request.head.sha }}
- name: Set up Go
uses: actions/setup-go@v4
with:
go-version: '1.25.0'
- name: Run Tests for ${{ matrix.challenge }}
working-directory: ${{ matrix.challenge }}
run: |
USERNAME="${{ github.event.pull_request.user.login }}"
# Check if this is a package challenge or regular challenge
if [[ "${{ matrix.challenge }}" =~ ^packages/ ]]; then
# Package challenge
SUBMISSION_DIR="submissions/$USERNAME"
if [ -d "$SUBMISSION_DIR" ]; then
echo "Testing package challenge submission from $USERNAME"
# Create a temporary directory for testing
TEMP_DIR=$(mktemp -d)
# Copy the user's solution file and test file to temp directory
cp "$SUBMISSION_DIR/solution.go" "$TEMP_DIR/"
cp "solution-template_test.go" "$TEMP_DIR/"
# Copy go.mod if it exists
if [ -f "go.mod" ]; then
cp "go.mod" "$TEMP_DIR/"
fi
# Rename solution.go to solution-template.go for the test
mv "$TEMP_DIR/solution.go" "$TEMP_DIR/solution-template.go"
# Navigate to temp directory
pushd "$TEMP_DIR" > /dev/null
# Handle dependencies if go.mod exists
if [ -f "go.mod" ]; then
echo "Found go.mod file, downloading dependencies..."
# Update module name to avoid conflicts
sed -i 's/^module .*/module challenge/' go.mod
go mod tidy
else
go mod init challenge
fi
# Run tests
go test -v
TEST_EXIT_CODE=$?
# Return to original directory and cleanup
popd > /dev/null
rm -rf "$TEMP_DIR"
exit $TEST_EXIT_CODE
else
echo "No submission found for $USERNAME in package challenge ${{ matrix.challenge }}"
fi
else
# Regular challenge
SUBMISSION_DIR="submissions/$USERNAME"
if [ -d "$SUBMISSION_DIR" ]; then
echo "Testing submission from $USERNAME"
cp "$SUBMISSION_DIR"/*.go .
# Handle dependencies if go.mod exists
if [ -f "go.mod" ]; then
echo "Found go.mod file, downloading dependencies..."
go mod tidy
fi
go test -v
else
echo "No submission found for $USERNAME in ${{ matrix.challenge }}"
fi
fi