Skip to content

Instantly share code, notes, and snippets.

@shrwnsan
Last active January 11, 2026 04:49
Show Gist options
  • Select an option

  • Save shrwnsan/c0a4eaa82e66a6e8c5ddcc0d00a8841f to your computer and use it in GitHub Desktop.

Select an option

Save shrwnsan/c0a4eaa82e66a6e8c5ddcc0d00a8841f to your computer and use it in GitHub Desktop.
Simple GitHub Repo Security Initialization — Apply security settings to GitHub repos in one command. Secret scanning, branch protection for main AND default branch, admin enforcement. v1.2.0: Now protects main branch even when default differs.

🔒 Simple GitHub Repo Security Initialization (github-security-init)

Apply security best practices to GitHub repositories in one command.

Perfect for: New maintainers who want to secure their repos without navigating GitHub Settings.

What It Does

  • Secret Scanning — Detects leaked API keys/credentials in commits
  • Branch Protection — Prevents accidental history rewrites (force push disabled)
  • Admin Enforcement — Even repo owners must follow protection rules

Quick Install

# Download and make executable
curl -fsSL https://gist.githubusercontent.com/shrwnsan/c0a4eaa82e66a6e8c5ddcc0d00a8841f/raw/github-security-init -o github-security-init
chmod +x github-security-init

# OR download manually
# 1. Click "Raw" button on the script file above
# 2. Save file as `github-security-init`
# 3. Run: chmod +x github-security-init

Prerequisites

  • GitHub CLI (gh command)
  • Authenticated: gh auth login
  • Write access to target repository

Usage

# From within a repo (auto-detects from git remote)
./github-security-init . --dry-run   # Preview first
./github-security-init .             # Apply changes

# Explicit repository
./github-security-init username/repo

Features

  • Auto-detects repo from git remote (SSH & HTTPS supported)
  • Dry-run mode for safe preview
  • Colored output (success/skip/error states)
  • Graceful handling of unavailable features (free tier)

Output Example

🔒 Analyzing repository: username/repo
[INFO] Repository visibility: public
[INFO] Default branch: main
[INFO] Secret scanning: disabled

[INFO] Checking secret scanning...
[SUCCESS] Secret scanning enabled

[INFO] Checking branch protection for 'main'...
[SUCCESS] Branch protection configured for 'main'

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
[APPLIED] Security configuration completed!
   ✓ Secret scanning enabled
   ✓ Branch protection configured for 'main'
   ✓ Admin enforcement enabled
   ✓ Force pushes disabled
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

Limitations

  • Secret scanning requires public repo or GitHub Pro/Team/Enterprise (free private repos: skips)
  • Branch protection requires write permissions (collaborator+: skips)
  • Gist repositories are not supported

Resources

License

MIT — Feel free to use, modify, and distribute.


Tip: Add to your ~/bin/ or /usr/local/bin/ to run from anywhere:

mv github-security-init ~/bin/
# Then use: github-security-init . (no ./ needed)
#!/usr/bin/env bash
# github-security-init - Apply security settings to a GitHub repository
# Version: 1.2.0
# License: MIT
# Source: https://gist.github.com/shrwnsan/c0a4eaa82e66a6e8c5ddcc0d00a8841f
# Usage: github-security-init [owner/repo | .] [--dry-run]
set -e
# Parse arguments
DRY_RUN=false
REPO=""
while [[ $# -gt 0 ]]; do
case $1 in
--dry-run)
DRY_RUN=true
shift
;;
-*)
echo "Unknown option: $1"
echo "Usage: $0 [owner/repo | .] [--dry-run]"
exit 1
;;
*)
REPO="$1"
shift
;;
esac
done
if [ -z "$REPO" ]; then
echo "Usage: $0 [owner/repo | .] [--dry-run]"
echo "Example: $0 shrwnsan/dotfiles"
echo " $0 . --dry-run (auto-detect from git remote)"
exit 1
fi
# Handle current directory detection
if [ "$REPO" = "." ]; then
if ! git rev-parse --git-dir >/dev/null 2>&1; then
echo "Error: Not in a git repository"
echo "Run this command from within a git repository, or specify owner/repo explicitly"
exit 1
fi
REMOTE_URL=$(git remote get-url origin 2>/dev/null || true)
if [ -z "$REMOTE_URL" ]; then
echo "Error: No 'origin' remote found"
echo "Please either:"
echo " 1. Add an origin remote: git remote add origin <repo-url>"
echo " 2. Specify owner/repo explicitly: $0 owner/repo"
exit 1
fi
# Parse remote URL to extract owner/repo
# Handle SSH format: [email protected]:owner/repo.git
# Handle HTTPS format: https://github.com/owner/repo.git
if [[ "$REMOTE_URL" =~ ^git@github\.com: ]]; then
# SSH format: [email protected]:owner/repo.git
REPO_PART="${REMOTE_URL#[email protected]:}"
REPO_PART="${REPO_PART%.git}"
REPO="$REPO_PART"
elif [[ "$REMOTE_URL" =~ ^https://github\.com/ ]]; then
# HTTPS format: https://github.com/owner/repo.git
REPO_PART="${REMOTE_URL#https://github.com/}"
REPO_PART="${REPO_PART%.git}"
REPO="$REPO_PART"
elif [[ "$REMOTE_URL" =~ ^https://gist\.github\.com/ ]]; then
echo "Error: Gist repositories are not supported"
exit 1
else
echo "Error: Unable to parse remote URL: $REMOTE_URL"
echo "Expected format: [email protected]:owner/repo.git or https://github.com/owner/repo.git"
echo "Please specify owner/repo explicitly: $0 owner/repo"
exit 1
fi
echo "Detected repository from git remote: $REPO"
fi
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
CYAN='\033[0;36m'
NC='\033[0m' # No Color
# Track what features were applied
APPLIED_FEATURES=()
SKIPPED_FEATURES=()
ALREADY_CONFIGURED=()
DRY_RUN_PREFIX=""
if [ "$DRY_RUN" = true ]; then
DRY_RUN_PREFIX="${CYAN}[DRY-RUN]${NC} "
fi
log_info() {
local prefix="$DRY_RUN_PREFIX"
echo -e "${prefix}${BLUE}[INFO]${NC} $1"
}
log_success() {
local prefix="$DRY_RUN_PREFIX"
echo -e "${prefix}${GREEN}[SUCCESS]${NC} $1"
APPLIED_FEATURES+=("$1")
}
log_skip() {
local prefix="$DRY_RUN_PREFIX"
echo -e "${prefix}${YELLOW}[SKIP]${NC} $1"
SKIPPED_FEATURES+=("$1")
}
log_configured() {
local prefix="$DRY_RUN_PREFIX"
echo -e "${prefix}${CYAN}[CONFIGURED]${NC} $1"
ALREADY_CONFIGURED+=("$1")
}
log_error() {
local prefix="$DRY_RUN_PREFIX"
echo -e "${prefix}${RED}[ERROR]${NC} $1" >&2
}
# Check if gh is installed and authenticated
if ! command -v gh &> /dev/null; then
log_error "GitHub CLI (gh) is not installed"
log_info "Install it from: https://cli.github.com/"
exit 1
fi
if ! gh auth status &> /dev/null; then
log_error "GitHub CLI is not authenticated"
log_info "Run: gh auth login"
exit 1
fi
echo "🔒 Analyzing repository: $REPO"
if [ "$DRY_RUN" = true ]; then
echo -e "${CYAN}[DRY-RUN MODE]${NC} No changes will be applied"
echo
fi
# Get repository info - using set +e to handle potential failures temporarily
set +e
REPO_INFO=$(gh repo view "$REPO" --json visibility,defaultBranchRef --jq '.visibility,.defaultBranchRef.name' 2>/dev/null)
REPO_EXIT_CODE=$?
set -e
if [ $REPO_EXIT_CODE -ne 0 ] || [ -z "$REPO_INFO" ]; then
log_error "Could not retrieve repository information for '$REPO'"
log_info "Please verify the repository exists and you have access to it"
exit 1
fi
# Parse repository info
VISIBILITY=$(echo "$REPO_INFO" | head -n 1)
DEFAULT_BRANCH=$(echo "$REPO_INFO" | tail -n 1)
if [ -z "$DEFAULT_BRANCH" ]; then
DEFAULT_BRANCH="main"
fi
log_info "Repository visibility: $VISIBILITY"
log_info "Default branch: $DEFAULT_BRANCH"
# Get secret scanning status via API
set +e
SECRET_SCANNING_STATUS=$(gh api "repos/$REPO" --jq '.security_and_analysis.secret_scanning.status' 2>/dev/null)
set -e
if [ "$SECRET_SCANNING_STATUS" = "enabled" ]; then
SECRET_SCANNING_ENABLED=true
log_info "Secret scanning: enabled"
else
SECRET_SCANNING_ENABLED=false
log_info "Secret scanning: disabled"
fi
echo
# Function to check if branch protection exists
check_branch_protection() {
local repo="$1"
local branch="$2"
set +e
PROTECTION=$(gh api "repos/$repo/branches/$branch/protection" --silent 2>/dev/null)
local exit_code=$?
set -e
if [ $exit_code -eq 0 ] && [ -n "$PROTECTION" ]; then
return 0 # Protection exists
fi
return 1 # No protection
}
# Determine branches to protect (always main if different from default)
BRANCHES_TO_PROTECT=("$DEFAULT_BRANCH")
if [ "$DEFAULT_BRANCH" != "main" ]; then
BRANCHES_TO_PROTECT+=("main")
fi
# Check current state before attempting changes
log_info "Checking current security configuration..."
# Check secret scanning state
SECRET_SCANNING_ALREADY_ENABLED=false
if [ "$SECRET_SCANNING_ENABLED" = "true" ]; then
SECRET_SCANNING_ALREADY_ENABLED=true
fi
# Check branch protection state for all target branches
# Use arrays in parallel to track branch names and their protection status
PROTECTED_BRANCHES=()
UNPROTECTED_BRANCHES=()
for branch in "${BRANCHES_TO_PROTECT[@]}"; do
if check_branch_protection "$REPO" "$branch"; then
PROTECTED_BRANCHES+=("$branch")
else
UNPROTECTED_BRANCHES+=("$branch")
fi
done
echo
# Determine what features are available and apply if needed
# Note: We attempt all features and handle failures gracefully since we can't
# reliably determine account tier/feature availability via API
# Try to enable secret scanning
log_info "Checking secret scanning..."
if [ "$SECRET_SCANNING_ALREADY_ENABLED" = true ]; then
log_configured "Secret scanning is already enabled"
elif [ "$DRY_RUN" = true ]; then
log_info "Would enable secret scanning"
APPLIED_FEATURES+=("Secret scanning (would be enabled)")
else
if gh repo edit "$REPO" --enable-secret-scanning 2>/dev/null; then
log_success "Secret scanning enabled"
else
log_skip "Secret scanning (not available for this repository - requires public repo or GitHub Pro)"
fi
fi
# Apply branch protection to all target branches
for branch in "${BRANCHES_TO_PROTECT[@]}"; do
log_info "Checking branch protection for '$branch'..."
# Check if branch is already protected
IS_PROTECTED=false
for protected in "${PROTECTED_BRANCHES[@]}"; do
if [ "$branch" = "$protected" ]; then
IS_PROTECTED=true
break
fi
done
if [ "$IS_PROTECTED" = true ]; then
log_configured "Branch protection already configured for '$branch'"
elif [ "$DRY_RUN" = true ]; then
log_info "Would configure branch protection for '$branch' (admin enforcement + force push disabled)"
APPLIED_FEATURES+=("Branch protection for '$branch'")
else
BRANCH_PROTECTION_PAYLOAD='{
"required_status_checks": {
"strict": false,
"contexts": []
},
"enforce_admins": true,
"required_pull_request_reviews": {
"required_approving_review_count": 0,
"dismiss_stale_reviews": false,
"require_code_owner_reviews": false,
"require_last_push_approval": false
},
"restrictions": null
}'
if echo "$BRANCH_PROTECTION_PAYLOAD" | gh api "repos/$REPO/branches/$branch/protection" --method PUT --input - --silent 2>/dev/null; then
log_success "Branch protection configured for '$branch' (admin enforcement + force push disabled)"
else
log_skip "Branch protection for '$branch' (could not apply - branch may not exist or requires additional permissions)"
fi
fi
done
# Print summary
echo
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
if [ ${#ALREADY_CONFIGURED[@]} -gt 0 ]; then
echo -e "${CYAN}[CONFIGURED]${NC} Features already in place:"
for feature in "${ALREADY_CONFIGURED[@]}"; do
echo " ✓ $feature"
done
echo
fi
if [ ${#APPLIED_FEATURES[@]} -gt 0 ]; then
if [ "$DRY_RUN" = true ]; then
echo -e "${CYAN}[DRY-RUN]${NC} Changes that would be applied:"
else
echo -e "${GREEN}[APPLIED]${NC} Security configuration completed!"
fi
echo
for feature in "${APPLIED_FEATURES[@]}"; do
echo " ✓ $feature"
done
elif [ "$DRY_RUN" != true ]; then
log_info "No new security features were applied"
fi
if [ ${#SKIPPED_FEATURES[@]} -gt 0 ]; then
echo
echo "Skipped features:"
for feature in "${SKIPPED_FEATURES[@]}"; do
echo " ○ $feature"
done
fi
# Exit with appropriate code
if [ ${#APPLIED_FEATURES[@]} -eq 0 ] && [ ${#SKIPPED_FEATURES[@]} -gt 0 ] && [ "$DRY_RUN" != true ]; then
echo
log_info "Repository '$REPO' may already have security configured, or lacks permissions for some features"
exit 0
fi
echo
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment