← Back to list

DevSecOps for Git: Shifting Security Left, One Commit at a Time.

A practical, hands-on guide to catching secrets and enforcing security before they ever reach production.

Sandeepkomal · 2026-08-13 18:41 · 17 claps · 4.8 min read
#github #gitleaks #devsecops #devops #rbac-access-control
Open on Medium ↗
Wiki topics: ☁️ · DevOps & Cloud 🔓 · Open Source

DevSecOps for Git: Shifting Security Left, One Commit at a Time.

https://github.com/SandeepKomal

https://github.com/SandeepKomal

A practical, hands-on guide to catching secrets and enforcing security before they ever reach production.

Every leaked API key, every exposed .env file, every "oops, I committed my AWS credentials" incident has one thing in common: it happened at the Git layer, and nobody stopped it in time.

DevSecOps isn’t just a buzzword tacked onto your CI/CD pipeline — it starts at the very first git add. In this post, we'll walk through a layered defense system for Git repositories, from the simplest .gitignore file all the way up to automated secret scanning, branch protection, and dependency management. Think of it as security in depth, applied to your version control workflow.

Why Git Needs Its Own Security Layer

Git repositories are often the softest target in an organization. Secrets get pasted into config files, .env files get committed "just this once," and history — unlike a live server — remembers everything forever. A secret removed in the next commit is still sitting in your Git history, discoverable by anyone with clone access.

The fix isn’t a single tool. It’s a pipeline of checks, each catching what the previous one missed:

  1. Stop tracked files from ever including secrets (.gitignore)
  2. Stop secrets from being committed locally (pre-commit hooks)
  3. Stop secrets from being pushed, even if a hook was bypassed (CI scanning)
  4. Stop unreviewed or unauthorized changes from reaching main (branch protection, RBAC, CODEOWNERS)
  5. Stop vulnerable dependencies from quietly piling up (Dependabot)

Let’s go through each layer.

1. .gitignore — Your First Line of Defense

Purpose

Prevent sensitive files from ever being tracked by Git in the first place.

Common Security Files to Ignore

.env
.env.*
*.pem
*.key
id_rsa
terraform.tfstate
.terraform/
node_modules/
dist/

Demo

bash

echo "AWS_SECRET_ACCESS_KEY=123" > .env
git status

Now add it to .gitignore:

bash

echo ".env" >> .gitignore
git status

✅ The file is no longer tracked.

⚠️ Important caveat: .gitignore only prevents future tracking. It does nothing to protect a secret that's already been committed — that secret is permanently baked into your Git history until it's purged and rotated.

2. Native Git Pre-Commit Hooks (Custom Scripts)

What This Is

A pre-commit hook is a script located at:

.git/hooks/pre-commit

Git executes it automatically before every commit is finalized — giving you a chance to block bad commits locally, before they ever leave your machine.

Exit Codes

CodeResult0Commit allowed≠ 0Commit blocked

Demo — A Minimal Native Secret Detector

bash

cat > .git/hooks/pre-commit << 'EOF'
#!/bin/bash
echo "Running native pre-commit hook..."
if git diff --cached | grep -i "secret"; then
  echo "Secret detected. Commit blocked."
  exit 1
fi
echo "Commit passed security checks."
exit 0
EOF

Make it executable:

bash

chmod +x .git/hooks/pre-commit

Test it:

bash

echo "my_secret=123" > test.txt
git add test.txt
git commit -m "test commit"

❌ Commit blocked.

This is a great learning exercise, but a grep for the word "secret" won't catch real-world credentials like AWS keys, JWTs, or private key blocks. That's where a purpose-built tool comes in.

3. Gitleaks — Blocking Commits (The Right Way)

Replace the Native Hook with Gitleaks

  1. Install pre-commit
  2. Create a .pre-commit-config.yaml file at the root of your repository:

yaml

repos:
  - repo: https://github.com/gitleaks/gitleaks
    rev: v8.24.2
    hooks:
      - id: gitleaks
  1. Keep it current:

bash

pre-commit autoupdate
  1. Install the hook:

bash

pre-commit install

You’re all set — Gitleaks now runs automatically on every commit.

Demo — Block a Secret Commit

bash

echo "AWS_SECRET_ACCESS_KEY=AKIA123456789" > secrets.env
git add secrets.env
git commit -m "adding secrets"

❌ Commit blocked — Gitleaks recognizes the AWS key pattern and stops it cold.

4. Gitleaks — Repository & History Scanning

Pre-commit hooks only protect commits made after the hook is installed. To catch secrets already lurking in your history (or to add custom detection rules), run Gitleaks directly against the repo.

Create a Custom Rules File — custom-rules.toml

toml

[[rules]]
id = "generic-password"
description = "Detect any PASSWORD assignment"
regex = '''(?i)password\s*=\s*["'][^"']+["']'''
tags = ["password", "custom"]

Run the Scan

bash

gitleaks detect --config custom-rules.toml

This scans your entire commit history, not just the current diff — which is exactly where old, “already deleted” secrets tend to hide.

5. Gitleaks in GitHub Actions

Local hooks can be skipped with --no-verify. That's why every serious setup backs them up with a check that runs remotely, on every push and pull request, where developers have no way around it.

yaml

name: gitleaks
on: [pull_request, push, workflow_dispatch]
jobs:
  scan:
    name: gitleaks
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
        with:
          fetch-depth: 0
      - uses: gitleaks/gitleaks-action@v2
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          GITLEAKS_LICENSE: ${{ secrets.GITLEAKS_LICENSE }} # Only required for Organizations, not personal accounts.

This is your safety net: even if someone bypasses their local hook, the CI pipeline still catches the leak before it merges.

6. Branch Protection Rules

Scanning secrets solves one problem. Controlling who can change what, and how solves another. Branch protection rules are where that control lives.

Enforce:

  • No direct pushes to main
  • Required pull requests
  • Required status checks (including your Gitleaks Action!)
  • No force pushes

This turns your main branch from "whatever anyone pushes" into a gate that every change must pass through.

7. RBAC — Least Privilege

Access should match responsibility, not convenience. A basic role model looks like this:

RolePermissionsAdminRepo settingsMaintainerMerge PRsDeveloperPR onlyAuditorRead-only

The principle is simple: nobody should have more access than their role requires.

8. Mandatory Reviews

Automated scanning catches known patterns. Human reviewers catch everything else — logic flaws, risky architecture decisions, and anything a regex was never going to detect.

Best practices:

  • Minimum 1–2 reviewers per pull request
  • Code owners assigned for sensitive paths
  • A dedicated security review for anything touching auth, infrastructure, or CI

9. CODEOWNERS

Pair mandatory reviews with a CODEOWNERS file so the right people are automatically requested as reviewers for sensitive paths:

/.github/        @security-team
/terraform/      @cloud-team

Now a change to your CI workflows or infrastructure code can’t merge without the team that owns that risk signing off.

10. Dependabot — Don’t Forget Your Dependencies

Your own code isn’t the only attack surface — your dependencies are too. Dependabot automatically opens pull requests when a new version (including security patches) is available:

yaml

version: 2
updates:
  - package-ecosystem: "npm"
    directory: "/"
    schedule:
      interval: "weekly"

Set it, forget it, and let outdated, vulnerable packages get flagged before they become the next CVE headline.

Putting It All Together

None of these layers is sufficient on its own — that’s the whole point of “defense in depth.” Here’s how they stack:

LayerStops….gitignoreSensitive files from being trackedPre-commit hooks / GitleaksSecrets from being committed locallyGitleaks history scanSecrets already buried in commit historyGitleaks in GitHub ActionsSecrets bypassing local hooksBranch protectionUnreviewed or unsafe changes reaching mainRBACOver-privileged accessMandatory reviews & CODEOWNERSRisky changes merging without the right eyesDependabotVulnerable dependencies going unnoticed

DevSecOps for Git isn’t about adding friction for its own sake — it’s about making the secure path the default path, so a single mistyped git add . doesn't turn into a security incident.

⭐ If you found this useful, don’t forget to **follow me on GitHub** for more projects and DevOps content! → https://github.com/SandeepKomal

DevSecOps #DevOps #Git #GitHub #GitLeaks #Dependabot #Precommithooks #RBAC #GitIgnore


메타데이터
post_id
9d18297d5ab4
slug
devsecops-for-git-shifting-security-left-one-commit-at-a-time-9d18297d5ab4
url
https://medium.com/@sandeepkomalp/devsecops-for-git-shifting-security-left-one-commit-at-a-time-9d18297d5ab4
canonical_url
https://medium.com/@sandeepkomalp/devsecops-for-git-shifting-security-left-one-commit-at-a-time-9d18297d5ab4
author_url
https://medium.com/@sandeepkomalp
status
ok
fetched_at
2026-08-18 16:37:06