High-throughput secret redaction for Python, backed by Rust.
Project description
ScrubbeRS
ScrubbeRS is a Rust-first, zero-copy (in-place) redaction engine with:
- A stdin → stdout CLI for shell pipelines.
- A Rust library API.
- Optional Python and Node.js bindings.
- Built-in high-confidence detector signatures for direct redaction.
- Optional
.scrubsignature files for custom org-specific patterns.
Why this is fast
- Redaction happens in place (
&mut [u8]) with byte-mask filling. - Literal signatures are matched with Aho-Corasick (single-pass multi-pattern automaton).
- Regex signatures use compiled
regex::bytes::Regexand run against raw bytes. - Release profile is tuned (
lto=fat,codegen-units=1,panic=abort).
CLI usage
# Build release binary
cargo build --release
# Pipe mode (stdin -> stdout)
cat app.log | ./target/release/scrubbers > redacted.log
# With custom signatures
cat app.log | ./target/release/scrubbers --scrub-file .scrub > redacted.log
# Custom mask byte
cat app.log | ./target/release/scrubbers --mask "#" > redacted.log
# Line-oriented streaming mode for log pipelines
tail -F app.log | ./target/release/scrubbers --stream-lines
.scrub format
Each non-empty, non-comment line is either:
name=regex_or_literalregex_or_literal(auto-named)
Example:
# redact internal session tokens
session_token=sess_[A-Za-z0-9]{32}
# redact literal phrase
MY_INTERNAL_SECRET_PREFIX
Rust API
use std::io::Cursor;
use scrubbers::Scrubber;
let scrubber = Scrubber::new()?;
let mut bytes = b"ghp_123456789012345678901234567890123456".to_vec();
scrubber.scrub_in_place(&mut bytes);
let mut output = Vec::new();
scrubber.scrub_lines(
Cursor::new(b"safe\nprefix ghp_123456789012345678901234567890123456 suffix\n"),
&mut output,
)?;
Python bindings
Install from PyPI once published:
uv add scrubbers
Build Python distributions locally with uv:
uv build
On Linux, a local uv build wheel is useful for smoke testing but is not guaranteed to be PyPI-uploadable. The release workflow builds Linux wheels in a manylinux container.
Exposed functions:
scrubbers.scrub_bytes(data: bytes) -> bytesscrubbers.scrub_text(data: str) -> strscrubbers.scrub_lines_bytes(data: bytes) -> bytesscrubbers.scrub_lines_text(data: str) -> str
The scrub_lines_* helpers apply the library's newline-delimited streaming path over the provided input.
Example:
import scrubbers
scrubbers.scrub_text("prefix ghp_123456789012345678901234567890123456 suffix")
# "prefix **************************************** suffix"
scrubbers.scrub_lines_text("safe\nprefix ghp_123456789012345678901234567890123456 suffix\n")
# "safe\nprefix **************************************** suffix\n"
Smoke test the built wheel and sdist locally:
python3 scripts/test_python_package.py --artifact all
You can still exercise the raw extension crate directly with:
cargo build --release --manifest-path bindings/python/Cargo.toml
Node.js bindings
Build the Node extension crate:
cargo build --release --manifest-path bindings/node/Cargo.toml
Exposed functions:
scrubBuffer(buf: Buffer) -> BufferscrubLinesBuffer(buf: Buffer) -> Buffer
scrubLinesBuffer(...) applies the library's newline-delimited streaming path over the provided buffer.
Example:
const { scrubBuffer, scrubLinesBuffer } = require("./scrubbers.node");
scrubBuffer(Buffer.from("prefix ghp_123456789012345678901234567890123456 suffix"))
// <Buffer 70 72 65 66 69 78 20 2a ...>
scrubLinesBuffer(
Buffer.from("safe\nprefix ghp_123456789012345678901234567890123456 suffix\n", "utf8"),
).toString("utf8");
// "safe\nprefix **************************************** suffix\n"
Run binding smoke tests locally:
python3 scripts/test_bindings.py --binding all
Verify the publishable Python package locally:
python3 scripts/test_python_package.py --artifact all
Benchmark the Python binding in a logging-style path:
python3 scripts/bench_python_bindings.py
Publishing
Release publishing is tag-driven through publish.yml:
python3 scripts/release.py
Dry run the release flow first:
python3 scripts/release.py --dry-run --verbose
That workflow:
- builds the Linux wheel in a manylinux2014 container and smoke tests it before upload
- builds and smoke tests Python wheels on macOS and Windows with
uv build - builds and smoke tests a source distribution
- publishes Python distributions to PyPI with Trusted Publishing
- verifies and publishes the
scrubberscrate to crates.io
Local preflight checks:
cargo publish --dry-run --locked --package scrubbers
python3 scripts/test_python_package.py --artifact all
scripts/release.py reads the version from the Cargo manifests, checks for tracked local changes, switches to main, pulls fast-forward from origin, pushes main, creates v<version>, and pushes the tag.
Before the release workflow can publish, configure trusted publishers on both registries:
- PyPI: add the GitHub repository/workflow as a trusted publisher for the
scrubbersproject and create thepypienvironment. - crates.io: publish the crate manually once, then add this repository/workflow as a trusted publisher for
scrubbersand create thecrates-ioenvironment.
TruffleHog parity workflow
TruffleHog detector coverage is tracked in src/generated_trufflehog.rs:
python scripts/sync_trufflehog_signatures.py
go run ./scripts/sync_trufflehog_pattern_fixtures.go
python scripts/verify_trufflehog_coverage.py
CI runs these commands and fails if:
- any upstream detector directory is missing from our generated signature surface, or
- generated signatures are missing when tests run.
- extracted positive fixtures are missing when tests run.
The generated TruffleHog data is tracked for parity and audit purposes, but it is not applied by default as raw redaction rules. Many upstream detectors rely on keyword gating and verifier callbacks, and running their extracted regexes directly creates false positives.
src/generated_trufflehog.rs is treated as a parity inventory, not a public API surface. Generated signature names are content-addressed hashes of the pattern data, so reordering upstream extraction no longer renumbers the whole file.
The extracted positive fixtures are also used in the Rust test suite as inline redaction cases. Each case builds literal secret fragments from the upstream positive example and asserts the scrubber preserves length while masking the matched spans in place.
Benchmark
Run the native Criterion benchmark:
cargo bench --bench throughput -- --noplot
It generates a 64 MiB synthetic payload, injects multiple secret shapes, and compares:
- raw
memcpy - straight
std::io::copypass-through into a fixed buffer scrubber/in_placescrubber/stream_lines
For a quick single-number smoke run, you can still use:
cargo run --release --bin scrub-bench
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distributions
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file scrubbers-0.1.1.tar.gz.
File metadata
- Download URL: scrubbers-0.1.1.tar.gz
- Upload date:
- Size: 128.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
eca022b3f0755c246e968d35d42d9e0fe05800c2518c7dfdcf0487578abd30c0
|
|
| MD5 |
6dc2de6a302c5817f10cea7586f6e5bc
|
|
| BLAKE2b-256 |
f74365542493fb23018e21748234ada1fb8ee1263af7a1fd4a15f384b90c9bba
|
Provenance
The following attestation bundles were made for scrubbers-0.1.1.tar.gz:
Publisher:
publish.yml on zmackie/ScrubbeRS
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
scrubbers-0.1.1.tar.gz -
Subject digest:
eca022b3f0755c246e968d35d42d9e0fe05800c2518c7dfdcf0487578abd30c0 - Sigstore transparency entry: 1272609208
- Sigstore integration time:
-
Permalink:
zmackie/ScrubbeRS@eb5b757000d17c3f31e56654298465ce9160f6ea -
Branch / Tag:
refs/tags/v0.1.1 - Owner: https://github.com/zmackie
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@eb5b757000d17c3f31e56654298465ce9160f6ea -
Trigger Event:
push
-
Statement type:
File details
Details for the file scrubbers-0.1.1-cp39-abi3-win_amd64.whl.
File metadata
- Download URL: scrubbers-0.1.1-cp39-abi3-win_amd64.whl
- Upload date:
- Size: 650.8 kB
- Tags: CPython 3.9+, Windows x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
803a15f6c19181a6e42b37c766375bf4639ff666861c3460dfc58fe6d48790d7
|
|
| MD5 |
30ce0a77503ca08eb5b43454a32410a0
|
|
| BLAKE2b-256 |
f0917cc4b7af58e699e0ab3cc7d66e27836fca98c0c6d85b72bafc438f454120
|
Provenance
The following attestation bundles were made for scrubbers-0.1.1-cp39-abi3-win_amd64.whl:
Publisher:
publish.yml on zmackie/ScrubbeRS
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
scrubbers-0.1.1-cp39-abi3-win_amd64.whl -
Subject digest:
803a15f6c19181a6e42b37c766375bf4639ff666861c3460dfc58fe6d48790d7 - Sigstore transparency entry: 1272609219
- Sigstore integration time:
-
Permalink:
zmackie/ScrubbeRS@eb5b757000d17c3f31e56654298465ce9160f6ea -
Branch / Tag:
refs/tags/v0.1.1 - Owner: https://github.com/zmackie
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@eb5b757000d17c3f31e56654298465ce9160f6ea -
Trigger Event:
push
-
Statement type:
File details
Details for the file scrubbers-0.1.1-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.
File metadata
- Download URL: scrubbers-0.1.1-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
- Upload date:
- Size: 839.1 kB
- Tags: CPython 3.9+, manylinux: glibc 2.17+ x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d0d876e61d41870ddab4634a02d92792102f0dda9c064b742231a15ad8aaf291
|
|
| MD5 |
b6f7695bafd9b74c660e31e003a68194
|
|
| BLAKE2b-256 |
af28cda699d89448e15c48cf3229004bed6aec2b8d68779c14dd9826933fa1a0
|
Provenance
The following attestation bundles were made for scrubbers-0.1.1-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl:
Publisher:
publish.yml on zmackie/ScrubbeRS
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
scrubbers-0.1.1-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl -
Subject digest:
d0d876e61d41870ddab4634a02d92792102f0dda9c064b742231a15ad8aaf291 - Sigstore transparency entry: 1272609230
- Sigstore integration time:
-
Permalink:
zmackie/ScrubbeRS@eb5b757000d17c3f31e56654298465ce9160f6ea -
Branch / Tag:
refs/tags/v0.1.1 - Owner: https://github.com/zmackie
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@eb5b757000d17c3f31e56654298465ce9160f6ea -
Trigger Event:
push
-
Statement type:
File details
Details for the file scrubbers-0.1.1-cp39-abi3-macosx_11_0_arm64.whl.
File metadata
- Download URL: scrubbers-0.1.1-cp39-abi3-macosx_11_0_arm64.whl
- Upload date:
- Size: 703.4 kB
- Tags: CPython 3.9+, macOS 11.0+ ARM64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f6f0afffccd0f92f1fbb3e451a4b2b3adc0b8a3bca57b21e3929f490397e9312
|
|
| MD5 |
36ca19476b1ae4d7bf49b540ff68ab0f
|
|
| BLAKE2b-256 |
36c84f7f9c12d1be4bd83ef7e6a45e42f98e0f9c89658124588737c79dc6e93b
|
Provenance
The following attestation bundles were made for scrubbers-0.1.1-cp39-abi3-macosx_11_0_arm64.whl:
Publisher:
publish.yml on zmackie/ScrubbeRS
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
scrubbers-0.1.1-cp39-abi3-macosx_11_0_arm64.whl -
Subject digest:
f6f0afffccd0f92f1fbb3e451a4b2b3adc0b8a3bca57b21e3929f490397e9312 - Sigstore transparency entry: 1272609242
- Sigstore integration time:
-
Permalink:
zmackie/ScrubbeRS@eb5b757000d17c3f31e56654298465ce9160f6ea -
Branch / Tag:
refs/tags/v0.1.1 - Owner: https://github.com/zmackie
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@eb5b757000d17c3f31e56654298465ce9160f6ea -
Trigger Event:
push
-
Statement type: