cloakrs 0.1.0a1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- cloakrs-0.1.0a1/Cargo.toml +46 -0
- cloakrs-0.1.0a1/LICENSE.md +21 -0
- cloakrs-0.1.0a1/PKG-INFO +190 -0
- cloakrs-0.1.0a1/README.md +169 -0
- cloakrs-0.1.0a1/bindings/python/CHANGELOG.md +13 -0
- cloakrs-0.1.0a1/bindings/python/Cargo.lock +542 -0
- cloakrs-0.1.0a1/bindings/python/Cargo.toml +23 -0
- cloakrs-0.1.0a1/bindings/python/LICENSE.md +21 -0
- cloakrs-0.1.0a1/bindings/python/README.md +169 -0
- cloakrs-0.1.0a1/bindings/python/release.py +144 -0
- cloakrs-0.1.0a1/bindings/python/src/lib.rs +383 -0
- cloakrs-0.1.0a1/bindings/python/tests/installed_smoke.py +44 -0
- cloakrs-0.1.0a1/bindings/python/tests/test_sanitizer.py +301 -0
- cloakrs-0.1.0a1/bindings/python/tests/test_scanner.py +175 -0
- cloakrs-0.1.0a1/bindings/python/tests/typing_smoke.py +25 -0
- cloakrs-0.1.0a1/crates/cloakrs-core/Cargo.toml +26 -0
- cloakrs-0.1.0a1/crates/cloakrs-core/README.md +192 -0
- cloakrs-0.1.0a1/crates/cloakrs-core/src/context.rs +244 -0
- cloakrs-0.1.0a1/crates/cloakrs-core/src/error.rs +51 -0
- cloakrs-0.1.0a1/crates/cloakrs-core/src/finding.rs +380 -0
- cloakrs-0.1.0a1/crates/cloakrs-core/src/lib.rs +47 -0
- cloakrs-0.1.0a1/crates/cloakrs-core/src/masker.rs +923 -0
- cloakrs-0.1.0a1/crates/cloakrs-core/src/prompt.rs +986 -0
- cloakrs-0.1.0a1/crates/cloakrs-core/src/recognizer.rs +200 -0
- cloakrs-0.1.0a1/crates/cloakrs-core/src/scanner.rs +594 -0
- cloakrs-0.1.0a1/crates/cloakrs-locales/Cargo.toml +19 -0
- cloakrs-0.1.0a1/crates/cloakrs-locales/README.md +192 -0
- cloakrs-0.1.0a1/crates/cloakrs-locales/src/br_br.rs +404 -0
- cloakrs-0.1.0a1/crates/cloakrs-locales/src/common.rs +45 -0
- cloakrs-0.1.0a1/crates/cloakrs-locales/src/de_de.rs +256 -0
- cloakrs-0.1.0a1/crates/cloakrs-locales/src/en_gb.rs +418 -0
- cloakrs-0.1.0a1/crates/cloakrs-locales/src/eu.rs +59 -0
- cloakrs-0.1.0a1/crates/cloakrs-locales/src/fr_fr.rs +285 -0
- cloakrs-0.1.0a1/crates/cloakrs-locales/src/in_in.rs +404 -0
- cloakrs-0.1.0a1/crates/cloakrs-locales/src/lib.rs +78 -0
- cloakrs-0.1.0a1/crates/cloakrs-locales/src/nl_nl.rs +182 -0
- cloakrs-0.1.0a1/crates/cloakrs-patterns/Cargo.toml +18 -0
- cloakrs-0.1.0a1/crates/cloakrs-patterns/README.md +192 -0
- cloakrs-0.1.0a1/crates/cloakrs-patterns/src/api_key.rs +434 -0
- cloakrs-0.1.0a1/crates/cloakrs-patterns/src/common.rs +51 -0
- cloakrs-0.1.0a1/crates/cloakrs-patterns/src/credit_card.rs +281 -0
- cloakrs-0.1.0a1/crates/cloakrs-patterns/src/crypto.rs +296 -0
- cloakrs-0.1.0a1/crates/cloakrs-patterns/src/date_of_birth.rs +376 -0
- cloakrs-0.1.0a1/crates/cloakrs-patterns/src/email.rs +219 -0
- cloakrs-0.1.0a1/crates/cloakrs-patterns/src/hostname.rs +363 -0
- cloakrs-0.1.0a1/crates/cloakrs-patterns/src/iban.rs +302 -0
- cloakrs-0.1.0a1/crates/cloakrs-patterns/src/ip_address.rs +250 -0
- cloakrs-0.1.0a1/crates/cloakrs-patterns/src/lib.rs +93 -0
- cloakrs-0.1.0a1/crates/cloakrs-patterns/src/mac_address.rs +204 -0
- cloakrs-0.1.0a1/crates/cloakrs-patterns/src/person_name.rs +331 -0
- cloakrs-0.1.0a1/crates/cloakrs-patterns/src/phone.rs +270 -0
- cloakrs-0.1.0a1/crates/cloakrs-patterns/src/physical_address.rs +262 -0
- cloakrs-0.1.0a1/crates/cloakrs-patterns/src/ssn.rs +169 -0
- cloakrs-0.1.0a1/crates/cloakrs-patterns/src/url.rs +643 -0
- cloakrs-0.1.0a1/crates/cloakrs-patterns/src/user_path.rs +384 -0
- cloakrs-0.1.0a1/crates/cloakrs-patterns/tests/false_positive.rs +77 -0
- cloakrs-0.1.0a1/crates/cloakrs-patterns/tests/scanner_integration.rs +359 -0
- cloakrs-0.1.0a1/pyproject.toml +41 -0
- cloakrs-0.1.0a1/python/cloakrs/__init__.py +231 -0
- cloakrs-0.1.0a1/python/cloakrs/_native.pyi +38 -0
- cloakrs-0.1.0a1/python/cloakrs/py.typed +0 -0
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
[workspace]
|
|
2
|
+
members = [
|
|
3
|
+
"crates/cloakrs-core",
|
|
4
|
+
"crates/cloakrs-patterns",
|
|
5
|
+
"crates/cloakrs-locales",
|
|
6
|
+
"crates/cloakrs-adapters",
|
|
7
|
+
"crates/cloakrs-tracing",
|
|
8
|
+
"crates/cloakrs-cli",
|
|
9
|
+
]
|
|
10
|
+
exclude = ["bindings/python"]
|
|
11
|
+
resolver = "2"
|
|
12
|
+
|
|
13
|
+
[workspace.package]
|
|
14
|
+
version = "0.4.0"
|
|
15
|
+
edition = "2021"
|
|
16
|
+
rust-version = "1.75.0"
|
|
17
|
+
license = "MIT"
|
|
18
|
+
repository = "https://github.com/kadir/cloakrs"
|
|
19
|
+
homepage = "https://github.com/kadir/cloakrs"
|
|
20
|
+
documentation = "https://docs.rs/cloakrs-core"
|
|
21
|
+
readme = "README.md"
|
|
22
|
+
keywords = ["pii", "redaction", "privacy", "masking", "cli"]
|
|
23
|
+
categories = ["command-line-utilities", "text-processing"]
|
|
24
|
+
|
|
25
|
+
[workspace.dependencies]
|
|
26
|
+
aes-gcm = "0.10"
|
|
27
|
+
base64 = "0.22"
|
|
28
|
+
clap = { version = "4", features = ["derive", "env"] }
|
|
29
|
+
criterion = "0.5"
|
|
30
|
+
csv = "1"
|
|
31
|
+
indicatif = "0.17"
|
|
32
|
+
ignore = "0.4"
|
|
33
|
+
once_cell = "1"
|
|
34
|
+
proptest = "1"
|
|
35
|
+
rayon = "1"
|
|
36
|
+
regex = "1"
|
|
37
|
+
serde = { version = "1", features = ["derive"] }
|
|
38
|
+
serde_json = "1"
|
|
39
|
+
same-file = "1"
|
|
40
|
+
tempfile = "3"
|
|
41
|
+
sha2 = "0.10"
|
|
42
|
+
thiserror = "2"
|
|
43
|
+
tokio = { version = "1", features = ["full"] }
|
|
44
|
+
toml = "0.8"
|
|
45
|
+
tracing = "0.1"
|
|
46
|
+
tracing-subscriber = "0.3"
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 cloakrs contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
cloakrs-0.1.0a1/PKG-INFO
ADDED
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: cloakrs
|
|
3
|
+
Version: 0.1.0a1
|
|
4
|
+
Classifier: Development Status :: 3 - Alpha
|
|
5
|
+
Classifier: Programming Language :: Python :: 3
|
|
6
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
7
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
8
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
9
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
10
|
+
Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
11
|
+
Classifier: Programming Language :: Rust
|
|
12
|
+
License-File: LICENSE.md
|
|
13
|
+
Summary: Local PII detection, redaction, and prompt sanitization powered by Rust
|
|
14
|
+
License-Expression: MIT
|
|
15
|
+
Requires-Python: >=3.11
|
|
16
|
+
Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
|
|
17
|
+
Project-URL: Homepage, https://github.com/kadir/cloakrs
|
|
18
|
+
Project-URL: Issues, https://github.com/kadir/cloakrs/issues
|
|
19
|
+
Project-URL: Repository, https://github.com/kadir/cloakrs
|
|
20
|
+
|
|
21
|
+
# cloakrs for Python
|
|
22
|
+
|
|
23
|
+
An alpha Python interface to the cloakrs 0.4.0 Rust engine, with local scanning,
|
|
24
|
+
redaction, and reversible prompt sanitization. Input stays in your process;
|
|
25
|
+
the library makes no network requests and does not invoke the CLI.
|
|
26
|
+
|
|
27
|
+
## Install the prerelease
|
|
28
|
+
|
|
29
|
+
```sh
|
|
30
|
+
python -m pip install --only-binary=:all: 'cloakrs==0.1.0a1'
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Requires regular CPython 3.11–3.14. Wheels include the compiled Rust engine;
|
|
34
|
+
installation needs no Rust compiler or additional Python runtime dependencies.
|
|
35
|
+
|
|
36
|
+
| Platform | Wheel support |
|
|
37
|
+
| --- | --- |
|
|
38
|
+
| Linux with glibc 2.17+ | x86_64 and ARM64 |
|
|
39
|
+
| macOS 11+ | Intel and Apple Silicon |
|
|
40
|
+
| Windows | x86_64 |
|
|
41
|
+
|
|
42
|
+
Alpine/musl, Windows ARM64, free-threaded Python, and alternative interpreters
|
|
43
|
+
do not have validated wheels. A source install requires Rust 1.83 or newer:
|
|
44
|
+
`python -m pip install --no-binary=cloakrs 'cloakrs==0.1.0a1'`.
|
|
45
|
+
This is an early prerelease; evaluate it on representative data before adoption.
|
|
46
|
+
|
|
47
|
+
## Use
|
|
48
|
+
|
|
49
|
+
```python
|
|
50
|
+
from cloakrs import Scanner
|
|
51
|
+
|
|
52
|
+
scanner = Scanner(locale="us")
|
|
53
|
+
result = scanner.scan("Email jane@example.com")
|
|
54
|
+
assert result.masked_text == "Email [EMAIL]"
|
|
55
|
+
|
|
56
|
+
finding = result.findings[0]
|
|
57
|
+
assert finding.entity_type == "email"
|
|
58
|
+
assert finding.text == "jane@example.com"
|
|
59
|
+
assert scanner.mask("Email jane@example.com") == "Email [EMAIL]"
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
`Scanner` and `Sanitizer` accept the same keyword-only options:
|
|
63
|
+
|
|
64
|
+
| Option | Default | Meaning |
|
|
65
|
+
| --- | --- | --- |
|
|
66
|
+
| `locale` | `"universal"` | One of `universal`, `us`, `uk`, `nl`, `de`, `fr`, `in`, `br`, `eu` |
|
|
67
|
+
| `min_confidence` | `0.0` | Finite threshold from 0 to 1, matching the Rust library default |
|
|
68
|
+
| `exclude_entities` | `()` | Sequence of canonical entity names; see `cloakrs.ENTITY_TYPES` |
|
|
69
|
+
| `allow_list` | `()` | Literal values whose overlapping findings are suppressed |
|
|
70
|
+
| `deny_list` | `()` | Literal values added as `deny-list` findings |
|
|
71
|
+
|
|
72
|
+
All options are explicit; no configuration files are discovered. Duplicate
|
|
73
|
+
exclusions have no additional effect. Deny-list rules survive entity exclusions;
|
|
74
|
+
allow-list precedence still applies to deny-list findings. `passport-number` and
|
|
75
|
+
`drivers-license` are reserved entity names without bundled recognizers.
|
|
76
|
+
|
|
77
|
+
```python
|
|
78
|
+
scanner = Scanner(locale="us", exclude_entities=["url"])
|
|
79
|
+
assert scanner.mask("https://example.com?email=jane%40example.com") == (
|
|
80
|
+
"https://example.com?email=[EMAIL]"
|
|
81
|
+
)
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
Excluding URLs can leave embedded credentials visible, including userinfo
|
|
85
|
+
passwords and unsupported encoded query secrets. Default URL detection remains
|
|
86
|
+
enabled. Pattern-based detection does not guarantee that arbitrary text contains
|
|
87
|
+
no secrets; choose a locale and check representative input for your application.
|
|
88
|
+
|
|
89
|
+
## Sanitize a prompt and restore a response
|
|
90
|
+
|
|
91
|
+
```python
|
|
92
|
+
from cloakrs import Mapping, Sanitizer
|
|
93
|
+
|
|
94
|
+
sanitizer = Sanitizer(locale="us")
|
|
95
|
+
clean, mapping = sanitizer.sanitize("Email jane@example.com")
|
|
96
|
+
assert clean == "Email [EMAIL_1]"
|
|
97
|
+
assert len(mapping) == 1
|
|
98
|
+
assert repr(mapping) == "Mapping(entries=1)"
|
|
99
|
+
|
|
100
|
+
# Send only clean to your model; keep mapping inside your application.
|
|
101
|
+
response = "Reply to [ email_1 ]"
|
|
102
|
+
assert mapping.restore(response) == "Reply to jane@example.com"
|
|
103
|
+
assert mapping.restore(response, strict=True) == response
|
|
104
|
+
|
|
105
|
+
# Explicit export contains original values. Keep this JSON private too.
|
|
106
|
+
mapping_json = mapping.to_json()
|
|
107
|
+
loaded = Mapping.from_json(mapping_json)
|
|
108
|
+
assert loaded.restore(clean) == "Email jane@example.com"
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
Use `sanitize(text, placeholder_style="braces")` for `{EMAIL_1}` placeholders;
|
|
112
|
+
the default is `"brackets"`. Repeated values of the same entity type share one
|
|
113
|
+
placeholder within a call. Existing placeholders are preserved, with new ones
|
|
114
|
+
using available numbers. Each call returns an independent mapping. Nested
|
|
115
|
+
findings are replaced once at their outermost span.
|
|
116
|
+
|
|
117
|
+
Restoration tolerates ASCII case changes and whitespace immediately inside
|
|
118
|
+
numbered placeholders. `strict=True` requires exact spelling. Unknown
|
|
119
|
+
placeholders remain untouched, and restored values are not expanded recursively.
|
|
120
|
+
|
|
121
|
+
`Mapping` is opaque: its representation shows only the entry count, and pickling
|
|
122
|
+
is disabled. It does not automatically write files or log values. `to_json()`
|
|
123
|
+
explicitly exports sensitive originals using the Rust CLI mapping schema;
|
|
124
|
+
`from_json()` can import files produced by `cloakrs sanitize`. The CLI's
|
|
125
|
+
`cloakrs restore` can read Python exports. Mapping spans remain **UTF-8 byte
|
|
126
|
+
offsets** of the first occurrence, while Python `Finding` indices count code
|
|
127
|
+
points. JSON import rejects invalid schema, empty or ambiguous duplicate
|
|
128
|
+
placeholders, confidence outside 0–1, and spans whose byte lengths do not match
|
|
129
|
+
their originals. Unknown extra JSON fields are ignored, as in the Rust CLI.
|
|
130
|
+
|
|
131
|
+
## Results and errors
|
|
132
|
+
|
|
133
|
+
`ScanResult` and `Finding` are immutable typed dataclasses. Findings include
|
|
134
|
+
`entity_type`, `start`, `end`, `confidence`, `recognizer_id`, and `text`.
|
|
135
|
+
`source[finding.start:finding.end]` selects the matched original text. Indices
|
|
136
|
+
count Python Unicode code points, not Rust UTF-8 bytes or visual graphemes.
|
|
137
|
+
Nested URL findings can overlap; use `masked_text` for Rust's redaction policy.
|
|
138
|
+
|
|
139
|
+
Representations omit original and masked text. Accessing `.text`, `.masked_text`,
|
|
140
|
+
or explicitly serializing a result can expose sensitive values. Masked text may
|
|
141
|
+
still contain unsupported or intentionally excluded values.
|
|
142
|
+
|
|
143
|
+
Unknown locale, entity, or placeholder-style names and invalid confidence values
|
|
144
|
+
raise `ValueError`; wrong argument types raise `TypeError`. Lone Unicode
|
|
145
|
+
surrogates are rejected with `ValueError`.
|
|
146
|
+
Internal scanning failures raise a generic `RuntimeError` without source values.
|
|
147
|
+
|
|
148
|
+
Reuse scanners and sanitizers for repeated calls. Scanning, sanitization,
|
|
149
|
+
restoration, and mapping JSON processing release the Python interpreter lock
|
|
150
|
+
while Rust runs. Instances can be shared between Python threads.
|
|
151
|
+
`mask()` avoids allocating Python finding objects when only redacted text is needed.
|
|
152
|
+
|
|
153
|
+
## Build and test from this repository
|
|
154
|
+
|
|
155
|
+
Requires regular CPython 3.11–3.14 and Rust 1.83 or newer for building this binding.
|
|
156
|
+
The Rust library and CLI retain their independent Rust 1.75 minimum. A built wheel
|
|
157
|
+
does not require a Rust compiler or additional Python runtime dependencies.
|
|
158
|
+
Free-threaded Python and alternative interpreters are not validated yet.
|
|
159
|
+
|
|
160
|
+
From the repository root:
|
|
161
|
+
|
|
162
|
+
```sh
|
|
163
|
+
python3 -m venv .venv-python
|
|
164
|
+
# Linux/macOS; on Windows activate .venv-python\Scripts\Activate.ps1 instead.
|
|
165
|
+
. .venv-python/bin/activate
|
|
166
|
+
python -m pip install 'maturin>=1.15,<2' 'pytest>=8,<10' 'mypy>=1.15,<3'
|
|
167
|
+
cd bindings/python
|
|
168
|
+
maturin build --release --locked --out dist
|
|
169
|
+
python -m pip install --no-deps --force-reinstall dist/*.whl
|
|
170
|
+
cargo build --manifest-path ../../Cargo.toml --locked -p cloakrs-cli
|
|
171
|
+
python -m pytest tests
|
|
172
|
+
python -m mypy python/cloakrs tests/typing_smoke.py
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
In PowerShell, install the wheel with
|
|
176
|
+
`python -m pip install --no-deps --force-reinstall (Get-ChildItem dist/*.whl)`.
|
|
177
|
+
The test suite runs against the installed wheel and compares all 58 existing
|
|
178
|
+
evaluation cases against the Rust detection snapshot. It also checks exact
|
|
179
|
+
sanitizer round trips in both placeholder styles and exchanges mappings with the
|
|
180
|
+
real CLI in both directions. These tests require the repository checkout and the
|
|
181
|
+
CLI built above. Python binding changes have a separate CI workflow.
|
|
182
|
+
|
|
183
|
+
## Releasing
|
|
184
|
+
|
|
185
|
+
Python releases use `python-v<version>` tags and the dedicated
|
|
186
|
+
`python-release.yml` workflow with PyPI Trusted Publishing. Branch pushes and
|
|
187
|
+
pull requests validate packages without publishing. See the repository's
|
|
188
|
+
[Python release guide](https://github.com/kadir/cloakrs/blob/master/docs/python-releases.md)
|
|
189
|
+
for account setup, artifact checks, tagging, and verification after upload.
|
|
190
|
+
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
# cloakrs for Python
|
|
2
|
+
|
|
3
|
+
An alpha Python interface to the cloakrs 0.4.0 Rust engine, with local scanning,
|
|
4
|
+
redaction, and reversible prompt sanitization. Input stays in your process;
|
|
5
|
+
the library makes no network requests and does not invoke the CLI.
|
|
6
|
+
|
|
7
|
+
## Install the prerelease
|
|
8
|
+
|
|
9
|
+
```sh
|
|
10
|
+
python -m pip install --only-binary=:all: 'cloakrs==0.1.0a1'
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Requires regular CPython 3.11–3.14. Wheels include the compiled Rust engine;
|
|
14
|
+
installation needs no Rust compiler or additional Python runtime dependencies.
|
|
15
|
+
|
|
16
|
+
| Platform | Wheel support |
|
|
17
|
+
| --- | --- |
|
|
18
|
+
| Linux with glibc 2.17+ | x86_64 and ARM64 |
|
|
19
|
+
| macOS 11+ | Intel and Apple Silicon |
|
|
20
|
+
| Windows | x86_64 |
|
|
21
|
+
|
|
22
|
+
Alpine/musl, Windows ARM64, free-threaded Python, and alternative interpreters
|
|
23
|
+
do not have validated wheels. A source install requires Rust 1.83 or newer:
|
|
24
|
+
`python -m pip install --no-binary=cloakrs 'cloakrs==0.1.0a1'`.
|
|
25
|
+
This is an early prerelease; evaluate it on representative data before adoption.
|
|
26
|
+
|
|
27
|
+
## Use
|
|
28
|
+
|
|
29
|
+
```python
|
|
30
|
+
from cloakrs import Scanner
|
|
31
|
+
|
|
32
|
+
scanner = Scanner(locale="us")
|
|
33
|
+
result = scanner.scan("Email jane@example.com")
|
|
34
|
+
assert result.masked_text == "Email [EMAIL]"
|
|
35
|
+
|
|
36
|
+
finding = result.findings[0]
|
|
37
|
+
assert finding.entity_type == "email"
|
|
38
|
+
assert finding.text == "jane@example.com"
|
|
39
|
+
assert scanner.mask("Email jane@example.com") == "Email [EMAIL]"
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
`Scanner` and `Sanitizer` accept the same keyword-only options:
|
|
43
|
+
|
|
44
|
+
| Option | Default | Meaning |
|
|
45
|
+
| --- | --- | --- |
|
|
46
|
+
| `locale` | `"universal"` | One of `universal`, `us`, `uk`, `nl`, `de`, `fr`, `in`, `br`, `eu` |
|
|
47
|
+
| `min_confidence` | `0.0` | Finite threshold from 0 to 1, matching the Rust library default |
|
|
48
|
+
| `exclude_entities` | `()` | Sequence of canonical entity names; see `cloakrs.ENTITY_TYPES` |
|
|
49
|
+
| `allow_list` | `()` | Literal values whose overlapping findings are suppressed |
|
|
50
|
+
| `deny_list` | `()` | Literal values added as `deny-list` findings |
|
|
51
|
+
|
|
52
|
+
All options are explicit; no configuration files are discovered. Duplicate
|
|
53
|
+
exclusions have no additional effect. Deny-list rules survive entity exclusions;
|
|
54
|
+
allow-list precedence still applies to deny-list findings. `passport-number` and
|
|
55
|
+
`drivers-license` are reserved entity names without bundled recognizers.
|
|
56
|
+
|
|
57
|
+
```python
|
|
58
|
+
scanner = Scanner(locale="us", exclude_entities=["url"])
|
|
59
|
+
assert scanner.mask("https://example.com?email=jane%40example.com") == (
|
|
60
|
+
"https://example.com?email=[EMAIL]"
|
|
61
|
+
)
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Excluding URLs can leave embedded credentials visible, including userinfo
|
|
65
|
+
passwords and unsupported encoded query secrets. Default URL detection remains
|
|
66
|
+
enabled. Pattern-based detection does not guarantee that arbitrary text contains
|
|
67
|
+
no secrets; choose a locale and check representative input for your application.
|
|
68
|
+
|
|
69
|
+
## Sanitize a prompt and restore a response
|
|
70
|
+
|
|
71
|
+
```python
|
|
72
|
+
from cloakrs import Mapping, Sanitizer
|
|
73
|
+
|
|
74
|
+
sanitizer = Sanitizer(locale="us")
|
|
75
|
+
clean, mapping = sanitizer.sanitize("Email jane@example.com")
|
|
76
|
+
assert clean == "Email [EMAIL_1]"
|
|
77
|
+
assert len(mapping) == 1
|
|
78
|
+
assert repr(mapping) == "Mapping(entries=1)"
|
|
79
|
+
|
|
80
|
+
# Send only clean to your model; keep mapping inside your application.
|
|
81
|
+
response = "Reply to [ email_1 ]"
|
|
82
|
+
assert mapping.restore(response) == "Reply to jane@example.com"
|
|
83
|
+
assert mapping.restore(response, strict=True) == response
|
|
84
|
+
|
|
85
|
+
# Explicit export contains original values. Keep this JSON private too.
|
|
86
|
+
mapping_json = mapping.to_json()
|
|
87
|
+
loaded = Mapping.from_json(mapping_json)
|
|
88
|
+
assert loaded.restore(clean) == "Email jane@example.com"
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
Use `sanitize(text, placeholder_style="braces")` for `{EMAIL_1}` placeholders;
|
|
92
|
+
the default is `"brackets"`. Repeated values of the same entity type share one
|
|
93
|
+
placeholder within a call. Existing placeholders are preserved, with new ones
|
|
94
|
+
using available numbers. Each call returns an independent mapping. Nested
|
|
95
|
+
findings are replaced once at their outermost span.
|
|
96
|
+
|
|
97
|
+
Restoration tolerates ASCII case changes and whitespace immediately inside
|
|
98
|
+
numbered placeholders. `strict=True` requires exact spelling. Unknown
|
|
99
|
+
placeholders remain untouched, and restored values are not expanded recursively.
|
|
100
|
+
|
|
101
|
+
`Mapping` is opaque: its representation shows only the entry count, and pickling
|
|
102
|
+
is disabled. It does not automatically write files or log values. `to_json()`
|
|
103
|
+
explicitly exports sensitive originals using the Rust CLI mapping schema;
|
|
104
|
+
`from_json()` can import files produced by `cloakrs sanitize`. The CLI's
|
|
105
|
+
`cloakrs restore` can read Python exports. Mapping spans remain **UTF-8 byte
|
|
106
|
+
offsets** of the first occurrence, while Python `Finding` indices count code
|
|
107
|
+
points. JSON import rejects invalid schema, empty or ambiguous duplicate
|
|
108
|
+
placeholders, confidence outside 0–1, and spans whose byte lengths do not match
|
|
109
|
+
their originals. Unknown extra JSON fields are ignored, as in the Rust CLI.
|
|
110
|
+
|
|
111
|
+
## Results and errors
|
|
112
|
+
|
|
113
|
+
`ScanResult` and `Finding` are immutable typed dataclasses. Findings include
|
|
114
|
+
`entity_type`, `start`, `end`, `confidence`, `recognizer_id`, and `text`.
|
|
115
|
+
`source[finding.start:finding.end]` selects the matched original text. Indices
|
|
116
|
+
count Python Unicode code points, not Rust UTF-8 bytes or visual graphemes.
|
|
117
|
+
Nested URL findings can overlap; use `masked_text` for Rust's redaction policy.
|
|
118
|
+
|
|
119
|
+
Representations omit original and masked text. Accessing `.text`, `.masked_text`,
|
|
120
|
+
or explicitly serializing a result can expose sensitive values. Masked text may
|
|
121
|
+
still contain unsupported or intentionally excluded values.
|
|
122
|
+
|
|
123
|
+
Unknown locale, entity, or placeholder-style names and invalid confidence values
|
|
124
|
+
raise `ValueError`; wrong argument types raise `TypeError`. Lone Unicode
|
|
125
|
+
surrogates are rejected with `ValueError`.
|
|
126
|
+
Internal scanning failures raise a generic `RuntimeError` without source values.
|
|
127
|
+
|
|
128
|
+
Reuse scanners and sanitizers for repeated calls. Scanning, sanitization,
|
|
129
|
+
restoration, and mapping JSON processing release the Python interpreter lock
|
|
130
|
+
while Rust runs. Instances can be shared between Python threads.
|
|
131
|
+
`mask()` avoids allocating Python finding objects when only redacted text is needed.
|
|
132
|
+
|
|
133
|
+
## Build and test from this repository
|
|
134
|
+
|
|
135
|
+
Requires regular CPython 3.11–3.14 and Rust 1.83 or newer for building this binding.
|
|
136
|
+
The Rust library and CLI retain their independent Rust 1.75 minimum. A built wheel
|
|
137
|
+
does not require a Rust compiler or additional Python runtime dependencies.
|
|
138
|
+
Free-threaded Python and alternative interpreters are not validated yet.
|
|
139
|
+
|
|
140
|
+
From the repository root:
|
|
141
|
+
|
|
142
|
+
```sh
|
|
143
|
+
python3 -m venv .venv-python
|
|
144
|
+
# Linux/macOS; on Windows activate .venv-python\Scripts\Activate.ps1 instead.
|
|
145
|
+
. .venv-python/bin/activate
|
|
146
|
+
python -m pip install 'maturin>=1.15,<2' 'pytest>=8,<10' 'mypy>=1.15,<3'
|
|
147
|
+
cd bindings/python
|
|
148
|
+
maturin build --release --locked --out dist
|
|
149
|
+
python -m pip install --no-deps --force-reinstall dist/*.whl
|
|
150
|
+
cargo build --manifest-path ../../Cargo.toml --locked -p cloakrs-cli
|
|
151
|
+
python -m pytest tests
|
|
152
|
+
python -m mypy python/cloakrs tests/typing_smoke.py
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
In PowerShell, install the wheel with
|
|
156
|
+
`python -m pip install --no-deps --force-reinstall (Get-ChildItem dist/*.whl)`.
|
|
157
|
+
The test suite runs against the installed wheel and compares all 58 existing
|
|
158
|
+
evaluation cases against the Rust detection snapshot. It also checks exact
|
|
159
|
+
sanitizer round trips in both placeholder styles and exchanges mappings with the
|
|
160
|
+
real CLI in both directions. These tests require the repository checkout and the
|
|
161
|
+
CLI built above. Python binding changes have a separate CI workflow.
|
|
162
|
+
|
|
163
|
+
## Releasing
|
|
164
|
+
|
|
165
|
+
Python releases use `python-v<version>` tags and the dedicated
|
|
166
|
+
`python-release.yml` workflow with PyPI Trusted Publishing. Branch pushes and
|
|
167
|
+
pull requests validate packages without publishing. See the repository's
|
|
168
|
+
[Python release guide](https://github.com/kadir/cloakrs/blob/master/docs/python-releases.md)
|
|
169
|
+
for account setup, artifact checks, tagging, and verification after upload.
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# Python changelog
|
|
2
|
+
|
|
3
|
+
## 0.1.0a1
|
|
4
|
+
|
|
5
|
+
First Python prerelease, powered by the cloakrs 0.4.0 Rust engine.
|
|
6
|
+
|
|
7
|
+
- Local scanning and masking with typed findings and Python string indices.
|
|
8
|
+
- Prompt sanitization with bracket/brace placeholders and tolerant or strict restoration.
|
|
9
|
+
- Explicit mapping JSON import/export interoperable with the Rust CLI.
|
|
10
|
+
- Locale, confidence, entity exclusion, and literal allow/deny options.
|
|
11
|
+
- Sensitive values omitted from mapping representations; automatic mapping pickling disabled.
|
|
12
|
+
- CPython 3.11–3.14 wheels for Linux x86_64/ARM64, macOS Intel/Apple Silicon,
|
|
13
|
+
and Windows x86_64, plus a source distribution.
|