diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 000000000000..ea7807bb5e9e --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1,51 @@ +# Bitcoin Core Code Owners +# This file defines code ownership for required reviews in protected branches +# See: https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-code-owners + +# Default owners for everything in the repository +# These owners will be requested for review when someone opens a pull request +* @bitcoin/maintainers + +# Core consensus code +/src/consensus/ @bitcoin/consensus-reviewers +/src/validation.cpp @bitcoin/consensus-reviewers +/src/validation.h @bitcoin/consensus-reviewers + +# Wallet code +/src/wallet/ @bitcoin/wallet-reviewers + +# Networking code +/src/net.cpp @bitcoin/network-reviewers +/src/net.h @bitcoin/network-reviewers +/src/net_processing.cpp @bitcoin/network-reviewers + +# Cryptographic code +/src/crypto/ @bitcoin/crypto-reviewers +/src/key.cpp @bitcoin/crypto-reviewers +/src/key.h @bitcoin/crypto-reviewers + +# Testing infrastructure +/test/ @bitcoin/test-reviewers +/src/test/ @bitcoin/test-reviewers + +# Build system +/CMakeLists.txt @bitcoin/build-reviewers +/cmake/ @bitcoin/build-reviewers +/depends/ @bitcoin/build-reviewers + +# CI/CD workflows +/.github/workflows/ @bitcoin/devops-reviewers +/.github/actions/ @bitcoin/devops-reviewers + +# Security policies +/SECURITY.md @bitcoin/security-team +/.github/rulesets/ @bitcoin/security-team + +# Documentation +/doc/ @bitcoin/doc-reviewers +*.md @bitcoin/doc-reviewers + +# Configuration files +*.yml @bitcoin/config-reviewers +*.yaml @bitcoin/config-reviewers +*.json @bitcoin/config-reviewers diff --git a/.github/QUICK_START_RULESETS.md b/.github/QUICK_START_RULESETS.md new file mode 100644 index 000000000000..a4ac8774902c --- /dev/null +++ b/.github/QUICK_START_RULESETS.md @@ -0,0 +1,347 @@ +# Quick Start Guide: Setting Up Branch Protection and Security Rulesets + +This guide will help you quickly set up the branch protection and security rulesets for your Bitcoin Core repository. + +## Prerequisites + +- Repository admin access +- GitHub CLI (`gh`) installed (optional, but recommended) +- Basic understanding of Git and GitHub + +## Step 1: Review the Rulesets + +The repository includes the following pre-configured rulesets in `.github/rulesets/`: + +1. **branch-protection-main.json** - Protects main/master branch +2. **branch-protection-release.json** - Protects release branches +3. **branch-protection-dev.json** - Protects development branches +4. **tag-protection.json** - Protects version tags +5. **security-checks.json** - Applies security checks to all branches + +Review each file to understand the protection rules. + +## Step 2: Customize for Your Repository + +Before applying, customize the rulesets for your specific needs: + +### A. Update Status Check Names + +Edit each ruleset to match your CI/CD workflow job names: + +```json +"required_status_checks": [ + { + "context": "your-actual-workflow-name", + "integration_id": null + } +] +``` + +Find your workflow names by checking `.github/workflows/` or running: +```bash +gh api /repos/$(gh repo view --json nameWithOwner -q .nameWithOwner)/actions/runs | \ + jq -r '.workflow_runs[0].check_suite_id' +``` + +### B. Configure Bypass Actors + +Determine which users/teams need bypass permissions and get their IDs: + +```bash +# Get organization teams +gh api /orgs/YOUR_ORG/teams + +# Get team ID +gh api /orgs/YOUR_ORG/teams/TEAM_NAME | jq .id +``` + +Update the `bypass_actors` section with appropriate IDs and roles. + +### C. Adjust Review Requirements + +Based on your team size, adjust: +- `required_approving_review_count`: Number of required reviewers +- `require_code_owner_review`: Enable/disable CODEOWNERS requirement +- `dismiss_stale_reviews_on_push`: Auto-dismiss old reviews + +## Step 3: Update CODEOWNERS + +Edit `.github/CODEOWNERS` to reflect your team structure: + +``` +# Replace with actual GitHub usernames or teams +* @your-org/maintainers + +/src/consensus/ @your-org/consensus-team +/src/wallet/ @your-org/wallet-team +``` + +## Step 4: Validate the Configuration + +Run the validation script to ensure all rulesets are properly formatted: + +```bash +python3 .github/validate-rulesets.py +``` + +Fix any issues reported before proceeding. + +## Step 5: Apply Rulesets to Repository + +### Option A: Using GitHub Web UI (Recommended for First-Time Setup) + +1. Go to your repository on GitHub +2. Navigate to **Settings** → **Rules** → **Rulesets** +3. Click **New ruleset** → **New branch ruleset** +4. Use the following settings based on each JSON file: + +For **branch-protection-main.json**: +- Name: "Main Branch Protection" +- Enforcement status: Active +- Target branches: + - Add pattern: `main` + - Add pattern: `master` +- Rules: + - ✅ Require a pull request before merging + - Required approvals: 2 + - Dismiss stale pull request approvals when new commits are pushed + - Require review from Code Owners + - Require approval of the most recent reviewable push + - ✅ Require status checks to pass + - Add your workflow check names + - ✅ Require branches to be up to date before merging + - ✅ Block force pushes + - ✅ Require signed commits + - ✅ Require linear history + - (Continue for all rules in the JSON) + +5. Save the ruleset +6. Repeat for other rulesets (release, dev, tag, security) + +### Option B: Using GitHub API + +```bash +# Set your repo details +OWNER="your-org" +REPO="bitcoin" + +# Apply main branch protection +gh api --method POST \ + -H "Accept: application/vnd.github+json" \ + /repos/$OWNER/$REPO/rulesets \ + --input .github/rulesets/branch-protection-main.json + +# Apply release branch protection +gh api --method POST \ + -H "Accept: application/vnd.github+json" \ + /repos/$OWNER/$REPO/rulesets \ + --input .github/rulesets/branch-protection-release.json + +# Apply dev branch protection +gh api --method POST \ + -H "Accept: application/vnd.github+json" \ + /repos/$OWNER/$REPO/rulesets \ + --input .github/rulesets/branch-protection-dev.json + +# Apply tag protection +gh api --method POST \ + -H "Accept: application/vnd.github+json" \ + /repos/$OWNER/$REPO/rulesets \ + --input .github/rulesets/tag-protection.json + +# Apply security checks +gh api --method POST \ + -H "Accept: application/vnd.github+json" \ + /repos/$OWNER/$REPO/rulesets \ + --input .github/rulesets/security-checks.json +``` + +### Option C: Using a Script + +Create a script to apply all rulesets: + +```bash +#!/bin/bash +# apply-rulesets.sh + +set -e + +OWNER="${1:-your-org}" +REPO="${2:-bitcoin}" + +echo "Applying rulesets to $OWNER/$REPO..." + +for ruleset in .github/rulesets/*.json; do + echo "Applying $(basename $ruleset)..." + gh api --method POST \ + -H "Accept: application/vnd.github+json" \ + /repos/$OWNER/$REPO/rulesets \ + --input "$ruleset" + echo "✅ Applied $(basename $ruleset)" +done + +echo "All rulesets applied successfully!" +``` + +Make it executable and run: +```bash +chmod +x apply-rulesets.sh +./apply-rulesets.sh your-org bitcoin +``` + +## Step 6: Enable Security Features + +Enable additional GitHub security features: + +1. **Dependabot** + - Go to Settings → Security → Dependabot + - Enable Dependabot alerts + - Enable Dependabot security updates + +2. **Code Scanning** + - The CodeQL workflow is already configured in `.github/workflows/codeql.yml` + - Verify it's running on pull requests + +3. **Secret Scanning** + - Go to Settings → Security → Secret scanning + - Enable secret scanning + - Enable push protection + +## Step 7: Set Up GPG Signing (Required) + +Since rulesets require signed commits, ensure all contributors set up GPG signing: + +1. **Generate GPG Key**: + ```bash + gpg --full-generate-key + # Select RSA and RSA, 4096 bits, appropriate expiration + ``` + +2. **Configure Git**: + ```bash + gpg --list-secret-keys --keyid-format=long + # Note your key ID + + git config --global user.signingkey YOUR_KEY_ID + git config --global commit.gpgsign true + git config --global tag.gpgsign true + ``` + +3. **Add to GitHub**: + ```bash + gpg --armor --export YOUR_KEY_ID | gh gpg-key add - + # Or manually add in GitHub Settings → SSH and GPG keys + ``` + +4. **Test Signing**: + ```bash + git commit --allow-empty -m "Test signed commit" + git log --show-signature -1 + ``` + +## Step 8: Test the Configuration + +Create a test pull request to verify the rulesets are working: + +```bash +# Create a test branch +git checkout -b test-branch-protection + +# Make a small change +echo "# Test" >> test-file.md +git add test-file.md +git commit -S -m "Test branch protection" + +# Push and create PR +git push -u origin test-branch-protection +gh pr create --title "Test: Branch Protection" --body "Testing ruleset configuration" +``` + +Verify that: +- Pull request requires reviews +- Status checks are required +- Force push is blocked +- Commit signature is verified + +## Step 9: Document and Communicate + +1. **Update your CONTRIBUTING.md**: + - Add section on branch protection policies + - Link to SECURITY_MANAGEMENT.md + - Include GPG signing requirements + +2. **Notify the Team**: + - Send announcement about new protection rules + - Provide link to this Quick Start Guide + - Schedule training if needed + +3. **Create a Wiki Page** (optional): + - Detailed workflow examples + - Troubleshooting guide + - FAQ section + +## Step 10: Monitor and Adjust + +After deployment: + +1. **Monitor Compliance**: + ```bash + # Check ruleset bypass usage + gh api /repos/$OWNER/$REPO/rulesets | jq '.[] | {name, enforcement}' + ``` + +2. **Collect Feedback**: + - Are rules too strict or too lenient? + - Are status checks completing in reasonable time? + - Do teams need additional bypass permissions? + +3. **Iterate**: + - Update rulesets based on feedback + - Re-run validation script + - Apply updates via API or UI + +## Troubleshooting + +### Issue: Status checks not found + +**Solution**: Update the `context` names in rulesets to match your actual workflow job names. Check `.github/workflows/` for the correct names. + +### Issue: Cannot push to protected branch + +**Solution**: +1. Verify your commits are signed: `git log --show-signature` +2. Ensure all required checks pass +3. Get required reviews before merging + +### Issue: Bypass not working + +**Solution**: Verify bypass actor IDs are correct. Repository roles: +- 1 = Admin +- 2 = Maintain +- 3 = Write +- 4 = Triage +- 5 = Read + +### Issue: Ruleset conflicts + +**Solution**: GitHub applies rulesets in order. More specific rules override general ones. Check for overlapping conditions. + +## Additional Resources + +- [GitHub Rulesets Documentation](https://docs.github.com/en/repositories/configuring-branches-and-merges-in-your-repository/managing-rulesets) +- [CODEOWNERS Documentation](https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-code-owners) +- [GPG Signing Guide](https://docs.github.com/en/authentication/managing-commit-signature-verification) +- [Security Management Guide](.github/SECURITY_MANAGEMENT.md) +- [Rulesets README](.github/rulesets/README.md) + +## Getting Help + +- Review documentation in `.github/rulesets/README.md` +- Check `.github/SECURITY_MANAGEMENT.md` for detailed guidance +- Open an issue for questions +- Contact repository maintainers + +--- + +**Last Updated**: 2026-02-13 +**Version**: 1.0.0 diff --git a/.github/SECURITY_MANAGEMENT.md b/.github/SECURITY_MANAGEMENT.md new file mode 100644 index 000000000000..0f70f2e5ada0 --- /dev/null +++ b/.github/SECURITY_MANAGEMENT.md @@ -0,0 +1,370 @@ +# Security and Branch Protection Management Guide + +## Overview + +This document provides comprehensive guidance on managing security and branch protection for the Bitcoin Core repository. It covers the implementation, maintenance, and best practices for using GitHub Rulesets and security workflows. + +## Table of Contents + +1. [Branch Protection Strategy](#branch-protection-strategy) +2. [Security Rulesets](#security-rulesets) +3. [Required Status Checks](#required-status-checks) +4. [Code Review Requirements](#code-review-requirements) +5. [Commit Signature Verification](#commit-signature-verification) +6. [Security Workflows](#security-workflows) +7. [Incident Response](#incident-response) +8. [Best Practices](#best-practices) + +## Branch Protection Strategy + +### Protected Branches + +The following branches have protection rules enforced: + +1. **Main/Master Branch** + - Highest level of protection + - Requires 2 approving reviews + - All status checks must pass + - No force pushes or deletions + - Requires signed commits + - Linear history enforced + +2. **Release Branches** (release/*, v*) + - Same protection as main branch + - No bypass actors allowed + - Critical for version integrity + +3. **Development Branches** (dev, develop, development) + - Moderate protection + - Requires 1 approving review + - Basic status checks + - Allows faster iteration + +### Branch Naming Conventions + +Follow these conventions for automatic protection: +- `main` or `master` - Primary branch +- `release/*` - Release preparation branches +- `v*` - Version-specific branches +- `dev`, `develop`, `development` - Development branches +- `feature/*` - Feature branches (no automatic protection) +- `hotfix/*` - Critical fixes (follow release protection) + +## Security Rulesets + +### Ruleset Components + +Each ruleset includes: +- **Target**: Branch or tag protection +- **Conditions**: Ref patterns to match +- **Rules**: Protection rules to enforce +- **Bypass Actors**: Who can bypass rules and under what conditions +- **Enforcement**: Active or evaluation mode + +### Applying Rulesets + +To apply rulesets to your repository: + +1. **Via GitHub UI**: + - Navigate to Settings → Rules → Rulesets + - Create new ruleset + - Use JSON files in `.github/rulesets/` as templates + - Configure actors and contexts for your repository + +2. **Via GitHub API**: + ```bash + # Create a ruleset via API + curl -X POST \ + -H "Authorization: token YOUR_TOKEN" \ + -H "Accept: application/vnd.github+json" \ + https://api.github.com/repos/OWNER/REPO/rulesets \ + -d @.github/rulesets/branch-protection-main.json + ``` + +3. **Via GitHub CLI**: + ```bash + # List existing rulesets + gh api /repos/OWNER/REPO/rulesets + + # Create new ruleset + gh api --method POST /repos/OWNER/REPO/rulesets \ + --input .github/rulesets/branch-protection-main.json + ``` + +## Required Status Checks + +### Security Checks + +All protected branches require these security checks to pass: + +1. **CodeQL Analysis** + - Automated code scanning + - Detects security vulnerabilities + - Runs on all PRs and pushes + +2. **Dependency Review** + - Scans for vulnerable dependencies + - Checks license compatibility + - Runs on pull requests + +3. **Secret Scanning** + - Detects exposed secrets and credentials + - Uses Gitleaks for comprehensive scanning + - Runs on all commits + +4. **Security Audit** + - Trivy vulnerability scanner + - SARIF results uploaded to Security tab + - Fails on critical/high severity issues + +### Build and Test Checks + +1. **CI Build** + - Compiles on multiple platforms + - Verifies build system integrity + +2. **Test Suite** + - Unit tests + - Functional tests + - Integration tests + +3. **Linting** + - Code style verification + - Security linting with Semgrep + +## Code Review Requirements + +### Review Process + +1. **Number of Reviewers** + - Main/Release: 2 approving reviews + - Development: 1 approving review + +2. **Code Owner Review** + - Required for main and release branches + - Defined in `.github/CODEOWNERS` + - Ensures domain expertise + +3. **Review Thread Resolution** + - All comments must be resolved + - Ensures all feedback is addressed + +4. **Stale Review Dismissal** + - New commits dismiss old approvals + - Ensures reviews are current + +### Reviewer Responsibilities + +- Verify functionality works as intended +- Check for security vulnerabilities +- Ensure code quality and maintainability +- Validate test coverage +- Review documentation updates + +## Commit Signature Verification + +### Why Require Signatures + +- Verifies commit author identity +- Prevents unauthorized commits +- Maintains chain of trust +- Required for protected branches + +### Setting Up GPG Signing + +1. **Generate GPG Key**: + ```bash + gpg --full-generate-key + # Select RSA, 4096 bits + # Set appropriate expiration + ``` + +2. **Configure Git**: + ```bash + git config --global user.signingkey YOUR_KEY_ID + git config --global commit.gpgsign true + git config --global tag.gpgsign true + ``` + +3. **Add to GitHub**: + - Export public key: `gpg --armor --export YOUR_KEY_ID` + - Add to GitHub Settings → SSH and GPG keys + +4. **Verify Signatures**: + ```bash + git log --show-signature + git verify-commit HEAD + git verify-tag v1.0.0 + ``` + +## Security Workflows + +### Automated Security Scanning + +The `.github/workflows/security-checks.yml` workflow provides: + +1. **Dependency Review** - Runs on pull requests +2. **Secret Scanning** - Runs on all commits +3. **Vulnerability Scanning** - Trivy scanner +4. **Security Linting** - Semgrep rules +5. **Permissions Check** - File permission validation +6. **Signature Verification** - Commit signature checks + +### Manual Security Reviews + +Conduct manual security reviews for: +- Cryptographic code changes +- Authentication/authorization changes +- Network protocol modifications +- Consensus rule changes + +## Incident Response + +### Security Vulnerability Reporting + +Follow the process in `SECURITY.md`: +1. Email security@bitcoincore.org +2. Use GPG encryption for sensitive details +3. Wait for acknowledgment +4. Coordinate disclosure timeline + +### Emergency Procedures + +For critical security issues: +1. Notify security team immediately +2. May require bypass of protection rules +3. Document all bypass actions +4. Post-incident review required + +### Rollback Procedures + +If a security issue is merged: +1. Create hotfix branch +2. Revert problematic changes +3. Fast-track review process +4. Deploy fix to all affected versions + +## Best Practices + +### For Contributors + +1. **Always Sign Commits** + - Configure GPG signing in Git + - Verify signatures before pushing + +2. **Run Security Checks Locally** + ```bash + # Run CodeQL locally + codeql database create db --language=cpp + codeql database analyze db + + # Run Semgrep + semgrep --config=auto . + + # Check for secrets + gitleaks detect --source . + ``` + +3. **Review Security Guidelines** + - Read SECURITY.md + - Follow secure coding practices + - Document security considerations + +### For Maintainers + +1. **Review Rulesets Regularly** + - Quarterly review of effectiveness + - Update based on team feedback + - Adjust bypass actors as needed + +2. **Monitor Security Alerts** + - Check GitHub Security tab daily + - Triage Dependabot alerts + - Respond to secret scanning alerts + +3. **Enforce Security Practices** + - Require security reviews for sensitive code + - Ensure all checks pass before merge + - Document security decisions + +### For Administrators + +1. **Manage Bypass Actors** + - Minimize bypass permissions + - Audit bypass usage + - Remove unused bypass actors + +2. **Update Security Workflows** + - Keep actions up to date + - Add new security tools + - Improve detection capabilities + +3. **Team Training** + - Security awareness training + - Tool usage documentation + - Incident response drills + +## Configuration Management + +### Updating Rulesets + +When modifying rulesets: +1. Update JSON file in `.github/rulesets/` +2. Test in evaluation mode first +3. Document changes +4. Communicate to team +5. Apply via GitHub Settings +6. Monitor for issues + +### Version Control + +- All ruleset changes tracked in Git +- Include rationale in commit messages +- Review changes as code +- Maintain changelog + +### Backup and Recovery + +- Rulesets stored in repository +- Can be restored from Git history +- Export current settings periodically +- Document critical configurations + +## Compliance and Auditing + +### Audit Trail + +Maintain records of: +- Ruleset changes and rationale +- Bypass usage and justification +- Security incidents and responses +- Review process metrics + +### Reporting + +Generate regular reports on: +- Merge compliance rates +- Security check pass/fail rates +- Review time metrics +- Incident frequency + +## Resources + +- [GitHub Rulesets Documentation](https://docs.github.com/en/repositories/configuring-branches-and-merges-in-your-repository/managing-rulesets) +- [GitHub Security Features](https://docs.github.com/en/code-security) +- [Git Commit Signing](https://git-scm.com/book/en/v2/Git-Tools-Signing-Your-Work) +- [Bitcoin Core Security Process](SECURITY.md) + +## Support and Feedback + +For questions or improvements: +1. Open an issue for discussion +2. Propose changes via pull request +3. Contact security team for sensitive matters +4. Participate in security reviews + +--- + +**Version**: 1.0.0 +**Last Updated**: 2026-02-13 +**Maintained By**: Bitcoin Core Security Team diff --git a/.github/apply-rulesets.sh b/.github/apply-rulesets.sh new file mode 100755 index 000000000000..97355f5dcc9b --- /dev/null +++ b/.github/apply-rulesets.sh @@ -0,0 +1,207 @@ +#!/bin/bash +# apply-rulesets.sh +# Script to apply GitHub rulesets to a repository +# Usage: ./apply-rulesets.sh [OWNER] [REPO] + +set -e + +# Default values +OWNER="${1}" +REPO="${2}" +RULESETS_DIR=".github/rulesets" + +# Colors for output +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +NC='\033[0m' # No Color + +# Helper functions +error() { + echo -e "${RED}Error: $1${NC}" >&2 + exit 1 +} + +success() { + echo -e "${GREEN}✅ $1${NC}" +} + +warning() { + echo -e "${YELLOW}⚠️ $1${NC}" +} + +info() { + echo -e "$1" +} + +# Check prerequisites +check_prerequisites() { + info "Checking prerequisites..." + + # Check if gh CLI is installed + if ! command -v gh &> /dev/null; then + error "GitHub CLI (gh) is not installed. Install it from: https://cli.github.com/" + fi + + # Check if authenticated + if ! gh auth status &> /dev/null; then + error "Not authenticated with GitHub. Run: gh auth login" + fi + + # Check if rulesets directory exists + if [ ! -d "$RULESETS_DIR" ]; then + error "Rulesets directory not found: $RULESETS_DIR" + fi + + # Check if there are any ruleset files + if ! ls "$RULESETS_DIR"/*.json &> /dev/null; then + error "No ruleset JSON files found in $RULESETS_DIR" + fi + + success "Prerequisites check passed" +} + +# Get repository info +get_repo_info() { + if [ -z "$OWNER" ] || [ -z "$REPO" ]; then + info "No repository specified, using current repository..." + + # Try to get from gh CLI + if ! REPO_INFO=$(gh repo view --json nameWithOwner -q .nameWithOwner 2>/dev/null); then + error "Could not determine repository. Please specify: $0 OWNER REPO" + fi + + OWNER=$(echo "$REPO_INFO" | cut -d'/' -f1) + REPO=$(echo "$REPO_INFO" | cut -d'/' -f2) + fi + + info "Target repository: $OWNER/$REPO" + + # Verify repository access + if ! gh repo view "$OWNER/$REPO" &> /dev/null; then + error "Cannot access repository: $OWNER/$REPO" + fi + + # Check if user has admin access + info "Checking permissions..." + PERMISSION=$(gh api "/repos/$OWNER/$REPO" -q .permissions.admin) + if [ "$PERMISSION" != "true" ]; then + error "You need admin access to $OWNER/$REPO to manage rulesets" + fi + + success "Repository access verified" +} + +# Validate rulesets +validate_rulesets() { + info "Validating rulesets..." + + if [ -f ".github/validate-rulesets.py" ]; then + if ! python3 .github/validate-rulesets.py; then + error "Ruleset validation failed. Please fix the issues before applying." + fi + success "Rulesets validated successfully" + else + warning "Validation script not found, skipping validation" + fi +} + +# List existing rulesets +list_existing_rulesets() { + info "\nChecking for existing rulesets..." + + EXISTING=$(gh api "/repos/$OWNER/$REPO/rulesets" 2>/dev/null || echo "[]") + COUNT=$(echo "$EXISTING" | jq 'length') + + if [ "$COUNT" -eq "0" ]; then + info "No existing rulesets found" + else + warning "Found $COUNT existing ruleset(s):" + echo "$EXISTING" | jq -r '.[] | " - \(.name) (ID: \(.id), Status: \(.enforcement))"' + + read -p "Do you want to continue? This may create duplicates. (y/N): " -r + if [[ ! $REPLY =~ ^[Yy]$ ]]; then + info "Aborted by user" + exit 0 + fi + fi +} + +# Apply rulesets +apply_rulesets() { + info "\nApplying rulesets..." + + SUCCESS_COUNT=0 + FAIL_COUNT=0 + + for ruleset_file in "$RULESETS_DIR"/*.json; do + RULESET_NAME=$(basename "$ruleset_file") + info "\nProcessing: $RULESET_NAME" + + # Extract ruleset name from file for better error messages + DISPLAY_NAME=$(jq -r '.name // "Unknown"' "$ruleset_file") + + # Try to apply the ruleset + if gh api --method POST \ + -H "Accept: application/vnd.github+json" \ + "/repos/$OWNER/$REPO/rulesets" \ + --input "$ruleset_file" &> /dev/null; then + success "Applied: $DISPLAY_NAME" + ((SUCCESS_COUNT++)) + else + warning "Failed to apply: $DISPLAY_NAME" + info " Note: This may be due to duplicate names or invalid configuration" + ((FAIL_COUNT++)) + fi + done + + info "" + info "==================================================" + info "Summary:" + success "Successfully applied: $SUCCESS_COUNT ruleset(s)" + if [ $FAIL_COUNT -gt 0 ]; then + warning "Failed to apply: $FAIL_COUNT ruleset(s)" + fi +} + +# Show next steps +show_next_steps() { + info "" + info "==================================================" + info "Next Steps:" + info "1. Verify rulesets in GitHub:" + info " https://github.com/$OWNER/$REPO/settings/rules" + info "" + info "2. Update CODEOWNERS file with your team members" + info "" + info "3. Configure GPG signing for all contributors:" + info " See: .github/QUICK_START_RULESETS.md" + info "" + info "4. Test branch protection by creating a pull request" + info "" + info "5. Review security settings:" + info " https://github.com/$OWNER/$REPO/settings/security_analysis" + info "" + info "For more information, see:" + info " - .github/rulesets/README.md" + info " - .github/SECURITY_MANAGEMENT.md" + info " - .github/QUICK_START_RULESETS.md" +} + +# Main execution +main() { + info "GitHub Rulesets Setup Script" + info "==================================================" + + check_prerequisites + get_repo_info + validate_rulesets + list_existing_rulesets + apply_rulesets + show_next_steps + + success "\nSetup complete!" +} + +# Run main function +main diff --git a/.github/rulesets/README.md b/.github/rulesets/README.md new file mode 100644 index 000000000000..382070091e9e --- /dev/null +++ b/.github/rulesets/README.md @@ -0,0 +1,151 @@ +# Branch Protection and Security Rulesets + +This directory contains GitHub repository rulesets that define branch protection rules, tag protection, and security policies for the Bitcoin Core repository. + +## Overview + +GitHub Rulesets provide a declarative way to manage branch protection, tag protection, and other repository policies. These rulesets help ensure code quality, security, and maintain the integrity of the Bitcoin Core codebase. + +## Ruleset Files + +### 1. Main Branch Protection (`branch-protection-main.json`) +Applies comprehensive protection to the main/master branch: +- **Required Reviews**: 2 approving reviews required +- **Code Owner Review**: Required for changes affecting code ownership areas +- **Status Checks**: CodeQL, build, test, and security scan must pass +- **Restrictions**: + - Branch deletion prevented + - Force pushes blocked + - Requires commit signatures + - Requires linear history +- **Stale Reviews**: Dismissed when new commits are pushed + +### 2. Release Branch Protection (`branch-protection-release.json`) +Protects release branches (release/*, v*): +- **Required Reviews**: 2 approving reviews required +- **Code Owner Review**: Required +- **Status Checks**: All CI/CD checks must pass +- **Restrictions**: + - Branch deletion prevented + - Force pushes blocked + - Requires commit signatures +- **No Bypass**: No bypass actors allowed + +### 3. Tag Protection (`tag-protection.json`) +Protects version tags (v*, all tags): +- **Creation**: Restricted to authorized users +- **Update**: Blocked to prevent tag modification +- **Deletion**: Prevented to maintain version history +- **Signatures**: Required for tag creation +- **Bypass**: Only repository admins can bypass + +### 4. Development Branch Protection (`branch-protection-dev.json`) +Basic protection for development branches: +- **Required Reviews**: 1 approving review required +- **Status Checks**: Build and test must pass +- **Restrictions**: Branch deletion prevented +- **Flexible**: Less strict for active development + +### 5. Security and Compliance (`security-checks.json`) +Security rules applicable to all branches: +- **Security Scans**: CodeQL and dependency scanning required +- **Applies To**: All branches except automated bot branches +- **No Bypass**: Ensures security checks are always performed + +## Safe Practices + +### 1. Enabling Rulesets +These ruleset files serve as templates. To enable them in your GitHub repository: + +1. Go to repository Settings → Rules → Rulesets +2. Click "New ruleset" → "New branch ruleset" or "New tag ruleset" +3. Use the JSON configurations from these files as reference +4. Adjust actor IDs and contexts to match your repository's specific setup + +**Note**: GitHub does not currently support importing rulesets directly from JSON files in the repository. These files serve as documentation and configuration templates. + +### 2. Customization Guidelines + +When customizing rulesets: +- **Actor IDs**: Update bypass_actors to match your repository roles +- **Status Checks**: Adjust context names to match your CI/CD workflow names +- **Review Requirements**: Modify based on team size and workflow +- **Branch Patterns**: Update ref_name patterns to match your branching strategy + +### 3. Security Best Practices + +- **Commit Signatures**: Always require GPG signed commits for protected branches +- **Status Checks**: Ensure all security scans complete before merging +- **Review Requirements**: Enforce code owner reviews for critical code paths +- **No Force Push**: Prevent history rewriting on protected branches +- **Linear History**: Maintain clean commit history +- **Stale Review Dismissal**: Ensure reviews are current with latest code + +### 4. Bypass Actors + +Configure bypass actors carefully: +- **Repository Admins**: May need bypass for emergency fixes +- **Bots**: Dependabot/Renovate may need bypass for automated updates +- **Use Pull Request Mode**: Prefer "pull_request" over "always" bypass mode +- **Minimize Bypasses**: Keep the number of bypass actors minimal + +### 5. Testing Rulesets + +Before enforcing rulesets in production: +1. Set enforcement to "evaluate" mode initially +2. Monitor for any issues or conflicts +3. Adjust rules based on team feedback +4. Switch to "active" enforcement once validated + +## Ruleset Management Workflow + +### Adding New Protection Rules + +1. Create a new JSON file in this directory +2. Define the ruleset configuration +3. Document the purpose and rules +4. Test in evaluation mode +5. Enable in the repository settings + +### Modifying Existing Rules + +1. Update the JSON configuration file +2. Document changes in commit message +3. Review with team before applying +4. Update the ruleset in repository settings +5. Communicate changes to the team + +### Reviewing Ruleset Effectiveness + +Regularly review: +- Number of bypasses used +- Status check failure rates +- Time to merge pull requests +- Team feedback on friction points + +## Integration with CI/CD + +Ensure your CI/CD workflows align with ruleset requirements: + +- **Status Check Names**: Match the contexts defined in rulesets +- **Required Checks**: Include all security scans (CodeQL, dependency scanning) +- **Build and Test**: Ensure comprehensive test coverage +- **Performance**: Optimize CI/CD to complete within reasonable time + +## References + +- [GitHub Rulesets Documentation](https://docs.github.com/en/repositories/configuring-branches-and-merges-in-your-repository/managing-rulesets/about-rulesets) +- [Branch Protection Best Practices](https://docs.github.com/en/repositories/configuring-branches-and-merges-in-your-repository/managing-protected-branches/about-protected-branches) +- [Requiring Commit Signatures](https://docs.github.com/en/authentication/managing-commit-signature-verification/about-commit-signature-verification) + +## Support + +For questions or issues with rulesets: +1. Check the GitHub Rulesets documentation +2. Review this README for guidance +3. Consult with repository maintainers +4. Open an issue for discussion + +## Version History + +- v1.0.0 - Initial ruleset configurations for branch protection, tag protection, and security diff --git a/.github/rulesets/branch-protection-dev.json b/.github/rulesets/branch-protection-dev.json new file mode 100644 index 000000000000..8812375d58c7 --- /dev/null +++ b/.github/rulesets/branch-protection-dev.json @@ -0,0 +1,54 @@ +{ + "name": "Development Branch Protection", + "description": "Basic protection rules for development branches", + "target": "branch", + "enforcement": "active", + "conditions": { + "ref_name": { + "include": [ + "refs/heads/dev", + "refs/heads/develop", + "refs/heads/development" + ], + "exclude": [] + } + }, + "rules": [ + { + "type": "pull_request", + "parameters": { + "required_approving_review_count": 1, + "dismiss_stale_reviews_on_push": false, + "require_code_owner_review": false, + "require_last_push_approval": false, + "required_review_thread_resolution": false + } + }, + { + "type": "required_status_checks", + "parameters": { + "required_status_checks": [ + { + "context": "ci/build", + "integration_id": null + }, + { + "context": "ci/test", + "integration_id": null + } + ], + "strict_required_status_checks_policy": false + } + }, + { + "type": "deletion" + } + ], + "bypass_actors": [ + { + "actor_id": 2, + "actor_type": "RepositoryRole", + "bypass_mode": "pull_request" + } + ] +} diff --git a/.github/rulesets/branch-protection-main.json b/.github/rulesets/branch-protection-main.json new file mode 100644 index 000000000000..636fcf2c0a73 --- /dev/null +++ b/.github/rulesets/branch-protection-main.json @@ -0,0 +1,76 @@ +{ + "name": "Main Branch Protection", + "description": "Comprehensive protection rules for the main/master branch to ensure code quality and security", + "target": "branch", + "enforcement": "active", + "conditions": { + "ref_name": { + "include": [ + "refs/heads/main", + "refs/heads/master" + ], + "exclude": [] + } + }, + "rules": [ + { + "type": "pull_request", + "parameters": { + "required_approving_review_count": 2, + "dismiss_stale_reviews_on_push": true, + "require_code_owner_review": true, + "require_last_push_approval": true, + "required_review_thread_resolution": true + } + }, + { + "type": "required_status_checks", + "parameters": { + "required_status_checks": [ + { + "context": "CodeQL", + "integration_id": null + }, + { + "context": "ci/build", + "integration_id": null + }, + { + "context": "ci/test", + "integration_id": null + }, + { + "context": "security-scan", + "integration_id": null + } + ], + "strict_required_status_checks_policy": true + } + }, + { + "type": "deletion" + }, + { + "type": "non_fast_forward" + }, + { + "type": "required_signatures" + }, + { + "type": "required_linear_history" + }, + { + "type": "required_deployments", + "parameters": { + "required_deployment_environments": [] + } + } + ], + "bypass_actors": [ + { + "actor_id": 1, + "actor_type": "RepositoryRole", + "bypass_mode": "pull_request" + } + ] +} diff --git a/.github/rulesets/branch-protection-release.json b/.github/rulesets/branch-protection-release.json new file mode 100644 index 000000000000..e6430b7414e0 --- /dev/null +++ b/.github/rulesets/branch-protection-release.json @@ -0,0 +1,61 @@ +{ + "name": "Release Branch Protection", + "description": "Protection rules for release branches to prevent unauthorized modifications", + "target": "branch", + "enforcement": "active", + "conditions": { + "ref_name": { + "include": [ + "refs/heads/release/*", + "refs/heads/v*" + ], + "exclude": [] + } + }, + "rules": [ + { + "type": "pull_request", + "parameters": { + "required_approving_review_count": 2, + "dismiss_stale_reviews_on_push": true, + "require_code_owner_review": true, + "require_last_push_approval": true, + "required_review_thread_resolution": true + } + }, + { + "type": "required_status_checks", + "parameters": { + "required_status_checks": [ + { + "context": "CodeQL", + "integration_id": null + }, + { + "context": "ci/build", + "integration_id": null + }, + { + "context": "ci/test", + "integration_id": null + }, + { + "context": "security-scan", + "integration_id": null + } + ], + "strict_required_status_checks_policy": true + } + }, + { + "type": "deletion" + }, + { + "type": "non_fast_forward" + }, + { + "type": "required_signatures" + } + ], + "bypass_actors": [] +} diff --git a/.github/rulesets/security-checks.json b/.github/rulesets/security-checks.json new file mode 100644 index 000000000000..d02462ba0528 --- /dev/null +++ b/.github/rulesets/security-checks.json @@ -0,0 +1,36 @@ +{ + "name": "Security and Compliance Ruleset", + "description": "Security-focused rules applicable to all branches", + "target": "branch", + "enforcement": "active", + "conditions": { + "ref_name": { + "include": [ + "refs/heads/*" + ], + "exclude": [ + "refs/heads/dependabot/*", + "refs/heads/renovate/*" + ] + } + }, + "rules": [ + { + "type": "required_status_checks", + "parameters": { + "required_status_checks": [ + { + "context": "security/codeql", + "integration_id": null + }, + { + "context": "security/dependency-scan", + "integration_id": null + } + ], + "strict_required_status_checks_policy": false + } + } + ], + "bypass_actors": [] +} diff --git a/.github/rulesets/tag-protection.json b/.github/rulesets/tag-protection.json new file mode 100644 index 000000000000..b9feb992875a --- /dev/null +++ b/.github/rulesets/tag-protection.json @@ -0,0 +1,36 @@ +{ + "name": "Tag Protection", + "description": "Protection rules for version tags to ensure release integrity", + "target": "tag", + "enforcement": "active", + "conditions": { + "ref_name": { + "include": [ + "refs/tags/v*", + "refs/tags/*" + ], + "exclude": [] + } + }, + "rules": [ + { + "type": "creation" + }, + { + "type": "update" + }, + { + "type": "deletion" + }, + { + "type": "required_signatures" + } + ], + "bypass_actors": [ + { + "actor_id": 1, + "actor_type": "RepositoryRole", + "bypass_mode": "always" + } + ] +} diff --git a/.github/validate-rulesets.py b/.github/validate-rulesets.py new file mode 100755 index 000000000000..d921b1690155 --- /dev/null +++ b/.github/validate-rulesets.py @@ -0,0 +1,166 @@ +#!/usr/bin/env python3 +""" +Validate GitHub Rulesets and Security Configuration + +This script validates the ruleset JSON files and security configuration +to ensure they follow best practices and are properly formatted. +""" + +import json +import sys +import os +from pathlib import Path +from typing import Dict, List, Any + +def validate_json_file(filepath: Path) -> tuple[bool, str]: + """Validate that a file contains valid JSON.""" + try: + with open(filepath, 'r') as f: + json.load(f) + return True, "Valid JSON" + except json.JSONDecodeError as e: + return False, f"Invalid JSON: {e}" + except Exception as e: + return False, f"Error reading file: {e}" + +def validate_ruleset_structure(ruleset: Dict[str, Any]) -> List[str]: + """Validate the structure of a ruleset.""" + issues = [] + + # Required fields + required_fields = ['name', 'target', 'enforcement', 'rules'] + for field in required_fields: + if field not in ruleset: + issues.append(f"Missing required field: {field}") + + # Validate target + if 'target' in ruleset and ruleset['target'] not in ['branch', 'tag']: + issues.append(f"Invalid target: {ruleset['target']}. Must be 'branch' or 'tag'") + + # Validate enforcement + if 'enforcement' in ruleset and ruleset['enforcement'] not in ['active', 'evaluate', 'disabled']: + issues.append(f"Invalid enforcement: {ruleset['enforcement']}") + + # Validate rules array + if 'rules' in ruleset: + if not isinstance(ruleset['rules'], list): + issues.append("Rules must be an array") + elif len(ruleset['rules']) == 0: + issues.append("Rules array is empty") + + return issues + +def validate_security_checks(ruleset: Dict[str, Any]) -> List[str]: + """Validate security-related checks in rulesets.""" + issues = [] + + # Check if ruleset includes security status checks + if 'rules' in ruleset: + has_status_checks = False + for rule in ruleset['rules']: + if rule.get('type') == 'required_status_checks': + has_status_checks = True + # Check for security contexts + params = rule.get('parameters', {}) + checks = params.get('required_status_checks', []) + + security_contexts = ['CodeQL', 'security-scan', 'security/codeql'] + has_security = any( + any(ctx.lower() in check.get('context', '').lower() for ctx in security_contexts) + for check in checks + ) + + if not has_security and 'main' in ruleset.get('name', '').lower(): + issues.append("Main branch protection should include security checks") + + if not has_status_checks and ruleset.get('target') == 'branch': + issues.append("Branch protection should include status checks") + + return issues + +def validate_branch_protection(ruleset: Dict[str, Any]) -> List[str]: + """Validate branch protection best practices.""" + issues = [] + + if ruleset.get('target') != 'branch': + return issues + + name = ruleset.get('name', '').lower() + is_main = 'main' in name or 'master' in name + is_release = 'release' in name + + # Check for critical protection rules + rule_types = {rule.get('type') for rule in ruleset.get('rules', [])} + + if is_main or is_release: + critical_rules = ['deletion', 'non_fast_forward', 'pull_request'] + missing = [rule for rule in critical_rules if rule not in rule_types] + if missing: + issues.append(f"Critical protection missing for {name}: {', '.join(missing)}") + + # Check review requirements + for rule in ruleset.get('rules', []): + if rule.get('type') == 'pull_request': + params = rule.get('parameters', {}) + review_count = params.get('required_approving_review_count', 0) + if is_main and review_count < 2: + issues.append("Main branch should require at least 2 approving reviews") + + return issues + +def main(): + """Main validation function.""" + script_dir = Path(__file__).parent + rulesets_dir = script_dir / 'rulesets' + + if not rulesets_dir.exists(): + print(f"Error: Rulesets directory not found: {rulesets_dir}") + return 1 + + print("Validating GitHub Rulesets...") + print("=" * 60) + + all_valid = True + ruleset_files = list(rulesets_dir.glob('*.json')) + + if not ruleset_files: + print("Warning: No ruleset JSON files found") + return 0 + + for filepath in ruleset_files: + print(f"\nValidating: {filepath.name}") + print("-" * 60) + + # Validate JSON syntax + is_valid, message = validate_json_file(filepath) + if not is_valid: + print(f"❌ {message}") + all_valid = False + continue + + # Load and validate structure + with open(filepath, 'r') as f: + ruleset = json.load(f) + + issues = [] + issues.extend(validate_ruleset_structure(ruleset)) + issues.extend(validate_security_checks(ruleset)) + issues.extend(validate_branch_protection(ruleset)) + + if issues: + all_valid = False + for issue in issues: + print(f"⚠️ {issue}") + else: + print("✅ Ruleset is valid") + + print("\n" + "=" * 60) + if all_valid: + print("✅ All rulesets validated successfully") + return 0 + else: + print("❌ Validation failed - please fix the issues above") + return 1 + +if __name__ == '__main__': + sys.exit(main()) diff --git a/.github/workflows/security-checks.yml b/.github/workflows/security-checks.yml new file mode 100644 index 000000000000..62a3cd057c8f --- /dev/null +++ b/.github/workflows/security-checks.yml @@ -0,0 +1,121 @@ +name: Security Checks + +on: + push: + branches: [ "**" ] + pull_request: + branches: [ "**" ] + schedule: + # Run weekly security scans + - cron: '0 0 * * 0' + +permissions: + contents: read + security-events: write + pull-requests: read + +jobs: + dependency-review: + name: Dependency Review + runs-on: ubuntu-latest + if: github.event_name == 'pull_request' + steps: + - name: Checkout Repository + uses: actions/checkout@v4 + + - name: Dependency Review + uses: actions/dependency-review-action@v4 + with: + fail-on-severity: moderate + deny-licenses: GPL-3.0, AGPL-3.0 + comment-summary-in-pr: always + + secret-scanning: + name: Secret Scanning + runs-on: ubuntu-latest + steps: + - name: Checkout Repository + uses: actions/checkout@v4 + + - name: Gitleaks Scan + uses: gitleaks/gitleaks-action@v2 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + security-audit: + name: Security Audit + runs-on: ubuntu-latest + steps: + - name: Checkout Repository + uses: actions/checkout@v4 + + - name: Run Trivy vulnerability scanner + uses: aquasecurity/trivy-action@master + with: + scan-type: 'fs' + scan-ref: '.' + format: 'sarif' + output: 'trivy-results.sarif' + severity: 'CRITICAL,HIGH' + + - name: Upload Trivy results to GitHub Security tab + uses: github/codeql-action/upload-sarif@v3 + if: always() + with: + sarif_file: 'trivy-results.sarif' + + linting-security: + name: Security Linting + runs-on: ubuntu-latest + steps: + - name: Checkout Repository + uses: actions/checkout@v4 + + - name: Run Semgrep + uses: returntocorp/semgrep-action@v1 + with: + config: >- + p/security-audit + p/secrets + p/owasp-top-ten + env: + SEMGREP_RULES: >- + p/ci + p/security-audit + + permissions-check: + name: Permissions Check + runs-on: ubuntu-latest + steps: + - name: Checkout Repository + uses: actions/checkout@v4 + + - name: Check for sensitive file permissions + run: | + echo "Checking for files with overly permissive permissions..." + # Check for executable files that shouldn't be + find . -type f \( -name "*.json" -executable \) -o -type f \( -name "*.md" -executable \) | while read file; do + echo "Warning: $file is executable but probably shouldn't be" + done + + # Check for world-writable files + find . -type f -perm -002 2>/dev/null | while read file; do + echo "Error: $file is world-writable" + exit 1 + done + + commit-signature-check: + name: Commit Signature Verification + runs-on: ubuntu-latest + if: github.event_name == 'pull_request' + steps: + - name: Checkout Repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Verify commit signatures + run: | + echo "Checking commit signatures..." + git log --show-signature origin/${{ github.base_ref }}..${{ github.event.pull_request.head.sha }} | \ + grep -E "gpg: Good signature|gpg: BAD signature" || echo "Warning: Some commits may not be signed" diff --git a/.github/workflows/validate-rulesets.yml b/.github/workflows/validate-rulesets.yml new file mode 100644 index 000000000000..080967ef6093 --- /dev/null +++ b/.github/workflows/validate-rulesets.yml @@ -0,0 +1,55 @@ +name: Validate Rulesets + +on: + push: + paths: + - '.github/rulesets/**' + - '.github/validate-rulesets.py' + pull_request: + paths: + - '.github/rulesets/**' + - '.github/validate-rulesets.py' + +jobs: + validate: + name: Validate Ruleset Configuration + runs-on: ubuntu-latest + + permissions: + contents: read + + steps: + - name: Checkout Repository + uses: actions/checkout@v4 + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: '3.x' + + - name: Validate Rulesets + run: | + python3 .github/validate-rulesets.py + + - name: Check JSON Formatting + run: | + echo "Checking JSON formatting..." + for file in .github/rulesets/*.json; do + if [ -f "$file" ]; then + echo "Validating $file..." + python3 -m json.tool "$file" > /dev/null || exit 1 + fi + done + echo "All JSON files are properly formatted" + + - name: Summary + if: always() + run: | + echo "## Ruleset Validation Results" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "Validated the following rulesets:" >> $GITHUB_STEP_SUMMARY + for file in .github/rulesets/*.json; do + if [ -f "$file" ]; then + echo "- $(basename $file)" >> $GITHUB_STEP_SUMMARY + fi + done diff --git a/README.md b/README.md index cfabb7f6723b..2ff12e29b364 100644 --- a/README.md +++ b/README.md @@ -77,3 +77,31 @@ Translations are periodically pulled from Transifex and merged into the git repo **Important**: We do not accept translation changes as GitHub pull requests because the next pull from Transifex would automatically overwrite them again. + +Security and Branch Protection +------------------------------- + +This repository uses comprehensive branch protection rules and security policies to maintain code quality and integrity. + +### Branch Protection Rulesets + +The repository is protected with GitHub Rulesets that enforce: +- Required code reviews before merging +- Status checks including security scans +- Signed commits for protected branches +- Prevention of force pushes and branch deletions + +See [.github/rulesets/README.md](.github/rulesets/README.md) for detailed information about protection rules. + +### Security Policies + +All changes undergo automated security scanning: +- **CodeQL Analysis**: Automated vulnerability detection +- **Dependency Scanning**: Detection of vulnerable dependencies +- **Secret Scanning**: Prevention of exposed credentials +- **Security Linting**: Automated security best practices checks + +For more information, see: +- [SECURITY.md](SECURITY.md) - Security vulnerability reporting +- [.github/SECURITY_MANAGEMENT.md](.github/SECURITY_MANAGEMENT.md) - Security management guide +- [.github/QUICK_START_RULESETS.md](.github/QUICK_START_RULESETS.md) - Quick start for rulesets setup