Skip to main content
Version: v1

Secret scanning

A password or API token committed to Git stays in the history after someone deletes the line, so catch it before it merges. This page is for developers and leads on a project built from Vortex. It shows how to run the secret scan locally, scan the Git history, ignore a false positive and read the findings in continuous integration (CI).

Gitleaks scans the code for hardcoded secrets such as passwords, API keys and tokens, and fails the security audit when it finds a secret.

Usage​

Vortex doesn't install Gitleaks locally, so install it on your system first; see the Gitleaks installation instructions. Then run the scan from the project root:

gitleaks dir .

Gitleaks reads .gitleaks.toml from the directory it scans, so run the command from the project root. By default, it prints only the number of findings. Add -v to see each finding. On a clean codebase, the scan ends with no leaks found.

Like CI, gitleaks dir scans the files on disk and skips the Git history. To scan the commits too, see Scan the Git history.

Configuration​

Global configuration lives in the .gitleaks.toml file at the repository root; see the Gitleaks configuration documentation. The file extends the Gitleaks default rule set. It allowlists the following paths, which hold third-party, generated or local-only files:

  • .artifacts/
  • .data/
  • .git/
  • .logs/
  • node_modules/
  • vendor/
  • web/core/
  • web/libraries/
  • web/modules/contrib/
  • web/sites/default/files/
  • web/themes/contrib/

Each pattern matches its directory at any depth. With those paths skipped, the scan covers the code your team writes, so a clean install reports 0 findings while Gitleaks still detects real secrets.

Ignore findings​

To ignore a single line you can edit, add a gitleaks:allow comment to it. Gitleaks skips every finding on a line that contains the comment, so the exception moves with the line:

$settings['example'] = 'not-a-real-secret'; // gitleaks:allow

For other exceptions, use 1 of the following:

  • To ignore a path or a known-safe value that recurs across the codebase, add an allowlist entry to the .gitleaks.toml file.
  • For a line that can't hold a comment, such as a line in a JSON file, use a .gitleaksignore file at the repository root. Add the finding's fingerprint to that file. The fingerprint holds the file, the rule and the line number, so it stops matching when the line moves.

Copy the fingerprint from the Fingerprint: line that gitleaks dir . -v prints for the finding. The file takes 1 fingerprint per line and skips blank lines and lines that start with #:

# Fake token in a Behat fixture.
tests/behat/fixtures/response.json:generic-api-key:3

Confirm that the exception works: run gitleaks dir . -v again and check that the finding no longer appears. When it was the only finding, the scan ends with no leaks found.

Scan the Git history​

The CI scan reads the files in the checkout. A secret committed in 1 commit and deleted in the next passes that scan, but anyone with a clone can still read it from the history. gitleaks git scans the lines each commit added, so it finds those secrets too.

A history scan gives the same answer for the same commits and rules, so CI doesn't run it on every build. Run it yourself at the points in the following sections. Run it from the project root, so Gitleaks reads .gitleaks.toml and .gitleaksignore the same way gitleaks dir does.

Onboarding scan​

Scan the full history once when you add secret scanning to a repository that already has commits, for example when you adopt Vortex on an existing project:

gitleaks git . -v --redact

--redact replaces each matched secret with REDACTED in the output. The rest of the source line still appears, so treat the output as sensitive and don't share it. The output also shows the file, line, commit and fingerprint of each finding.

Gitleaks scans only the commits and branches present in your clone. A clone made with git clone --depth holds part of the history and 1 branch, so clone the repository again without --depth before you scan.

Handle each finding as described in Handle a finding in the history. Then run the scan again and confirm that it ends with no leaks found.

Ad hoc scans​

Scan the history again in these cases:

  • Before you merge a branch where a secret might have been committed and then removed. Check out the branch, then pass a Git revision range to --log-opts to scan only its commits:

    gitleaks git . -v --redact --log-opts="origin/main..HEAD"

    Replace origin/main with your default branch. HEAD is the checked-out branch, so the range is empty when you run the scan from the default branch.

  • Before you make a private repository public. Every commit becomes readable, including commits that exist only in closed pull requests. GitHub keeps those under refs/pull/, so fetch every branch, every tag and those refs before you scan the full history:

    git fetch --tags origin
    git fetch origin "+refs/pull/*:refs/remotes/origin/pull/*"
  • After a Gitleaks upgrade or a rule change in .gitleaks.toml. A new rule can match content in old commits, and only a history scan checks them.

Handle a finding in the history​

A secret in the history stays exposed for as long as the credential works. To close the exposure, rotate the credential:

  1. Revoke the credential with the service that issued it.
  2. Create a new credential.
  3. Update every place that uses the credential.

Don't rewrite the history to remove the secret. A rewrite leaves the secret in existing clones, forks, CI caches and the hosting provider's pull request references. After rotation, the old value in the history no longer grants access.

After you rotate the credential, add the finding's fingerprint to .gitleaksignore as described in Ignore findings, so later history scans pass. A history fingerprint starts with the commit hash, so it matches the finding in that commit only:

# GitHub token revoked and rotated.
06d02de9fdcbeac96844a410462a26f3fc5c9004:web/sites/default/settings.php:github-pat:2

Continuous integration​

Gitleaks runs from its official Docker image in the security audit workflow and fails that workflow when it finds a secret. The installer turns the scan on by default. Answering no at its Scan for committed secrets with Gitleaks? prompt removes the CI steps and the .gitleaks.toml file.

Ignore failures​

Set VORTEX_CI_GITLEAKS_IGNORE_FAILURE to 1 to let the workflow pass anyway. In GitHub Actions, set it as a repository variable. In CircleCI, set it as an environment variable. Gitleaks still runs and reports how many findings it made. In GitHub Actions, the details reach code scanning and the security-findings artifact. In CircleCI, run gitleaks dir . -v locally to see them.

➡️ See Ignore tool failures.

Where findings appear​

In GitHub Actions, the workflow also uploads Gitleaks findings to the repository's Security > Code scanning tab under the gitleaks category. Each finding becomes an alert naming the rule that matched, located at the line it was found on.

➡️ See Code scanning.

Dismiss a finding​

Dismissing an alert in the Security > Code scanning tab records a reason against that finding and removes it from the open list. Dismiss an alert to track an assessment.

Dismissing leaves the scan unchanged. Gitleaks still matches the value and still fails the workflow until you ignore it as described in Ignore findings. That takes 1 of the following:

  • A gitleaks:allow comment on the line
  • An allowlist entry in .gitleaks.toml
  • The finding's fingerprint in .gitleaksignore