Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions .git-hooks/pre-commit
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#!/usr/bin/env bash
# pre-commit hook: Gitleaks secret scanning

echo "🔍 Scanning for secrets with Gitleaks..."

# Run gitleaks on staged files
if command -v gitleaks &> /dev/null; then
gitleaks protect --staged --verbose --redact --no-banner 2>&1

if [ $? -eq 1 ]; then
echo ""
echo "❌ Gitleaks found secrets in staged files!"
echo "Fix the issues above or use 'git commit --no-verify' to bypass (not recommended)"
exit 1
fi

echo "✅ No secrets detected"
else
echo "⚠️ Gitleaks not installed, skipping secret scan"
echo "Install with: brew install gitleaks (macOS) or https://github.com/gitleaks/gitleaks"
fi

exit 0
51 changes: 51 additions & 0 deletions .gitleaks.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# Gitleaks configuration for RedAI
# Prevents API keys and secrets from being committed

title = "RedAI Gitleaks Configuration"

# Strict mode: catch ALL potential secrets in documentation files
[allowlist]
description = "Allowlist for safe patterns"
regexes = [
'''your_.*_key_here''',
'''your_.*_token_here''',
'''your_.*_secret_here''',
'''sk_test_''', # Test Stripe keys are OK
'''whsec_test_''', # Test webhook secrets
]

# Custom rules for documentation files
[[rules]]
id = "google-api-key-in-docs"
description = "Google API keys should never be in documentation"
regex = '''AIza[0-9A-Za-z\-_]{35}'''
path = '''(\.md|\.mdx|\.txt|docs/)'''
tags = ["key", "google", "documentation"]

[[rules]]
id = "any-api-key-in-docs"
description = "Generic API keys in documentation must be placeholders"
regex = '''(?i)(api[_-]?key|apikey|api_secret|token)\s*[:=]\s*["']?(?!your_|sk_test_|whsec_test_)[a-zA-Z0-9\-_]{20,}["']?'''
path = '''(\.md|\.mdx|\.txt|docs/)'''
tags = ["key", "documentation"]

[[rules]]
id = "supabase-keys-in-docs"
description = "Supabase keys in documentation"
regex = '''eyJ[A-Za-z0-9_-]{10,}'''
path = '''(\.md|\.mdx|\.txt|docs/)'''
tags = ["jwt", "supabase", "documentation"]

[[rules]]
id = "clerk-keys-in-docs"
description = "Clerk keys in documentation"
regex = '''(sk|pk)_(test|live)_[a-zA-Z0-9]{20,}'''
path = '''(\.md|\.mdx|\.txt|docs/)'''
tags = ["clerk", "documentation"]

[[rules]]
id = "stripe-keys-in-docs"
description = "Stripe keys in documentation (non-test)"
regex = '''(sk|pk)_live_[a-zA-Z0-9]{20,}'''
path = '''(\.md|\.mdx|\.txt|docs/)'''
tags = ["stripe", "documentation"]
124 changes: 124 additions & 0 deletions docs/CREDENTIAL-SAFETY.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
# Credential Safety Guide

## 🚨 Critical Rule: NEVER Commit Real Credentials

All API keys, tokens, and secrets must use placeholders in:
- ✅ Documentation files (`.md`, `.mdx`, `.txt`)
- ✅ Example files (`.example`, `.sample`, `.template`)
- ✅ Configuration files committed to git
- ✅ Scripts and code examples

## How We Got Breached

**November 2025**: Production Google API key was exposed in:
- `docs/doppler/environment-setup.md`
- `docs/doppler/integration-guide.md`
- Public repo: `dev-lifecycle-marketplace`

**Root cause**: Documentation generation scripts read from real `.env` files and inserted live credentials.

## Prevention Measures Now in Place

### 1. Strict Gitleaks Configuration

`.gitleaks.toml` now catches:
- Google API keys in docs: `AIza[0-9A-Za-z\-_]{35}`
- Any API key pattern in docs
- Supabase JWT tokens in docs
- Clerk/Stripe keys in docs

Test it:
```bash
# Check all files
gitleaks detect --verbose

# Check staged files before commit
gitleaks protect --staged
```

### 2. Environment Variable Sanitization Script

**For documentation authors and doc-generating agents:**

```bash
# Source the sanitization script
source scripts/sanitize-env-for-docs.sh

# Use sanitized variables in docs
echo "GOOGLE_API_KEY=$SANITIZED_GOOGLE_API_KEY"
# Output: GOOGLE_API_KEY=your_google_api_key_here
```

**Never use:**
```bash
❌ echo "GOOGLE_API_KEY=$GOOGLE_API_KEY" # Real credential!
```

**Always use:**
```bash
✅ echo "GOOGLE_API_KEY=your_google_api_key_here"
✅ echo "GOOGLE_API_KEY=$SANITIZED_GOOGLE_API_KEY"
```

### 3. Pre-Commit Hook

Git hook at `.git/hooks/pre-commit` runs Gitleaks on every commit.

**If secrets detected:**
```
❌ Gitleaks found secrets in staged files!
Fix the issues above or use 'git commit --no-verify' to bypass (not recommended)
```

**Never bypass the hook** unless you're 100% certain it's a false positive.

### 4. Security Scanning in CI/CD

`.github/workflows/security-scan.yml` runs Gitleaks on every push.

## For AI Agents & Doc Generators

When generating documentation that includes environment variables:

**DO:**
- ✅ Use `scripts/sanitize-env-for-docs.sh` to get safe placeholders
- ✅ Use patterns like `your_*_key_here`
- ✅ Use test keys for Stripe/Clerk: `sk_test_*`, `whsec_test_*`

**DON'T:**
- ❌ Read directly from `.env` files
- ❌ Use `os.getenv()` or `process.env` for documentation
- ❌ Insert any value that looks like a real credential

## Emergency Response Checklist

If you discover exposed credentials:

1. **Immediately invalidate the key** (Google Cloud Console, Stripe Dashboard, etc.)
2. **Find all occurrences**: `gh search code "KEY_VALUE" --owner vanman2024`
3. **Replace with placeholders**: `sed -i 's/REAL_KEY/your_key_here/g' files`
4. **Commit fix to all repos** (public repos first!)
5. **Generate new keys** and update `.env` files (never commit these!)
6. **Review deployment configs** to ensure new keys are deployed

## Testing

Verify the protection works:

```bash
# Try to commit a fake Google API key
echo "GOOGLE_API_KEY=AIzaSyDOTGETBLOCKEDBYGITLEAKS" > test.md
git add test.md
git commit -m "test"
# Should be BLOCKED by gitleaks

# Clean up
rm test.md
git reset HEAD test.md
```

## Resources

- [Gitleaks Documentation](https://github.com/gitleaks/gitleaks)
- [GitHub Secret Scanning](https://docs.github.com/en/code-security/secret-scanning)
- [OWASP Secrets Management](https://cheatsheetseries.owasp.org/cheatsheets/Secrets_Management_Cheat_Sheet.html)
75 changes: 75 additions & 0 deletions scripts/sanitize-env-for-docs.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
#!/usr/bin/env bash
# Script to sanitize environment variables for documentation
#
# Usage:
# source scripts/sanitize-env-for-docs.sh
# echo $SANITIZED_GOOGLE_API_KEY # Output: "your_google_api_key_here"
#
# Or use in doc generation:
# SANITIZED_ENV=$(bash scripts/sanitize-env-for-docs.sh)

set -euo pipefail

# Function to sanitize a key by replacing it with a placeholder
sanitize_key() {
local key_name="$1"
local key_value="${!key_name:-}"
local placeholder="your_${key_name,,}_here"

if [[ -n "$key_value" ]]; then
echo "$placeholder"
else
echo "$placeholder"
fi
}

# Export sanitized versions of all API keys
echo "# Sanitized Environment Variables for Documentation"
echo "# NEVER use real credentials in documentation!"
echo ""

# Google API Keys
if [[ -n "${GOOGLE_API_KEY:-}" ]]; then
echo "export SANITIZED_GOOGLE_API_KEY='your_google_api_key_here'"
fi

if [[ -n "${GEMINI_API_KEY:-}" ]]; then
echo "export SANITIZED_GEMINI_API_KEY='your_gemini_api_key_here'"
fi

# Supabase Keys
if [[ -n "${SUPABASE_ANON_KEY:-}" ]]; then
echo "export SANITIZED_SUPABASE_ANON_KEY='your_supabase_anon_key_here'"
fi

if [[ -n "${SUPABASE_SERVICE_KEY:-}" ]]; then
echo "export SANITIZED_SUPABASE_SERVICE_KEY='your_supabase_service_key_here'"
fi

# Clerk Keys
if [[ -n "${CLERK_SECRET_KEY:-}" ]]; then
echo "export SANITIZED_CLERK_SECRET_KEY='sk_test_your_clerk_secret_key_here'"
fi

# Stripe Keys
if [[ -n "${STRIPE_SECRET_KEY:-}" ]]; then
echo "export SANITIZED_STRIPE_SECRET_KEY='sk_test_your_stripe_secret_key_here'"
fi

# Airtable Keys
if [[ -n "${AIRTABLE_API_KEY:-}" ]]; then
echo "export SANITIZED_AIRTABLE_API_KEY='your_airtable_api_key_here'"
fi

# Generic API Keys
for var in RESEND_API_KEY GROQ_API_KEY MEM0_API_KEY NGROK_AUTHTOKEN UNSPLASH_ACCESS_KEY; do
if [[ -n "${!var:-}" ]]; then
placeholder="your_${var,,}_here"
echo "export SANITIZED_${var}='$placeholder'"
fi
done

echo ""
echo "# Usage in documentation:"
echo "# Instead of: GOOGLE_API_KEY=\$GOOGLE_API_KEY"
echo "# Use: GOOGLE_API_KEY=\$SANITIZED_GOOGLE_API_KEY"
Loading