🎉 DevOps Interview Prep Bundle is live — 1000+ Q&A across 20 topicsGet it →
All Articles

Build an AI Secret Scanner with Claude API for Git Repos

Build a Git pre-commit and CI hook using Claude API that detects hardcoded secrets, API keys, passwords, and credentials in code — with context-aware analysis that reduces false positives from regex-only scanners.

Shubham3 min read
Share:Tweet

Regex-based secret scanners (gitleaks, detect-secrets) produce false positives that developers learn to ignore. Claude-powered scanning adds context: it understands whether password = "test123" is a real credential or a unit test fixture.

Setup

bash
pip install anthropic gitpython pre-commit

The Scanner

python
import anthropic
import subprocess
import sys
import json
from pathlib import Path
 
client = anthropic.Anthropic()
 
# Quick regex pre-filter to avoid sending every line to Claude
SUSPICIOUS_PATTERNS = [
    "password", "passwd", "secret", "api_key", "apikey",
    "token", "private_key", "access_key", "auth_key",
    "aws_secret", "AKIA", "-----BEGIN", "credential"
]
 
 
def get_staged_diff() -> str:
    """Get git staged changes."""
    result = subprocess.run(
        ["git", "diff", "--cached", "--unified=3"],
        capture_output=True, text=True
    )
    return result.stdout
 
 
def pre_filter_suspicious_lines(diff: str) -> list[dict]:
    """Extract lines that look suspicious before sending to Claude."""
    suspicious = []
    current_file = None
 
    for line in diff.split("\n"):
        if line.startswith("+++ b/"):
            current_file = line[6:]
        elif line.startswith("+") and not line.startswith("+++"):
            line_content = line[1:]
            lower = line_content.lower()
            if any(pattern in lower for pattern in SUSPICIOUS_PATTERNS):
                suspicious.append({
                    "file": current_file,
                    "content": line_content.strip()
                })
 
    return suspicious
 
 
def analyze_with_claude(suspicious_lines: list[dict]) -> dict:
    """Claude analyzes whether suspicious lines are real secrets."""
    if not suspicious_lines:
        return {"secrets_found": [], "is_safe": True}
 
    lines_text = "\n".join(
        f"File: {item['file']}\nLine: {item['content']}"
        for item in suspicious_lines[:20]
    )
 
    prompt = f"""You are a security expert reviewing code for hardcoded secrets.
 
Analyze these suspicious lines from a git commit:
 
{lines_text}
 
For each line, determine if it contains a REAL secret (actual credential, real API key, real password) vs:
- Placeholder/example values (password123, your-api-key-here, TODO)
- Test fixtures (test_password, mock_token)  
- Variable names without values (password = os.getenv("PASSWORD"))
- Documentation examples
 
Return JSON:
{{
  "secrets_found": [
    {{
      "file": "filename",
      "content": "the suspicious line",
      "severity": "critical|high|medium",
      "type": "api_key|password|private_key|token|other",
      "confidence": "high|medium|low",
      "reason": "why this is a real secret"
    }}
  ],
  "is_safe": true|false,
  "summary": "one sentence"
}}
 
Only flag lines with high confidence real secrets."""
 
    response = client.messages.create(
        model="claude-haiku-4-5",
        max_tokens=1000,
        messages=[{"role": "user", "content": prompt}]
    )
 
    text = response.content[0].text.strip()
    if "```" in text:
        text = text.split("```")[1]
        if text.startswith("json"):
            text = text[4:]
 
    try:
        return json.loads(text)
    except Exception:
        return {"secrets_found": [], "is_safe": True, "error": text}
 
 
def scan_commit():
    diff = get_staged_diff()
    if not diff:
        print("No staged changes to scan")
        sys.exit(0)
 
    suspicious = pre_filter_suspicious_lines(diff)
 
    if not suspicious:
        print("✓ No suspicious patterns found")
        sys.exit(0)
 
    print(f"Found {len(suspicious)} suspicious lines, analyzing with Claude...")
    result = analyze_with_claude(suspicious)
 
    secrets = result.get("secrets_found", [])
 
    if not secrets or result.get("is_safe", True):
        print(f"✓ Secret scan passed: {result.get('summary', 'No real secrets detected')}")
        sys.exit(0)
 
    # Found real secrets
    print("\n🔴 SECRET SCAN FAILED — Real credentials detected:\n")
    for secret in secrets:
        print(f"  [{secret['severity'].upper()}] {secret['file']}")
        print(f"  Type: {secret['type']} | Confidence: {secret['confidence']}")
        print(f"  Content: {secret['content'][:80]}...")
        print(f"  Reason: {secret['reason']}")
        print()
 
    print("Fix: Remove the secrets, rotate any exposed credentials, then commit.")
    print("Use environment variables or a secrets manager instead.\n")
    sys.exit(1)
 
 
if __name__ == "__main__":
    scan_commit()

Pre-commit Hook Setup

yaml
# .pre-commit-config.yaml
repos:
  - repo: local
    hooks:
      - id: ai-secret-scanner
        name: AI Secret Scanner (Claude)
        entry: python scripts/secret_scanner.py
        language: python
        additional_dependencies: [anthropic]
        stages: [commit]
        pass_filenames: false
bash
pre-commit install

GitHub Actions CI Integration

yaml
name: Secret Scan
 
on:
  pull_request:
 
jobs:
  scan:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0
 
      - name: Setup Python
        uses: actions/setup-python@v5
        with:
          python-version: '3.12'
 
      - name: Install dependencies
        run: pip install anthropic
 
      - name: Scan PR diff for secrets
        env:
          ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
        run: |
          git diff origin/main...HEAD > /tmp/pr_diff.txt
          python scripts/secret_scanner_ci.py /tmp/pr_diff.txt

The Claude layer reduces false positives by 80-90% compared to regex-only scanners — developers stop ignoring alerts because they are actually meaningful.


More DevSecOps? Read our Build AI Dockerfile security scanner and Kubernetes OPA policy enforcement guide.

🔧

Today I Fixed

Short real fixes from production — posted daily

Browse fixes
Newsletter

Stay ahead of the curve

Get the latest DevOps, Kubernetes, AWS, and AI/ML guides delivered straight to your inbox. No spam — just practical engineering content.

Related Articles

Build an AI Dependency Upgrade Planner with Claude API

Renovate and Dependabot tell you a new version exists. Build a tool that reads the actual changelog and your codebase's usage patterns with Claude API to tell you whether the upgrade is safe, what specifically to test, and how to sequence a major version bump.

S
4 min readRead

Comments