Secret Detection
"The fastest way to leak a credential is to commit it. The second fastest is to let someone else commit it."
Overview
Aura performs secret detection on every commit and on every function body it is about to sync to another peer. The scan is regex-driven, runs before any network transmission, and — when configured to do so — aborts the commit entirely. Detected secrets are never logged in cleartext; they are masked at detection time and stored in the intent log only as a hash. This page documents the detection rules, the masking strategies, the .auraignore opt-out mechanism, and how to integrate the scanner with your existing pre-commit pipeline.
Secret detection is not a substitute for a secret manager. The correct home for credentials is an environment-scoped secret store (Vault, AWS Secrets Manager, Doppler, 1Password Connect). Aura's role is to catch the residual failure mode: a credential accidentally pasted into source. In practice this residual is where most breaches originate.
Threat Scope
The detector addresses a specific adversarial chain:
- A developer (or AI agent) writes code that contains a credential — typically during local experimentation, sometimes in a test fixture, occasionally in a config file checked in by mistake.
- The credential reaches a commit without being noticed.
- The commit is synced to peers; from there, if a peer's disk is later compromised or if the peer is a CI machine with broad artifact distribution, the credential propagates.
Aura intervenes at step 2. If the secret is caught before it is committed, the other failure modes do not occur.
The detector does not attempt to defend against deliberate exfiltration — a malicious insider who wants to publish a credential has many channels. It is aimed at mistakes, not adversaries. See the threat model for how this fits the broader design.
Mechanism
Rule classes
The built-in ruleset ships with patterns for credentials that follow structured formats. Each rule has a name, a regex, a masking strategy, and a confidence level. The high-confidence rules (structured formats with checksums or distinctive prefixes) abort the commit by default. Medium-confidence rules (generic high-entropy strings) produce warnings unless explicitly promoted.
| Rule | Pattern summary | Confidence | Default action |
|---|---|---|---|
| aws_access_key_id | AKIA[0-9A-Z]{16} or ASIA[0-9A-Z]{16} | High | Block |
| aws_secret_access_key | 40-char base64 adjacent to an access key ID | High | Block |
| gcp_service_account | JSON with "type": "service_account" and private_key field | High | Block |
| github_pat | ghp_, gho_, ghs_, ghu_, github_pat_ prefixes | High | Block |
| gitlab_pat | glpat-[0-9a-zA-Z_-]{20} | High | Block |
| stripe_live_key | sk_live_[0-9a-zA-Z]{24,} | High | Block |
| stripe_test_key | sk_test_[0-9a-zA-Z]{24,} | Medium | Warn |
| openai_api_key | sk-[A-Za-z0-9]{48,} or sk-proj-... | High | Block |
| anthropic_api_key | sk-ant-[A-Za-z0-9-_]{90,} | High | Block |
| slack_token | xox[baprs]-[0-9A-Za-z-]+ | High | Block |
| jwt | Three base64url segments separated by . | Medium | Warn |
| pem_private_key | -----BEGIN (RSA|OPENSSH|EC|DSA|PGP) PRIVATE KEY----- | High | Block |
| generic_high_entropy | ≥32 chars, Shannon entropy ≥4.5, not a natural-language match | Low | Warn |
| env_assignment | (?i)(password|secret|token|key)\s*=\s*["']?\S{8,} in .env-like files | Medium | Warn |
The ruleset is versioned; aura doctor --rules prints the active version and checksum. Naridon publishes rule updates through the same signed-release channel as the binary (see supply-chain integrity).
Entropy heuristic
For generic detection, Aura computes Shannon entropy over contiguous alphanumeric runs of ≥32 characters. Runs with entropy ≥4.5 bits/symbol are flagged, with a secondary check against a small allowlist of known non-secret high-entropy patterns (BLAKE3 hashes, UUIDs, git commit SHAs). This catches most hand-rolled API keys without excessive noise; the tradeoff is tuned toward false-positive warnings over false-negative silence.
Detection pipeline
The scanner runs in two places:
- Pre-commit. When
aura saveor the underlying pre-commit hook fires, the diff is scanned. A detected high-confidence match aborts the commit. The user receives a report identifying the file, line, rule, and a masked preview. - Pre-sync. When a function body is about to be pushed to a peer via live sync or to the Mothership, it is scanned again. A match blocks that specific node from syncing and emits an alert to the team channel. The commit may proceed locally, but the affected node will not leave the peer.
The two-stage design exists because a commit might contain secrets via files not tracked by the AST (a new .env file), while a sync might carry a secret inside a function body edited long ago that matches a newly added rule.
Masking strategies
Once a match is detected, Aura never stores or transmits the original value. Three masking strategies are available:
| Strategy | Example output | When to use |
|---|---|---|
| full_redact | [REDACTED:aws_access_key_id] | Default for high-confidence rules |
| prefix_suffix | AKIA****************WXYZ | For diff review where identification matters |
| hash_only | blake3:7f2c3a... | For correlation across intents without disclosure |
Masked output appears in diffs, intent log entries, and all UI surfaces. The original value exists only in the developer's working directory until they rotate and remove it.
.auraignore and inline opt-out
Secret detection has false positives. Test fixtures contain deliberately invalid keys; documentation contains illustrative examples; some codebases legitimately embed public keys. Aura supports three escape hatches:
File-level via .auraignore. Same syntax as .gitignore:
# Fixtures contain deliberate test keys
tests/fixtures/**
docs/examples/*.md
# Embedded public certificates are not secrets
infrastructure/certs/public/*.pem
Line-level via inline pragma. A comment immediately above the offending line:
# aura:allow-secret(reason="test fixture, not a live key")
TEST_STRIPE_KEY = "sk_test_4eC39HqLyjWDarjtT1zdp7dc"
Commit-level via intent acknowledgement. A committer can override the block by including a specific marker in the intent text:
aura log-intent "Adding canned response containing test API key. ALLOW_SECRET=stripe_test_key"
This path is audited: the acknowledgement is preserved in the intent log with full context, so the next auditor can confirm the override was deliberate rather than accidental.
Log output format
When a secret is detected and blocked, the report looks like this:
aura: secret detection blocked 1 commit
src/config/billing.py:42
rule: stripe_live_key (high confidence)
preview: STRIPE_KEY = "sk_live_****************abcd"
suggestion: move to secret manager; rotate the exposed key now
To override (not recommended), add ALLOW_SECRET=stripe_live_key to your intent.
The masked preview is exactly what appears in the intent log if the override path is taken, not the cleartext.
Configuration
Global defaults live in aura.toml; per-repository overrides live in the repository root's .aura/config.toml.
[secret_detection]
# "off" | "warn" | "block"
mode = "block"
# Default masking strategy for detected matches
masking = "full_redact"
# Enable the generic entropy heuristic (produces more warnings)
entropy_heuristic = true
# Custom rules defined per repository
[[secret_detection.custom_rules]]
name = "internal_service_token"
pattern = '(?i)internal_svc_[0-9a-f]{32}'
confidence = "high"
action = "block"
masking = "prefix_suffix"
[secret_detection.overrides]
# Promote a medium-confidence rule to blocking
stripe_test_key = "block"
# Demote a high-confidence rule to warn (not recommended)
# jwt = "warn"
To test the scanner without committing:
aura scan --staged
aura scan --path src/
aura scan --diff HEAD~10
These run the same pipeline the hook uses and return a non-zero exit code on block.
Limitations
- Regex detection cannot recognize every secret format. If your organization mints credentials in a proprietary format without distinctive prefix or checksum, you must supply a custom rule.
- Secrets constructed at runtime are invisible.
token = prefix + body + checksumis not matched by a regex against the literal source. Pair Aura's detection with secret-scanning on built artifacts for completeness. - Encoded secrets. A base64-wrapped, PGP-encrypted, or archived credential is, by construction, high-entropy random data. The generic entropy heuristic may catch it or may not, depending on length. Encoding does not make a secret safe to commit.
- Rotation is the only real remediation. If Aura catches a secret on commit, you have avoided propagation, but if the secret ever existed in your working directory on a shared machine you should still rotate it. Detection does not equal containment.
- False positives exist. The entropy heuristic is tuned toward over-detection. Teams that find the noise burdensome should narrow the ruleset rather than disable the system entirely.
- Detection is not rotation. A secret caught on commit is a secret that was, at minimum, present in a developer's shell history, terminal scrollback, IDE recent-files, and possibly their clipboard manager. Treat every caught secret as potentially leaked and rotate it. The value of detection is that you learn about it immediately rather than months later when it appears in a public paste.
- Ruleset drift. Cloud vendors change credential formats. A ruleset written in 2024 may miss a format introduced in 2026. Update the shipped rules regularly; Naridon publishes rule-only updates between binary releases specifically so the detector keeps pace with new credential shapes without requiring a full upgrade cycle.
Gotcha: An allowlisted fixture file is not a free pass. Review
.auraignoreas rigorously as source. An entry likeconfig/**is almost always too broad and will hide real credentials as the repository grows.
See Also
- Threat model — how secret leakage fits the adversary set
- Audit trail — how overrides are recorded
- Aura save — the commit pipeline where detection runs
- Aura push — pre-sync scanning
- Compliance and audit — evidence for auditors
- Supply-chain integrity — where the ruleset updates come from