This document describes the secret-management and secret-scanning practices for the QuickEx repository.
All sensitive credentials must be stored outside of version control:
| Secret type | Where to store |
|---|---|
| CI/CD tokens, API keys, database URLs | GitHub Secrets (Settings → Secrets and variables → Actions) |
| Local development keys | Local .env file (already gitignored) |
| Cloud provider credentials | Vault / managed secret store |
| Stellar wallet secrets | GitHub Secrets (E2E_WALLET_SECRET, etc.) |
Never commit real secrets to the repository — not even in example files, test fixtures, or documentation.
The repository uses pre-commit to run secret scanning before every commit.
# Install pre-commit (Python tool)
pip install pre-commit
# Install the git hooks
pre-commit install
# (Optional) Run against all files to verify
pre-commit run --all-files| Hook | Purpose |
|---|---|
detect-secrets |
Yelp's pattern-based scanner; uses .secrets.baseline |
gitleaks |
Broad regex-based scanner covering 130+ secret providers |
detect-private-key |
Blocks PEM-encoded private keys |
no-commit-to-branch |
Prevents direct commits to main |
quickex-precommit |
Local rules: forbidden paths, secret shapes, formatting |
The .github/workflows/secret-scanning.yml workflow runs automatically on every push and pull request.
It consists of three jobs:
- detect-secrets – Scans the full repo against the baseline file. Fails if new, un-audited secrets are found.
- gitleaks – Runs the gitleaks scanner for broader pattern coverage.
- env-guard – Blocks any
.envfile (except.env.example) from being committed.
The .secrets.baseline file is a JSON file that records known false-positive matches (e.g., test tokens in .env.example). It is committed to the repo so that CI can verify no new secrets are introduced.
# Re-scan the repo
./scripts/secret-scan.sh
# Interactively mark findings as true/false positives
./scripts/secret-scan.sh --audit
# Commit the updated baseline
git add .secrets.baseline
git commit -m "chore: update secret scanning baseline"The .env.example file serves as a template for developers. It must contain only placeholder values:
# ✅ Good
SUPABASE_ANON_KEY=test-anon-key
STELLAR_SECRET_KEY=your-secret-key-here
# ❌ Bad – real secret
SUPABASE_ANON_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
STELLAR_SECRET_KEY=SBFG...If a secret is accidentally committed:
- Rotate the secret immediately — assume it is compromised.
- Remove it from git history using
git filter-repoor BFG Repo-Cleaner. - Audit access logs for the affected service.
- Open a security incident in the project tracker.
# Run local secret scan
./scripts/secret-scan.sh
# Verify no new secrets
./scripts/secret-scan.sh --verify
# Run pre-commit on all files
pre-commit run --all-filesThe QuickEx hook (scripts/precommit/check.mjs) covers what the generic scanners cannot express.
It is dependency-free, so it runs from the checkout with no install step, and the same check runs in
CI, so a rule that exists only on one developer's machine cannot merge.
It blocks:
- Paths that must never be committed —
.envfiles (.env.exampleexcepted), private keys and keystores,node_modules/,dist/,.turbo/,coverage/, Rusttarget/, and log files. - High-signal secret shapes in any text file — Stellar secret keys, Supabase service-role JWTs, PEM private-key blocks, GitHub PATs, AWS key ids, and Slack tokens. A finding names the kind of secret, never the value: printing a match into a log would copy the secret somewhere it is not protected.
- Whitespace problems in files you are editing: missing final newline, trailing blank line, CRLF endings, and trailing whitespace.
Formatting is enforced as a ratchet: a violation that already exists in the committed file is
reported as pre-existing debt rather than blocking you, but any violation your change introduces
fails. Run node scripts/precommit/check.mjs --all --no-ratchet to see the full backlog.
Reviewed exceptions live in
scripts/precommit/allowlist.txt, one <glob> | <justification>
per line. An entry without a justification fails the test suite, so a suppression cannot be added
quietly. Full documentation: scripts/precommit/README.md.