Q38 of 40 · Git

A QA engineer discovers that an API key was committed to the test repository 6 months ago and is still in Git history. What is your remediation plan?

GitSeniorgitsecuritysecretsfilter-repoincident-response

Short answer

Short answer: Immediate: revoke the key. Then rewrite history with `git filter-repo --path-glob '*.env' --invert-paths` or a text replacement filter. Force-push all branches, require all team members to re-clone, and notify GitHub secret scanning. The key must be rotated even after history rewrite.

Detail

History rewrite cannot be the only step — the key was exposed and must be treated as compromised regardless of whether history is cleaned. Any system that ever cloned the repo, any CI logs, any GitHub/GitLab diff views may have cached it.

Immediate triage:

  1. Revoke/rotate the key immediately — don't wait for history rewrite.
  2. Check access logs for the key in your API provider's dashboard.
  3. Assess blast radius: what does the key have access to?

History rewrite with git filter-repo (the modern successor to the deprecated git filter-branch):

  • Remove a whole file: git filter-repo --path secrets/.env --invert-paths
  • Replace a string everywhere: git filter-repo --replace-text replacements.txt where replacements.txt contains ACTUAL_KEY==>REDACTED

After the rewrite, all commit SHAs in the affected history change, so all branches must be force-pushed and all team members must re-clone (their clones contain the old history with the secret).

Platform cleanup:

  • GitHub: contact support to purge caches. GitHub's secret scanning alerts will persist until acknowledged.
  • GitLab: use the "Remove blob" API endpoint.

Prevention: enable GitHub secret scanning (free for public repos, available on Enterprise). Use git-secrets or gitleaks as pre-commit hooks. Never store real credentials in test fixture files — use environment variables or a secrets manager.

// EXAMPLE

# Step 1: rotate the key (do this FIRST, outside of Git)
# ... revoke in AWS/API provider dashboard ...

# Step 2: remove the file from all history (modern approach)
pip install git-filter-repo
git filter-repo --path config/test.env --invert-paths

# Or: replace just the secret string everywhere in history
cat > replacements.txt << 'EOF'
ACTUAL_API_KEY_VALUE_HERE==>REDACTED_ROTATE_THIS_KEY
EOF
git filter-repo --replace-text replacements.txt

# Step 3: force-push ALL branches (coordinate with team first)
git push --force --all origin
git push --force --tags origin

# Step 4: all teammates must re-clone (old clones still have the secret)
# rm -rf repo && git clone https://github.com/company/repo.git

# Step 5: expire the reflog and GC to remove local dangling objects
git reflog expire --expire=now --all
git gc --prune=now --aggressive

# Prevention: add gitleaks pre-commit hook
cat > .pre-commit-config.yaml << 'EOF'
repos:
  - repo: https://github.com/gitleaks/gitleaks
    rev: v8.18.2
    hooks:
      - id: gitleaks
EOF

// WHAT INTERVIEWERS LOOK FOR

Rotating the key FIRST (before history rewrite). git filter-repo over git filter-branch (deprecated). Force-pushing all branches. Requiring all-team re-clone. GitHub secret scanning and cache purge. Prevention with pre-commit hooks. This is a serious incident response question — calmness and completeness signal seniority.