Skip to main content

🛡️ SecretShield

Your secrets shouldn't end up in your terminal, your logs, or your commit history. SecretShield makes sure they don't — automatically.

PyPI version Python versions PyPI Downloads License: MIT

Install · Demo · Quick start · Features · Docs below


The problem

You've done this. Everyone has.

print("API key:", api_key)          # ...now it's in your terminal history
logger.info("Token: %s", token)     # ...now it's in your log files
API_KEY = "sk-live-abc123..."       # ...now it's about to get committed

One print() left in from debugging. One log line that dumps a config dict. One hardcoded key that slips past code review. That's usually all it takes.

The fix

pip install secretshield
import secretshield

api_key = "sk-example1234567890abcdefFAKEKEY"
print("API key:", api_key)
API key: ********
⚠ secretshield: Potential secret detected and redacted.

No configuration. No code changes. Just pip install and import. The moment you import it, your terminal output, your logs — protected.

Demo

See SecretShield in action:

SecretShield Demo

▶ Watch the full demo on YouTube

Quick start

pip install secretshield

Protect a running script:

import secretshield   # that's it — stdout, stderr, and logging are now protected

Scan an entire project for hardcoded secrets:

secretshield scan .

Set a whole project up in one step (config file + Git hook + CI):

secretshield init

Typing secretshield a lot? Every command also works with the short alias ss — ss scan ., ss init, ss --fix, all identical to their secretshield equivalents.

What SecretShield does

SecretShield isn't just one trick — it's five layers that cover the whole path a secret takes from your keyboard to a place you can't take it back from:

🖥️ Runtime protection Automatically redacts secrets from stdout, stderr, and logging the instant you import secretshield
🔍 Static scanning secretshield scan . finds hardcoded secrets across Python, JS/TS, HTML, YAML, .env, and more
🔧 Auto-Fix scan . --fix interactively moves a hardcoded secret into .env and rewrites your code to use it — safely, and only when it's unambiguous
🪝 Git pre-commit hook install-hook blocks a commit before a secret ever reaches your repo's history
⚙️ GitHub Actions github-action generates a workflow that scans every push and PR automatically

All of it: zero required dependencies, no telemetry, no network calls, nothing sent anywhere. Everything happens locally, in your own process.

Why star this repo

If SecretShield has ever caught something before it hit your terminal or your Git history — that's the whole point of the project working. Starring it costs nothing and helps the next developer who's about to print() an API key by accident actually find this before it's too late.


Installation

pip install secretshield

Requires Python 3.10+. No required third-party runtime dependencies (a tiny tomli backport is pulled in automatically, but only on Python 3.10).

Basic usage

import secretshield

password = "hunter2-example-not-real"
print("Using password:", password)
Using password: ********
⚠ secretshield: Potential secret detected and redacted.

Toggle protection manually if you need to:

import secretshield

secretshield.disable()
secretshield.enable()      # idempotent, safe to call repeatedly
secretshield.is_enabled()

Or use detection/redaction directly, without touching stdout at all:

from secretshield import detect, redact

detect("aws_key=AKIAABCDEFGHIJKLMNOP")
# [Match(start=8, end=28, value='AKIA...', kind='aws_access_key_id')]

redact("aws_key=AKIAABCDEFGHIJKLMNOP")
# ("aws_key=********", True)

CLI reference

(Every command below also works via the shorter ss alias — ss scan . is identical to secretshield scan ..)

Output is colored (red ✗ / green ✓) automatically when running in a real terminal, and plain everywhere else — piped output, CI logs, and --json are never colored, so nothing downstream ever has to deal with stray escape codes. Force it either way with the standard NO_COLOR=1 / FORCE_COLOR=1 environment variables.

secretshield init — set a project up in one step

secretshield init

Detects your project, then interactively offers to create a config file, install the Git hook, and generate the GitHub Actions workflow — all in one pass instead of discovering each command separately.

secretshield scan — find hardcoded secrets

secretshield scan .                       # scan a directory
secretshield scan app.py                  # scan a single file
secretshield scan . --json                # machine-readable, CI-safe
secretshield scan . --fix                 # interactively move secrets to .env
secretshield scan --staged                # would this commit introduce a secret?
secretshield scan --diff HEAD~1           # did my changes introduce a secret?
secretshield scan . --baseline            # adopt SecretShield without fixing everything today

Scans Python, JavaScript/TypeScript, HTML, CSS, Vue, Svelte, JSON, YAML, TOML/INI, .env files, shell scripts, and more — treating each as text and running the same detection engine regardless of language. Automatically skips .git/, node_modules/, .venv/, binary files, and obvious documentation placeholders like your_api_key_here.

SecretShield scan

✗ src/app.js:82
  Potential secret: Bearer token
  Type: token

✓ 143 files scanned
✗ 1 potential secret(s) found

Exit code: 1

--fix — interactively move a secret into .env

secretshield scan . --fix

For each detected secret, SecretShield asks whether to move it into a local .env file:

Secret 1/1 detected
File: app.js
Line: 3
Detected value: ********

Would you like me to move this secret to a local .env file automatically? [y/N]

Confirm, and this:

const API_KEY = "actual-secret-value";

becomes:

const API_KEY = process.env["API_KEY"];

Supported for Auto-Fix: Python, JavaScript, TypeScript (and JSX/TSX). Each language gets a genuinely safe check, not a guess — Python via its own ast module, JS/TS via a deliberately narrow parser built for this purpose. Both reject anything less than a fully unambiguous single assignment: a value inside a dict/object literal, an f-string or template-literal interpolation, a function-call argument, or more than one variable on the same line is reported but left untouched. On TypeScript, the rewritten line gets a ! non-null assertion so it stays type-compatible with any existing type annotation. Other languages are still fully scanned — only the automatic rewrite is limited to these three, per SecretShield's "detect aggressively, modify conservatively" rule. --fix only prompts in a real interactive terminal; it makes no changes in CI or any other non-interactive environment.

secretshield run — protect a script without editing it

secretshield run app.py

secretshield install-hook / uninstall-hook — stop secrets before they're committed

secretshield install-hook

Scans staged content (not your whole working tree) before every commit and blocks it if something looks like a secret. Never destroys an existing pre-commit hook — backs it up and wraps it instead.

secretshield github-action — catch what slips past locally

secretshield github-action

Generates .github/workflows/secretshield.yml, scanning every push and pull request automatically.

Configuration file

Project-wide scan settings live in secretshield.toml (generate one with secretshield init):

[scan]
entropy_threshold = 4.2

[scan.ignore]
paths = ["tests/fixtures/", "docs/examples/"]

[scan.include]
patterns = ["*.py", "*.js"]

[output]
format = "text"

CLI flags always override the file.

Prefer a plain .gitignore-style file instead of editing TOML? Drop a .secretshieldignore in the project root — one glob pattern per line, # comments and blank lines ignored, same idea as .gitignore:

# .secretshieldignore
vendor/
*.min.js
tests/fixtures/

Both secretshield.toml's [scan.ignore] and .secretshieldignore can be used together — their patterns combine.

Runtime configuration

import secretshield

secretshield.configure(
    enabled=True,             # master on/off switch
    redact_with="********",   # placeholder used in place of a secret
    entropy_threshold=4.2,    # bits/char threshold for generic detection
    notify=True,              # print the "potential secret" warning
)

Detection methods

SecretShield combines two strategies:

  1. Known-format pattern matching — AWS keys, GitHub tokens, OpenAI-style keys, Slack tokens, Stripe keys, Google API keys, JWTs, bearer tokens, PEM private-key blocks, and labeled generic secrets (password =, api_key:, etc.)
  2. High-entropy detection — catches random-looking secrets that don't match a known format, used as a conservative supplement (not the primary mechanism) to keep false positives low.

Architecture

secretshield/
├── patterns.py, detector.py, redactor.py   # detection & redaction engine
├── guardian.py                              # stdout/stderr + logging protection
├── config.py, notifications.py               # runtime settings & safe warnings
├── project_config.py, baseline.py, ignorefile.py   # secretshield.toml, --baseline, .secretshieldignore
├── color.py                                          # dependency-free ANSI colors, TTY-aware
├── cli.py                                        # command-line interface
├── autofix/                                        # interactive scan --fix
│   ├── python.py                                       # Python: ast-based safety check
│   └── javascript.py                                   # JS/TS: narrow safety check (v0.6.0)
├── git/                                              # pre-commit hook
└── github/                                             # Actions workflow generation

Testing

pip install -e ".[dev]"
pytest

176+ tests covering detection, redaction, runtime protection, static scanning across languages, Auto-Fix, Git hooks, and CI integration. All secrets used in tests and examples are fake.

Limitations

Be honest about what this is and isn't:

  • Runtime protection covers this Python process's stdout, stderr, and logging — not screenshots, clipboard, arbitrary file writes, other applications, or network traffic.
  • Auto-Fix (--fix) only rewrites Python, and only unambiguous simple assignments (checked via Python's own ast module, not a regex). Anything less certain is reported but left untouched.
  • The pre-commit hook needs secretshield resolvable on PATH at commit time — keep your virtual environment active.

Treat SecretShield as a strong defense-in-depth safety net, not a replacement for proper secret management (vaults, least-privilege credentials, secret scanning in CI, etc.).

Security considerations

No network calls. No telemetry. Nothing sent anywhere — detection and redaction happen entirely locally, in-process.

Contributing

Issues and PRs welcome. Please add tests for new detection patterns or behavior changes, use only fake credentials in tests/examples, and run pytest before opening a PR.

☕ Get me a coffee

If you find this project useful, consider supporting its development through GitHub Sponsors.

License

MIT — see LICENSE.

Release files for secretshield 0.6.0

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

Source distribution (sdist)

Source distribution for secretshield 0.6.0
File Size Uploaded
secretshield-0.6.0.tar.gz 63.3 kB Details

Built distribution (wheel)

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

Total release size: 109.5 kB

Release files / secretshield-0.6.0.tar.gz

Download URL secretshield-0.6.0.tar.gz
Size 63.3 kB
Tags Source
SHA-256 checksum
How to use checksums
918f83c7849633af27a3a2d5264685df4f75e6db47cfa5f821866f296029191b
BLAKE2b-256 checksum
How to use checksums
74e9ff89a276c1abaa7789d6d67d7f2133e6bc0e381701b5ec5c0718ac95536a
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.14.2

Release files / secretshield-0.6.0-py3-none-any.whl

Download URL secretshield-0.6.0-py3-none-any.whl
Size 46.2 kB
Tags Python 3
SHA-256 checksum
How to use checksums
fbde2377690e06148751cd7ff6bebb9705ac5df91d05800c83b1d41068a5360a
BLAKE2b-256 checksum
How to use checksums
3580009bbffdb1a0904ad7b1f136b1b45a42454ee2cd763f4448de8d04bac23a
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.14.2

Release history Release notifications | RSS feed

0.7.0

2 release files

0.6.1

2 release files

This release

0.6.0 This release

2 release files

0.5.2

2 release files

0.5.0

2 release files

0.4.2

2 release files

0.4.1

2 release files

0.4.0

2 release files

0.3.2

2 release files

0.3.1

2 release files

0.3.0

2 release files

0.2.1

2 release files

0.2.0

2 release files

0.1.2

2 release files

0.1.1

2 release files

0.1.0

2 release files

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