Skip to main content

PyPI CI License

Credactor

Find the secret. Fix it. Commit clean.

Most secret scanners stop at the alert: you get a list of leaked credentials and the cleanup is yours. Credactor finds hardcoded secrets and rewrites them in place, so a leak in your working tree goes from found to fixed in one command. It runs on your machine, as a pre-commit hook, or as a CI gate, and it can also fix what Gitleaks, TruffleHog or Betterleaks find.

Credactor: scan, redact, commit clean
# Before
db_password = "h8Tq2vKp9mRz4Wd"

# credactor --fix-all .   (default: a sentinel that fails loudly at runtime)
db_password = "REDACTED_BY_CREDACTOR"

# credactor --replace-with env --fix-all .   (read it from the environment)
db_password = os.environ["DB_PASSWORD"]

Redaction cleans your working tree. It does not revoke the credential and does not touch git history. If a secret was ever committed or shared, rotate it, and rewrite history if you need it gone from there too (for example with git filter-repo).

Why Credactor

  • It fixes, not just flags. Each finding is replaced in place with a sentinel, your own marker, or an environment-variable lookup in the file's own language.
  • Careful writes. Each edit is planned in memory, backed up first (mode 0600), written to a temp file and renamed into place. A file that changed during the run, a path that leaves the scan root, or a private key block is skipped with a warning, and its finding stays reported.
  • Works with the scanner you already run. Feed it a Gitleaks, TruffleHog or Betterleaks report and it fixes their findings and its own in one deduplicated pass.
  • Built for pipelines. A read-only --ci gate with clear exit codes, SARIF 2.1.0 with line and column locations for GitHub Code Scanning, a GitHub Action and a pre-commit hook. Secrets are masked in every report format, and text output is cleaned so a scanned file cannot inject escape sequences or CI workflow commands into your terminal or log.
  • Releases checked against their source. Pure Python 3.11+ standard library with no runtime dependencies. Before a release reaches PyPI, every Credactor file in the wheel and sdist is compared byte for byte with the tagged source, and the package metadata must match pyproject.toml (see Supply-chain hardening).

Where it is not the right tool on its own: its built-in rules cover the credential formats that leak most often rather than hundreds of providers, and it does not check whether a secret is live. A clean run means nothing matched its rules, not that the code holds no secrets. Pair it with a dedicated scanner for breadth (see Bring your own scanner).

Install

pip install credactor

With uv, install it as a tool, or run it once without installing:

uv tool install credactor
uvx credactor --dry-run .

Or with Homebrew on macOS and Linux:

brew install rxb06/tap/credactor

Needs Python 3.11 or newer. Tested in CI on Linux and Windows, Python 3.11 to 3.13. For files in encodings other than UTF-8, Latin-1 or simple UTF-16, add the optional extra: pip install 'credactor[encoding]' or uv tool install 'credactor[encoding]' (the Homebrew formula includes it).

Quick start

1. See what it finds. Nothing is changed.

credactor --dry-run .
======================================================================
  CREDENTIAL SCAN REPORT  --  2 finding(s) in 1 file(s)
======================================================================

  FILE: config.py
  ────────────────────────────────────────────────────────────
  Line    3  [HIGH]  [variable:db_password]
           db_password = "h8Tq[REDACTED]"
  Line    4  [CRITICAL]  [pattern:AWS access key]
           aws_key = "AKIA[REDACTED]"

Reports show only the first four characters of each secret.

2. Fix them. Review the findings first: --fix-all rewrites every one, false positives included. To keep one, add # credactor:ignore to its line (see Suppress false positives) or fix interactively and answer n. Before each file is changed, a .bak copy is written beside it so you can undo.

credactor .                          # ask y/N for each finding (needs a terminal)
credactor --fix-all .                # fix all after one confirmation
credactor --fix-all --yes .          # fix all, no prompt (scripts)
credactor --replace-with env --fix-all .   # environment lookups instead of the sentinel

Env mode writes the lookup, not the import: add import os or your language's equivalent if the file lacks it. On Windows Git Bash, run the interactive mode as winpty credactor ..

3. Check, then clean up the backups. Each .bak still holds the original secret, and Credactor never edits your .gitignore, so a plain git add . would stage it.

diff config.py.bak config.py         # check what changed
cp config.py.bak config.py           # undo

Add *.bak and *.credactor.tmp to .gitignore and delete the backups once you are happy. To keep backups out of the tree from the start, pass --secure-backup-dir DIR (a directory outside the repository) on the fix run. --secure-delete wipes each backup instead, which leaves git as your only undo.

Commands at a glance

Command What it does Changes files
credactor --dry-run . Report findings No
credactor . Review and fix one finding at a time Yes, on y
credactor --fix-all . Fix every finding after one confirmation Yes
credactor --ci . Gate: exit 1 on findings Never
credactor --staged --ci . Gate only what is staged for commit Never
credactor --scan-history . Scan the last 100 file-changing commits Never
credactor --ci -f sarif . > results.sarif SARIF 2.1.0 for Code Scanning Never
credactor --ci -f json . > findings.json Machine-readable report Never

Every flag and combination is in the Manual.

Use it in pre-commit and CI

Pre-commit hook

# .pre-commit-config.yaml
repos:
  - repo: https://github.com/rxb06/credactor
    rev: v2.7.5
    hooks:
      - id: credactor
pre-commit install

Every git commit then scans the staged content and is blocked if it holds a credential. The hook never rewrites files. It needs pre-commit 3.2.0 or newer; without the framework, see the standalone hook.

GitHub Action

steps:
  - uses: actions/checkout@v7
  - uses: rxb06/credactor@v2.7.5

The Action always runs with --ci, so it never rewrites the checkout. Findings fail the step, and so does any error. To report to Code Scanning instead of failing:

permissions:
  contents: read
  security-events: write

steps:
  - uses: actions/checkout@v7
  - uses: rxb06/credactor@v2.7.5
    with:
      format: sarif
      upload-sarif: true
      fail-on-findings: false

The CI integration guide lists every Action input and output and covers GitLab CI and plain shell steps.

Bring your own scanner

Already running Gitleaks, TruffleHog or Betterleaks? Hand Credactor the report and it fixes their findings together with its own, deduplicated (on a duplicate, the higher severity is kept). Preview with --dry-run first, as for a native scan.

gitleaks dir . -f json -r gl.json
credactor --from-gitleaks gl.json --fix-all --yes .

trufflehog filesystem . --no-verification --json > th.json
credactor --from-trufflehog th.json --fix-all --yes .

betterleaks dir . -f json -r bl.json
credactor --from-betterleaks bl.json --fix-all --yes .

Point Credactor at the same directory the scanner ran on, and regenerate the report after the tree changes. A report holds every secret it found in plaintext and is not scanned itself (.json needs --scan-json), so write it outside the repository or delete it after the run. A finding Credactor cannot place safely (no valid line number, a path under .git, a value too short to match on its own) stays reported and is never written. Details and supported versions are in the Manual.

What it detects

Category Examples Severity
Cloud and SaaS keys AWS (AKIA…, ASIA…), GCP (AIza…), Stripe live (sk_live_…, rk_live_…), Slack (xoxb-…, xoxp-…) Critical
Platform tokens GitHub (ghp_, gho_, ghs_, github_pat_), GitLab (glpat-), npm (npm_), PyPI (pypi-) Critical
Private keys PEM blocks (-----BEGIN … PRIVATE KEY-----, encrypted ones included), PGP private key blocks Critical
JWTs eyJ… three-segment tokens High
Connection strings URLs with inline credentials (scheme://user:pass@host) High
Credential variables password = "…", api_key = "…", secret_key = "…" High, Medium or Low
XML attributes <add key="Password" value="…" /> High, Medium or Low
Stripe test keys sk_test_… Medium
High-entropy strings quoted hex (32 to 64 characters) or Base64 (60 or more) Medium or Low

Provider tokens are matched by their format, so they are found even in comments and whatever their randomness. Heuristic matches must also look random enough, and a bare hex or Base64 value is reported only when it is quoted or assigned to a credential-named variable, which spares git SHAs and checksums. Placeholders such as changeme, environment lookups and template references are not flagged.

Good to know:

  • Private keys are reported, never rewritten. Rotate the key and delete the block by hand; --fix-all counts it as failed.
  • History is read-only and capped. --scan-history covers the 100 most recent file-changing commits, and warns when the repository has more than 100 commits.
  • A fixed set of file types is scanned: 29 code, config and text extensions plus .env.* and SSH key files (id_rsa and friends). JSON needs --scan-json, and anything else needs extra_extensions in .credactor.toml. A file you name directly is always scanned.
  • Gitignored files are skipped when walking a directory, and that usually includes a local .env. The text report lists them, but -f json and -f sarif say nothing about them. To check one, name it directly: credactor --dry-run .env.
  • Some formats are out of scope natively, for example SendGrid and Twilio keys and Slack webhooks, as are secrets built at runtime and values under 8 characters.

The full rules are in the Manual, and the threat model is in the Security doc.

How it rewrites

--replace-with Result Use it when
sentinel (default) "REDACTED_BY_CREDACTOR" You want anything still using the secret to fail loudly
env os.environ["DB_PASSWORD"] The value should come from the environment
custom "<your marker>" via --replacement You have your own convention

Env mode writes the lookup in the file's language: os.environ[…] in Python, process.env[…] in JavaScript and TypeScript, ENV[…] in Ruby, and the native form in Go, Java, Kotlin and PHP. Every other file type gets a ${VAR} placeholder, which is a real lookup only where something expands it (a shell script, a .env loader, many config tools). In C# it stays a plain string, and in Terraform it reads as a reference that does not exist, so the config fails to validate; use the sentinel for both. The name comes from the finding: db_password becomes DB_PASSWORD, and a known token type gets its own name, so an AWS key becomes AWS_ACCESS_KEY whatever the variable was called. Where a lookup would break the code (inside a larger string, say), the sentinel is used instead.

Suppress false positives

On the line itself, in a #, //, /* */ or <!-- --> comment:

example_key = "AKIAIOSFODNN7EXAMPLE"  # credactor:ignore

Or in a .credactorignore at the scan root:

tests/fixtures/*
app/settings.py:42

An entry can be a glob, a file:line, or an exact value. A value that contains ., /, ? or * (a URL, a JWT, Base64) must be written as value:<literal>, or it is read as a glob and has no effect. Findings from an ingested report are filtered by .credactorignore only: an inline credactor:ignore does not stop them. Thresholds, skipped directories and extra file types go in a .credactor.toml.

Exit codes

Code Meaning
0 No findings, or all of them fixed
1 Findings left unresolved
2 Error, such as a bad path, an invalid flag combination, an unreadable report or explicit --config, or --staged / --scan-history outside a git repository

An unresolved finding never exits 0. A file Credactor could not read is only a warning by default, so add --fail-on-error to a gate that must read every file; it then exits 2. The full list is in the Manual.

Supply-chain hardening

A tool that reads every secret in your repository should be safe to install, not only safe to run. Several popular packages have shipped malicious releases after an attacker got into their build or publishing pipeline: a tampered build dependency, a stolen upload token, or a file slipped into the release archive. Credactor's release pipeline has a specific check for each of those.

  • No runtime dependencies. pip install credactor installs Credactor and nothing else (the optional [encoding] extra adds charset-normalizer), so there is no third-party code to vet at install time.
  • Every release is checked against its source. In CI and again before publishing, scripts/audit_wheel.py compares every file in the wheel and sdist that comes from the repository byte for byte (sha256) with the committed source, and rejects anything else apart from the package metadata, which must match pyproject.toml. An extra or altered module, an injected dependency, a repointed command, or a file that is in the tarball but not in git fails the release.
  • Hash-pinned builds. CI and release builds install their tools with pip install --require-hashes, build backend included, so a tampered dependency fails the build instead of reaching the package.
  • No upload token in the pipeline. Releases go to PyPI through OIDC trusted publishing from a dedicated pypi environment, with Sigstore attestations, and only when the package version matches the release tag. The pipeline stores no long-lived PyPI token.
  • Locked-down CI. Every GitHub Action is pinned to a commit SHA, workflow tokens default to read-only, checkouts keep no credentials, and Dependabot proposes updates monthly.
  • The Action installs outside your checkout. The GitHub Action installs Credactor from the runner's temp directory with pip in isolated mode (python -I), then runs the installed command.

These checks show that what is published matches the source committed at the release tag. They cannot make that source trustworthy or protect a build machine that is already compromised; the Security doc has the full detail and limits.

Security

Report a vulnerability privately through GitHub Security Advisories, not a public issue. SECURITY.md lists the supported versions, and the Security doc covers the threat model, each hardening measure and the known limits.

Docs

Document What is in it
Manual Every flag, mode and combination, with verified behaviour
Setup Installation and .credactor.toml
CI integration Pre-commit, the GitHub Action, other CI systems, ingestion
Examples Common workflows with output
Security Threat model, hardening, known limits
Disclaimer Safe usage and warranty
Changelog Version history
Contributing Development setup and pull requests

Licence

Apache 2.0. See LICENSE.

Metadata

Release files for credactor 2.7.5

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for credactor 2.7.5
File Size Uploaded Provenance
credactor-2.7.5.tar.gz 113.7 kB With provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for credactor 2.7.5
File Interpreter ABI Platform
credactor-2.7.5-py3-none-any.whl Python 3 none any Details

Total release size: 227.1 kB

Release files / credactor-2.7.5.tar.gz

Download URL credactor-2.7.5.tar.gz
Size 113.7 kB
Tags Source
SHA-256 checksum
How to use checksums
16b13bdc442aac62b7eb1648817d1f95a750d7ef35b91397e0c8db37cc534ba7
BLAKE2b-256 checksum
How to use checksums
e949eb4eaecdaab99d5c1653ff7d96e9ac20d43e628493a0f41dca8e8952c2b0
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 22, 2026.

Transparency log

Release files / credactor-2.7.5-py3-none-any.whl

Download URL credactor-2.7.5-py3-none-any.whl
Size 113.4 kB
Tags Python 3
SHA-256 checksum
How to use checksums
55c2c90e7fa1aab853290cbfcec1f3da444dfdddfec6786aa2704e37658ccb3e
BLAKE2b-256 checksum
How to use checksums
6aab3209902c4d649f1ca89e1a020138e9132865339676520e0158f8d7080e9a
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 22, 2026.

Transparency log

Security for credactor 2.7.5

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for credactor-2.7.5.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 22, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page