Skip to content

Leaked secrets ​

dagsec reads every line added in every commit of the repository's history and matches it against the rules below. A secret that was committed and later deleted is still found, because it stays in git history and anyone with the repository can read it.

Rules ​

Rule IDFindsPattern
aws-access-key-idAWS access key IDAKIA or ASIA followed by 16 uppercase letters or digits
github-tokenGitHub tokenghp_, gho_, ghu_, ghs_ or ghr_ followed by 36 characters
github-fine-grained-patGitHub fine-grained personal access tokengithub_pat_ followed by 82 characters
slack-tokenSlack tokenxoxb-, xoxa-, xoxp-, xoxr- or xoxs- followed by at least 10 characters
stripe-live-keyStripe live secret or restricted keysk_live_ or rk_live_ followed by at least 24 characters
google-api-keyGoogle API keyAIza followed by 35 characters
private-keyPrivate key blockA -----BEGIN ... PRIVATE KEY----- header (RSA, EC, DSA, OPENSSH, PGP or plain)
generic-secretHigh-entropy value assigned to a secret-like nameA quoted value of 16+ characters assigned to a name containing secret, token, password, passwd, api_key or access_key, with Shannon entropy of at least 3.5

Values that contain EXAMPLE, PLACEHOLDER, DUMMY, CHANGEME or REDACTED (in any case) are treated as documentation placeholders and skipped, such as AWS's AKIAIOSFODNN7EXAMPLE. Low-entropy values such as password = "changemechangeme" don't match the generic rule.

Where a secret is ​

Every finding gets a location from its file path. Only secrets in code fail a scan; the others are listed in a collapsed section, because they are usually test fixtures.

LocationThe path
testis in a directory named test, tests, __tests__, spec, specs, testdata, fixtures, __fixtures__, __mocks__ or mocks; or the file name starts with test_, ends with _test, or contains .test., .spec. or _spec.
exampleis in example, examples, sample, samples, demo or demos; or the file ends with .example, .sample, .template or .dist
docsis in doc, docs or documentation; or the file ends with .md, .mdx, .rst, .adoc or .ipynb
codeanything else, including .env and config files

Checks are made in that order and names are compared case-insensitively. For example, tests/test_api.py is test, .env.example is example, README.md is docs, and config/deploy.env is code.

A fixture can still be real

If a "test" secret is a real credential, rotate it anyway. The location only decides whether the check fails.

Masked values ​

Reports never contain the full secret. They show its first four characters followed by asterisks, such as AKIA******** or ghp_********. For private-key, the report shows the key type from the header (for example BEGIN RSA PRIVATE KEY), which is not secret.

The full value exists only in memory while the scan runs. It is never written to a report, the dagsec database or logs.

Is it still active? ​

For GitHub, Stripe and Slack credentials, dagsec asks the provider whether the credential still works, with a read-only call that changes nothing:

RuleCallActive whenRevoked when
github-token, github-fine-grained-patGET https://api.github.com/rate_limit2xx or 403401
stripe-live-keyGET https://api.stripe.com/v1/balance2xx or 403 (restricted key)401
slack-tokenPOST https://slack.com/api/auth.test"ok": trueinvalid_auth, token_revoked, account_inactive, token_expired, not_authed

The report marks each checked finding ACTIVE or (revoked), and the summary line says how many are still active. Anything else (a timeout, a rate limit, another rule) leaves the finding unchecked. At most 25 distinct credentials are checked per scan, with a 10-second timeout each.

When checks run. Only where the repository's owner has authorized the scan:

  • In your own CI and on the command line: on by default. Turn off with --no-verify.
  • In GitHub App scans: on, because the repository's owner installed the App.
  • In dashboard scans: never, because anyone can scan any public repository there.

AWS access key IDs can't be checked without the secret half of the key pair; Google API keys and private keys have no neutral check.

Fingerprints and false positives ​

Each finding has a stable fingerprint:

<rule id>:<first 12 characters of the commit>:<path>:<line>

for example aws-access-key-id:3f2a9c1d7e04:config/deploy.env:1. To accept a finding, add its fingerprint to .dagsecignore on the default branch. The dashboard has a Copy ignore line button on each finding.

What to do about a real secret ​

  1. Revoke or rotate the credential at its provider first. Deleting it from the code doesn't remove it from git history, and history is copied to every clone and fork.
  2. Remove it from the code and load it from your CI's secret store or environment instead.
  3. Rewriting history (git filter-repo) is optional once the credential is revoked, and doesn't reach existing clones.