Guard Scanning¶
Use envdrift guard as a last line of defense against plaintext secrets.
What guard checks¶
- Unencrypted
.envfiles missing dotenvx or SOPS markers - Common secret patterns in source and config files
- High-entropy strings (optional)
- Git history for previously committed secrets (optional)
Quick start¶
Run a default scan in the current directory:
Run without external tools:
Run in CI with a strict threshold:
Choosing scanners¶
By default, guard runs the native scanner and gitleaks. You can enable additional scanners with CLI flags:
For maximum detection including password hashes:
For entropy-based and encoded content detection:
For comprehensive multi-target security scanning:
For 140+ secret types with git history support:
You can also enable scanners in envdrift.toml:
[guard]
scanners = ["native", "gitleaks", "trufflehog", "detect-secrets", "kingfisher", "talisman", "trivy", "infisical"]
Scanner comparison¶
| Scanner | Strengths |
|---|---|
| native | Fast, zero dependencies, unencrypted .env detection |
| gitleaks | Great pattern coverage, fast |
| trufflehog | Service-specific tokens (GitHub, Slack, AWS) |
| detect-secrets | 27+ plugin detectors, keyword scanning |
| kingfisher | 700+ rules, password hashes, secret validation |
| talisman | Entropy detection, encoded content, file analysis |
| trivy | Comprehensive multi-target scanning, severity filtering |
| infisical | 140+ secret types, git history, staged changes |
Reporting and CI¶
Generate SARIF output for code scanning systems:
See the CI/CD Integration guide for upload examples.
Configuration¶
Guard configuration lives under [guard]:
[guard]
auto_install = true
include_history = false
check_entropy = true
entropy_threshold = 4.5
fail_on_severity = "high"
ignore_paths = ["tests/**", "*.test.py"]
Handling False Positives¶
No secret scanner is 100% accurate. Envdrift provides a centralized ignore system that works uniformly across ALL scanners (native, gitleaks, trufflehog, detect-secrets, kingfisher, git-secrets, talisman, trivy, and infisical). This ensures you configure ignores once and they apply everywhere.
Inline Ignore Comments (Recommended)¶
Add ignore comments directly in your code. These travel with your code, are visible in pull requests, and are the most maintainable approach:
# Ignore all rules on this line
password = ref(false) # envdrift:ignore
# Ignore a specific rule
SECRET_KEY = "test-key-for-unit-tests" # envdrift:ignore:django-secret-key
# Ignore with a reason (best practice)
API_KEY = "test_fixture" # envdrift:ignore reason="test data, not a real key"
Supported comment styles:
| Language | Syntax |
|---|---|
| Python, Shell, YAML | # envdrift:ignore |
| JavaScript, TypeScript, Go, C | // envdrift:ignore |
| CSS, C-style | /* envdrift:ignore */ |
| JSON | Use TOML config (no comments allowed) |
TOML Configuration for Bulk Ignores¶
For patterns that appear across many files (like translation files or test fixtures),
use the ignore_rules setting:
[guard]
# Global path ignores - skip these paths entirely
ignore_paths = [
"**/tests/**",
"**/fixtures/**",
"**/locales/**",
"**/__mocks__/**",
]
# Rule-specific path ignores - ignore specific rules in specific paths
[guard.ignore_rules]
# FTP password pattern matches "Mot de passe" in French translations
"ftp-password" = ["**/*.json", "**/locales/**"]
# Connection string pattern matches Helm value templates
"connection-string-password" = ["**/helm/**", "**/charts/**"]
# Django secret key in test settings is intentional
"django-secret-key" = ["**/test_settings.py", "**/conftest.py"]
Ignore Priority¶
The ignore system applies in this order:
- Inline comments - Checked first, most specific
- Rule+path ignores - From
[guard.ignore_rules]in TOML - Global path ignores - From
ignore_pathsin TOML
Finding Rule IDs¶
To see which rule triggered a finding, run with --verbose:
Or use JSON output:
Common rule IDs:
| Rule ID | Description |
|---|---|
aws-access-key-id |
AWS access key pattern (AKIA...) |
aws-secret-access-key |
AWS secret key |
github-token |
GitHub personal access token |
django-secret-key |
Django SECRET_KEY setting |
connection-string-password |
Database connection string passwords |
ftp-password |
FTP/SFTP password in JSON/config |
high-entropy-string |
High entropy value (entropy scan) |
unencrypted-env-file |
.env file without encryption markers |
Skipping Clear Files¶
By default, .clear files ARE scanned. This ensures all configuration files
are checked for accidentally included secrets. To skip them:
Note:
skip_clear_filestakes precedence over thepartial_encryption.clear_fileallowlist. When enabled, ALL.clearfiles are skipped entirely--even those declared inpartial_encryption.environments. The allowlist only affects behavior whenskip_clear_files=false, exempting specified files from the "unencrypted-env-file" check while still scanning them for secret patterns.
Option 1: Skip all .clear files globally¶
Or via CLI: envdrift guard --skip-clear
Option 2: Skip specific .clear files using ignore rules¶
Option 3: Use inline ignore comments¶
In your .clear file:
Allowed Clear Files (Partial Encryption)¶
For partial encryption setups, files listed in partial_encryption.environments
with clear_file are automatically excluded from the "unencrypted env file" check
(but still scanned for secret patterns):
[[partial_encryption.environments]]
name = "production"
clear_file = "app/.env.production.clear"
secret_file = "app/.env.production.secret"
combined_file = "app/.env.production"
Precedence Example¶
[guard]
skip_clear_files = true # Takes precedence
[[partial_encryption.environments]]
name = "production"
clear_file = "app/.env.production.clear" # Will NOT be scanned when skip_clear_files=true
secret_file = "app/.env.production.secret"
combined_file = "app/.env.production"
With skip_clear_files = true: app/.env.production.clear is completely skipped from scanning.
With skip_clear_files = false (default): app/.env.production.clear is scanned for patterns but
exempt from the "unencrypted-env-file" check.
Tips¶
--historyrequires a git repository and can be slower on large histories.skip_clear_filesskips.clearfiles entirely (default: false, they ARE scanned).ignore_pathsapplies globally to all scanners.ignore_rulesprovides fine-grained control per rule per path pattern.- Use inline comments for one-off ignores; use TOML for bulk patterns.
- Always provide a
reasonin inline comments for future maintainers. - External scanners can auto-install; disable with
--no-auto-install.