Loading...
Loading...
Secret Protection prevents credential leaks across the development lifecycle through `.env` scanning, pre-commit hooks, secret rotation policies, and runtime detection.
npx skill4agent add itallstartedwithaidea/agent-skills secret-protection.envgraph TD
A[Developer / Agent Writes Code] --> B[Ring 1: Pre-Commit Hook]
B --> C{Secrets Detected?}
C -->|Yes| D[Block Commit + Alert]
C -->|No| E[Allow Commit]
E --> F[Ring 2: CI/CD Scan]
F --> G{Secrets in Diff?}
G -->|Yes| H[Fail Pipeline + Alert]
G -->|No| I[Continue Pipeline]
I --> J[Ring 3: Runtime Detection]
J --> K[Redact Secrets in Logs]
J --> L[Redact Secrets in Agent Output]
K --> M[Production Safe]
L --> M
N[Rotation Schedule] --> O[Rotate Keys Monthly]
O --> P[Update Secrets Manager]
P --> Q[Deploy Updated References]# .pre-commit-config.yaml
repos:
- repo: https://github.com/gitleaks/gitleaks
rev: v8.18.0
hooks:
- id: gitleaks
- repo: local
hooks:
- id: dotenv-check
name: Check .env files not committed
entry: bash -c 'git diff --cached --name-only | grep -E "\.env($|\.)" && exit 1 || exit 0'
language: system
pass_filenames: falseimport re
from dataclasses import dataclass
@dataclass
class SecretPattern:
name: str
pattern: str
severity: str
SECRET_PATTERNS = [
SecretPattern("AWS Access Key", r"AKIA[0-9A-Z]{16}", "CRITICAL"),
SecretPattern("AWS Secret Key", r"(?i)aws_secret_access_key\s*=\s*[A-Za-z0-9/+=]{40}", "CRITICAL"),
SecretPattern("GitHub Token", r"gh[ps]_[A-Za-z0-9_]{36,}", "CRITICAL"),
SecretPattern("Generic API Key", r"(?i)(api[_-]?key|apikey)\s*[:=]\s*['\"][A-Za-z0-9]{20,}['\"]", "HIGH"),
SecretPattern("Private Key", r"-----BEGIN (?:RSA |EC |DSA )?PRIVATE KEY-----", "CRITICAL"),
SecretPattern("Cloudflare Token", r"(?i)cloudflare.*(?:token|key)\s*[:=]\s*['\"][A-Za-z0-9_-]{40,}['\"]", "CRITICAL"),
SecretPattern("JWT Token", r"eyJ[A-Za-z0-9_-]{10,}\.eyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}", "HIGH"),
SecretPattern("Database URL", r"(?:postgres|mysql|mongodb)://\w+:[^@\s]+@", "CRITICAL"),
]
class SecretScanner:
def __init__(self, patterns: list[SecretPattern] = SECRET_PATTERNS):
self.patterns = [(p, re.compile(p.pattern)) for p in patterns]
def scan_text(self, text: str, source: str = "unknown") -> list[dict]:
findings = []
for pattern, compiled in self.patterns:
for match in compiled.finditer(text):
findings.append({
"type": pattern.name,
"severity": pattern.severity,
"source": source,
"position": match.start(),
"preview": self._redact(match.group(), keep_chars=4),
})
return findings
def redact_output(self, text: str) -> str:
"""Redact any detected secrets from agent output."""
for pattern, compiled in self.patterns:
text = compiled.sub(f"[REDACTED:{pattern.name}]", text)
return text
@staticmethod
def _redact(secret: str, keep_chars: int = 4) -> str:
if len(secret) <= keep_chars * 2:
return "*" * len(secret)
return secret[:keep_chars] + "*" * (len(secret) - keep_chars * 2) + secret[-keep_chars:]
class SecretRotationPolicy:
ROTATION_SCHEDULES = {
"CRITICAL": 30,
"HIGH": 90,
"MEDIUM": 180,
}
def check_rotation_due(self, secret_metadata: dict) -> bool:
from datetime import datetime, timedelta
last_rotated = datetime.fromisoformat(secret_metadata["last_rotated"])
max_age = self.ROTATION_SCHEDULES[secret_metadata["severity"]]
return datetime.now() - last_rotated > timedelta(days=max_age)gitleaks detect| Platform | Support | Notes |
|---|---|---|
| Cursor | Full | Pre-commit + scanning |
| VS Code | Full | gitleaks extension |
| Windsurf | Full | Secret detection |
| Claude Code | Full | Pre-commit integration |
| Cline | Full | Security scanning |
| aider | Full | Pre-commit hook support |
secret-protectioncredential-leakpre-commitgitleakssecret-rotationredaction.env-scanningapi-key-security