Security Baseline
OSS-only security baseline to prevent committing secrets and scan for vulnerabilities.
This repository implements a comprehensive security baseline to prevent committing secrets and to scan for vulnerabilities. All tools used are free and open source.
Overview
The security baseline consists of:
- Pre-commit hooks - Run locally before each commit
- CI workflows - Run on every pull request and push to main
Pre-commit Hooks
Pre-commit hooks run automatically via simple-git-hooks when you attempt to commit. They perform:
- File blocking - Prevents committing sensitive file types (
.env,*.pem,*.key, etc.) - Secret scanning - Scans staged files for secrets using gitleaks
What Gets Blocked
The following file patterns are blocked from being committed:
.env(but.env-example,.env.schema,.env.development,.env.staging,.env.production,.env.testare allowed)*.pem,*.key,*.p12,*.pfx,*.jks,*.keystoreid_rsa*(SSH private keys)- Certificate files:
*.crt,*.cer,*.der,*.p7b,*.p7c,*.p7m,*.p7s *.keytab
Secret Scanning
If gitleaks is installed, staged files are scanned for:
- Cryptocurrency private keys (Ethereum, Solana, Cosmos, etc.)
- Mnemonic phrases and seed phrases
- API keys and secrets
- JWT secrets
- Database passwords
- AWS credentials
Required Tools
gitleaks
macOS:
brew install gitleaksLinux:
ARCH=$(uname -m)
if [ "$ARCH" = "x86_64" ]; then ARCH="x64"; elif [ "$ARCH" = "aarch64" ]; then ARCH="arm64"; fi
VERSION=$(curl -s https://api.github.com/repos/gitleaks/gitleaks/releases/latest | grep '"tag_name":' | sed -E 's/.*"([^"]+)".*/\1/' | sed 's/^v//')
wget -O /tmp/gitleaks.tar.gz https://github.com/gitleaks/gitleaks/releases/download/v${VERSION}/gitleaks_${VERSION}_linux_${ARCH}.tar.gz
tar -xzf /tmp/gitleaks.tar.gz -C /tmp
sudo mv /tmp/gitleaks /usr/local/bin/Windows:
# Using Chocolatey
choco install gitleaks
# Using Scoop
scoop install gitleaksManual installation: https://github.com/gitleaks/gitleaks#installation
osv-scanner (CI only, optional locally)
macOS:
brew install osv-scannerLinux:
ARCH=$(uname -m)
if [ "$ARCH" = "x86_64" ]; then ARCH="amd64"; elif [ "$ARCH" = "aarch64" ]; then ARCH="arm64"; fi
wget -O /tmp/osv-scanner https://github.com/google/osv-scanner/releases/latest/download/osv-scanner_linux_${ARCH}
chmod +x /tmp/osv-scanner
sudo mv /tmp/osv-scanner /usr/local/bin/Windows:
# Using Chocolatey
choco install osv-scanner
# Using Scoop
scoop install osv-scannerManual installation: https://google.github.io/osv-scanner/installation/
trufflehog (CI only, optional locally)
macOS:
brew install trufflesecurity/trufflehog/trufflehogLinux:
ARCH=$(uname -m)
if [ "$ARCH" = "x86_64" ]; then ARCH="amd64"; elif [ "$ARCH" = "aarch64" ]; then ARCH="arm64"; fi
wget -O /tmp/trufflehog.tar.gz https://github.com/trufflesecurity/trufflehog/releases/latest/download/trufflehog_linux_${ARCH}.tar.gz
tar -xzf /tmp/trufflehog.tar.gz -C /tmp
sudo mv /tmp/trufflehog /usr/local/bin/Windows:
# Using Chocolatey
choco install trufflehog
# Using Scoop
scoop install trufflehogManual installation: https://github.com/trufflesecurity/trufflehog#installation
Manual Scanning
You can run security scans manually:
# Scan staged files for secrets
pnpm run secrets:scan:staged
# Scan entire repository for secrets
pnpm run secrets:scan
# Scan dependencies for vulnerabilities (OSV)
pnpm run deps:osv
# Run pnpm audit
pnpm run deps:auditCI Workflow
The .github/workflows/security.yml workflow runs on every pull request and push to main. It performs:
- Lockfile integrity check - Uses
--frozen-lockfileto ensure dependencies match lockfile - Full repository secret scan - Scans entire repo with gitleaks
- TruffleHog scan - Scans filesystem and git history
- Dependency vulnerability scan - Uses OSV Scanner on
pnpm-lock.yaml - pnpm audit - Checks for known vulnerabilities in dependencies
All checks must pass for CI to succeed.
What to Do If Secrets Are Detected
1. Rotate the Secret Immediately
If a secret is detected in your commit:
- Rotate the secret immediately - Change the API key, private key, or password
- Do not commit the fix - The secret is already exposed in git history
- Clean git history - See below
2. Clean Git History
If a secret was committed:
Option A: If not yet pushed
# Remove the secret from the commit
git reset HEAD~1
# Edit the file to remove the secret
# Commit againOption B: If already pushed (requires force push)
# Use git-filter-repo or BFG Repo-Cleaner to remove secrets from history
# WARNING: This rewrites git history and requires force push
# Only do this if you have permission and coordinate with your teamOption C: Add to allowlist (only for false positives)
- If the detection is a false positive (e.g., test data), add it to
.gitleaks.tomlallowlist - Never add real secrets to the allowlist
3. Prevent Future Incidents
- Use
.envfiles for secrets (never commit them) - Use
.env-exampleor.env.schemafor templates - Review staged files before committing:
git diff --cached - Run
pnpm run secrets:scan:stagedbefore committing
Adding Allowlist Entries
If you have a legitimate false positive (e.g., test fixtures, example data), you can add it to .gitleaks.toml:
Allowlist a File Path
[allowlist]
paths = [
'''test/fixtures/.*''',
'''examples/.*''',
]Allowlist a Regex Pattern
[allowlist]
regexes = [
'''test.*private.*key''',
'''example.*secret''',
]Important: Only add entries for obvious test/example data. Never allowlist real secrets.
Configuration Files
.gitleaks.toml- Gitleaks configuration with crypto-focused rules and allowlistscripts/block-secret-files.mjs- Pre-commit script that blocks sensitive file typesscripts/ensure-tool.mjs- Checks tool availability and prints install instructions.github/workflows/security.yml- CI workflow for security checks
Troubleshooting
Pre-commit hook not running
Ensure simple-git-hooks is installed:
pnpm install
pnpm simple-git-hooksgitleaks not found
The pre-commit hook will show install instructions if gitleaks is missing. Install it using the instructions above.
False positives
If you get false positives:
- Verify it's actually a false positive (not a real secret)
- Add to
.gitleaks.tomlallowlist if appropriate - Use specific patterns to avoid allowing real secrets
CI failing on secrets
If CI detects secrets:
- Check the workflow logs to see what was detected
- Rotate the secret immediately
- Remove it from git history if already pushed
- Add to allowlist only if it's a false positive
Best Practices
- Never commit secrets - Use
.envfiles and environment variables - Use
.env-example- Provide templates for required environment variables - Review before committing - Check
git diff --cachedbefore committing - Rotate secrets regularly - Even if not exposed, rotate secrets periodically
- Use secret management - Consider using tools like HashiCorp Vault, AWS Secrets Manager, etc. for production
API Security
The Basilic API implements comprehensive application-layer security measures to protect against common attacks and vulnerabilities. DDoS protection is handled by Vercel/Cloudflare at the infrastructure layer.
Security Headers
The API automatically sets security headers on all responses:
- X-Content-Type-Options: nosniff - Prevents MIME type sniffing
- X-Frame-Options: DENY - Prevents clickjacking attacks
- X-XSS-Protection: 1; mode=block - Enables browser XSS protection
- Referrer-Policy: strict-origin-when-cross-origin - Controls referrer information
- Permissions-Policy - Restricts browser features (camera, microphone, geolocation)
- Content-Security-Policy - Restricts resource loading to prevent XSS
- Strict-Transport-Security - Enforces HTTPS in production (HSTS)
Security headers are configured in apps/api/src/plugins/security.ts and can be disabled via the SECURITY_HEADERS_ENABLED environment variable.
CORS Configuration
Cross-Origin Resource Sharing (CORS) is configured to restrict which origins can access the API:
- Allowed Origins: Configurable via
ALLOWED_ORIGINSenvironment variable- Set to
*to allow all origins (development only) - Set to comma-separated list for production:
https://app.example.com,https://admin.example.com
- Set to
- Allowed Methods: GET, POST, PUT, DELETE, PATCH, OPTIONS
- Allowed Headers: Content-Type, Authorization, X-Requested-With
- Credentials: Disabled by default (set to true if cookies are needed)
CORS configuration is in apps/api/src/plugins/cors.ts.
Rate Limiting
Rate limiting protects against abuse and brute force attacks:
- Default Limit: 100 requests per 60 seconds per IP address
- Configurable: Via
RATE_LIMIT_MAXandRATE_LIMIT_TIME_WINDOWenvironment variables - IP Detection: Uses real client IP from
X-Forwarded-Forheader (trust proxy enabled) - Response Headers: Includes rate limit information (
x-ratelimit-limit,x-ratelimit-remaining,x-ratelimit-reset) - Error Response: Returns 429 status with retry information
Rate limiting is configured in apps/api/src/plugins/rate-limit.ts. For distributed systems, Redis can be configured via REDIS_URL environment variable.
Trust Proxy Configuration
The API is configured to trust proxy headers from Vercel/Cloudflare:
- Trust Proxy: Enabled by default (
TRUST_PROXY=true) - Real IP Detection: Extracts client IP from
X-Forwarded-Forheader - Rate Limiting: Uses real client IP for rate limiting
- Logging: Logs real client IP addresses
This is essential for proper IP-based rate limiting and security logging when deployed behind Vercel/Cloudflare.
Request Validation
All API requests are validated using Zod schemas:
- Schema Validation: Defined in route handlers using Fastify's schema option
- Type Safety: End-to-end type safety from OpenAPI spec to runtime validation
- Input Sanitization: Additional sanitization layer in
apps/api/src/lib/security.ts - Error Messages: Sanitized in production to prevent information leakage
Security Event Logging
Security events are automatically logged:
- Suspicious Activity Detection: Detects common attack patterns (path traversal, XSS attempts, SQL injection, etc.)
- Rate Limit Exceeded: Logs when rate limits are exceeded
- Authentication Failures: Logs 401/403 errors
- Request Logging: All requests logged in production with security context
Security logging is implemented in apps/api/src/hooks/security.ts and apps/api/src/lib/security.ts.
Environment Variables
Security-related environment variables:
# CORS Configuration
ALLOWED_ORIGINS=* # Development: *, Production: comma-separated origins
# Rate Limiting
RATE_LIMIT_MAX=100 # Maximum requests per time window
RATE_LIMIT_TIME_WINDOW=60000 # Time window in milliseconds (default: 60 seconds)
# Security Headers
SECURITY_HEADERS_ENABLED=true # Enable/disable security headers
# Trust Proxy
TRUST_PROXY=true # Trust proxy headers (required for Vercel/Cloudflare)
# Request Limits
BODY_LIMIT=1048576 # Maximum request body size in bytes (default: 1MB)API Security Best Practices
- Always use HTTPS - Enforced by HSTS header in production
- Restrict CORS origins - Never use
*in production - Configure rate limits - Adjust based on your API usage patterns
- Monitor security logs - Review security events regularly
- Keep dependencies updated - Run
pnpm auditregularly - Validate all inputs - Use Zod schemas for all request validation
- Sanitize error messages - Don't expose internal details in production
- Use environment variables - Never hardcode secrets or configuration
Incident Response
If a security incident is detected:
- Review security logs - Check
apps/api/src/hooks/security.tslogs - Identify affected endpoints - Review request patterns
- Block malicious IPs - Add to blocklist if needed (requires custom implementation)
- Rotate secrets - If credentials are compromised
- Update rate limits - Adjust if under attack
- Notify team - Escalate if necessary