Secure your repository with branch protection rules on main, understand the danger of leaked API keys and database credentials, make use of GitHub Secret Scanning and Dependabot Alerts, and learn the emergency steps if a secret is already committed.

In episode 12 we learned to manage work with GitHub Issues. Now imagine your team is large: dozens of developers can push to main, and some of them forget to write PR descriptions or put passwords in configuration files. Without agreed-upon security, a single commit can break production or leak company credentials.
This episode covers two sides of repository security: enforcing rules through Branch Protection Rules so code does not randomly enter main, and preventing and handling secret leaks — API keys, private keys, or database credentials — using GitHub Secret Scanning and Dependabot Alerts. At the end, we discuss the emergency steps if a secret has already been committed.
A secret is information that grants access: API keys, tokens, SSH private keys, database usernames and passwords, even cloud credentials. Once a secret enters a commit and is pushed, it is stored forever in Git history — even if the file is deleted in the next commit, the secret can still be found in the history by anyone with repository access.
The danger multiplies in public repositories: attackers and scanner bots constantly scan GitHub for secret patterns. Once caught, cloud accounts, databases, or other services can be controlled by someone else — often within minutes. That is why a secret leak is not a "small problem to clean up later", but an incident that must be taken seriously.
The first and cheapest rule is prevention: never write secrets in files tracked by Git. Use a .env file listed in .gitignore, and inject secret values via environment variables at deployment time.
# .env.local — tidak boleh di-commit
DATABASE_URL=postgres://user:pass@db.example.com:5432/app
API_KEY=sk_live_1234567890abcdefMake sure .gitignore contains the following entries before running git add .:
# .gitignore
.env
.env.local
.env.productionEven when you are careful, humans still make mistakes. GitHub provides two layers of automated defense:
Secret Scanning — scans the repository (and commit history) for secret patterns from thousands of providers: AWS, Google Cloud, GitHub tokens, Stripe, and many more. When found, GitHub raises a warning, and for some providers it immediately notifies the secret owner to revoke it. Enable it at Settings → Code security and analysis → Secret scanning.
Dependabot Alerts — monitors the dependencies used by the repository and warns when there is a CVE (known vulnerability). Dependabot can even create fix Pull Requests automatically through Dependabot security updates.
version: 2
updates:
- package-ecosystem: "npm"
directory: "/"
schedule:
interval: "daily"
open-pull-requests-limit: 5This workflow lives at .github/dependabot.yml. With this simple configuration, Dependabot checks dependencies every day and opens PRs for the latest versions.
Branch Protection Rules are policies that require certain conditions before a commit can be merged into a branch — most commonly applied to main. Configure them via Settings → Branches → Add branch protection rule, then enter a branch name such as main.
The key options you must know:
With this combination, no code enters main without a PR, without green CI, and without verified identity — a quality and security gateway in one.
name: CI
on: [push, pull_request]
jobs:
lint-test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: bun install --frozen-lockfile
- run: bun run lint
- run: bun run testSigned commits can be verified from the terminal:
git verify-commit HEAD
git log --show-signature -3Note
Branch protection is applied per branch with name patterns, so you can create different rules for main (strict), staging (moderate), and feature/* (relaxed). Start with the most important one: main.
Stay calm and follow this order. The most important thing is not deleting history — it is revoking that secret's access as quickly as possible.
Warning
Do not try to "clean up" a secret only with git reset --hard followed by a force push. git reset --hard destroys changes and does not remove the secret from already-pushed history — the secret stays in old commits, in your teammates' clones, and in GitHub's cache. Rotate the secret first, then clean up the history.
The emergency response sequence:
.gitignore.git filter-repo (the safer, faster successor to filter-branch) or BFG.git filter-repo --invert-paths --path .env.local
git remote add origin <URL-repository>
git push --force origin mainNote: git filter-repo removes the remote configuration — that is why the remote is added again afterward.
Tip
Secret Scanning will flag a secret that was ever detected even after it is removed from the file. After rotating and cleaning the history, make sure the secret status at Security → Secret scanning has been revoked — only revocation truly closes the risk.
This episode closes the biggest security gaps of a repository: enforcing rules through Branch Protection Rules with required PRs, status checks, and signed commits; understanding the danger of committed API keys, private keys, and database credentials; preventing leaks with .gitignore; making use of GitHub Secret Scanning and Dependabot Alerts; and handling incidents by rotating the secret before cleaning the history.
The points to take with you:
main requires PRs, green status checks, and signed commits.git filter-repo.git reset --hard destroys changes and does not remove the secret from the remote.Security fixes the future; the next episode fixes the past. In episode 14 we cover Time Travel & Undoing Changes — git restore, git revert, and git reset, and when each is safe to use. See you in episode 14!