Ctadel

Secrets detection

What we look for

The pattern library covers six broad categories:

CategoryExamples
Cloud credentialsAWS access keys, GCP service account keys, Azure SAS tokens, Scaleway secret keys
SaaS tokensGitHub PATs, GitLab tokens, Stripe keys, Slack tokens, OpenAI API keys
Database connection stringspostgres://, mysql://, mongodb+srv:// with credentials embedded
Cryptographic materialPEM private keys, SSH private keys, JWT secrets
Generic high-entropyLong base64/hex strings near the words "secret", "token", "key"
Webhook URLsSlack incoming webhooks, Discord webhooks

Each pattern has a confidence level (HIGH, MEDIUM, LOW). Patterns that match a unique vendor format (AWS keys start with AKIA, Stripe with sk_live_) are HIGH; generic high-entropy patterns are LOW.

Where we scan

SourceScope
Object storageSample first N MB of each object, scan for patterns
Block volumesMount snapshot read-only, walk filesystems
Container imagesLayer by layer
DatabasesSampled rows from text columns
IaC reposEvery file, on every push and PR
Serverless functionsSource code + environment variables

You can scope each pattern to specific source types via Settings → Policies → Secrets (e.g. only scan IaC for GitHub PATs, not buckets).

Validation, the differentiator

A finding without validation is just a regex hit. The pattern matched something that looks like an AWS key, but is it real? Is it still active?

For high-confidence patterns, Ctadel optionally calls the vendor's identity endpoint with the candidate secret to determine its validity:

ValidityMeaning
VALIDThe secret was successfully used to authenticate. Treat as a live breach.
INVALIDThe vendor rejected the secret. Probably a leftover or rotated value.
UNKNOWNWe can't validate (no vendor endpoint, or validation disabled). Triage manually.

Validation is read-only and uses the least-privileged introspection endpoint each vendor exposes (e.g. AWS STS GetCallerIdentity, GitHub /user).

You can disable validation per pattern if your security policy forbids it.

What ends up in a Secret finding

FieldExample
Rule keyaws_access_key
Resource IDThe bucket / volume / image / repo holding the secret
File paths3://prod-data-eu/legacy/config.yml
Snippetaws_access_key_id: AKIA****PROD (redacted)
ValidityVALID / INVALID / UNKNOWN
Risk scoreComposite of severity × validity × exposure (0–100)

The full secret value is never stored in Ctadel. Only a hash and a redacted snippet.

What's next