spanguard 0.1.0__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.
- spanguard-0.1.0/.github/ISSUE_TEMPLATE/bug_report.yml +87 -0
- spanguard-0.1.0/.github/ISSUE_TEMPLATE/config.yml +8 -0
- spanguard-0.1.0/.github/ISSUE_TEMPLATE/feature_request.yml +59 -0
- spanguard-0.1.0/.github/PULL_REQUEST_TEMPLATE.md +19 -0
- spanguard-0.1.0/.github/workflows/ci.yml +22 -0
- spanguard-0.1.0/.github/workflows/docs.yml +32 -0
- spanguard-0.1.0/.github/workflows/publish.yml +16 -0
- spanguard-0.1.0/.gitignore +52 -0
- spanguard-0.1.0/.gitleaks.toml +13 -0
- spanguard-0.1.0/CHANGELOG.md +90 -0
- spanguard-0.1.0/CITATION.cff +22 -0
- spanguard-0.1.0/CODE_OF_CONDUCT.md +142 -0
- spanguard-0.1.0/CONTRIBUTING.md +163 -0
- spanguard-0.1.0/LICENSE +21 -0
- spanguard-0.1.0/PKG-INFO +235 -0
- spanguard-0.1.0/README.md +206 -0
- spanguard-0.1.0/SECURITY.md +115 -0
- spanguard-0.1.0/benchmarks/throughput.py +129 -0
- spanguard-0.1.0/docs/PUBLISH-CHECKLIST.md +96 -0
- spanguard-0.1.0/docs/api.md +78 -0
- spanguard-0.1.0/docs/assets/logo.svg +12 -0
- spanguard-0.1.0/docs/cli.md +112 -0
- spanguard-0.1.0/docs/css/extra.css +56 -0
- spanguard-0.1.0/docs/index.md +135 -0
- spanguard-0.1.0/docs/known-limitations.md +122 -0
- spanguard-0.1.0/docs/quickstart.md +110 -0
- spanguard-0.1.0/docs/recognizers.md +89 -0
- spanguard-0.1.0/docs/streaming.md +105 -0
- spanguard-0.1.0/mkdocs.yml +111 -0
- spanguard-0.1.0/pyproject.toml +67 -0
- spanguard-0.1.0/src/spanguard/__init__.py +21 -0
- spanguard-0.1.0/src/spanguard/_keys.py +29 -0
- spanguard-0.1.0/src/spanguard/_overlap.py +53 -0
- spanguard-0.1.0/src/spanguard/_transform.py +54 -0
- spanguard-0.1.0/src/spanguard/cli.py +145 -0
- spanguard-0.1.0/src/spanguard/policy.py +35 -0
- spanguard-0.1.0/src/spanguard/py.typed +0 -0
- spanguard-0.1.0/src/spanguard/recognizers/__init__.py +8 -0
- spanguard-0.1.0/src/spanguard/recognizers/base.py +28 -0
- spanguard-0.1.0/src/spanguard/recognizers/luhn.py +19 -0
- spanguard-0.1.0/src/spanguard/recognizers/ner.py +57 -0
- spanguard-0.1.0/src/spanguard/recognizers/regex_recognizers.py +156 -0
- spanguard-0.1.0/src/spanguard/redactor.py +94 -0
- spanguard-0.1.0/src/spanguard/result.py +49 -0
- spanguard-0.1.0/src/spanguard/streaming.py +149 -0
- spanguard-0.1.0/src/spanguard/tokenize.py +71 -0
- spanguard-0.1.0/tests/__init__.py +0 -0
- spanguard-0.1.0/tests/test_audit_safety.py +55 -0
- spanguard-0.1.0/tests/test_benchmark_smoke.py +42 -0
- spanguard-0.1.0/tests/test_cli.py +186 -0
- spanguard-0.1.0/tests/test_docs_examples.py +47 -0
- spanguard-0.1.0/tests/test_modes.py +99 -0
- spanguard-0.1.0/tests/test_ner.py +101 -0
- spanguard-0.1.0/tests/test_overlap.py +75 -0
- spanguard-0.1.0/tests/test_policy.py +35 -0
- spanguard-0.1.0/tests/test_recognizers.py +225 -0
- spanguard-0.1.0/tests/test_redactor.py +104 -0
- spanguard-0.1.0/tests/test_result.py +50 -0
- spanguard-0.1.0/tests/test_smoke.py +6 -0
- spanguard-0.1.0/tests/test_streaming.py +263 -0
- spanguard-0.1.0/tests/test_tokenize.py +124 -0
- spanguard-0.1.0/uv.lock +1347 -0
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
name: Bug report
|
|
2
|
+
description: Report a problem with spanguard (detector miss, incorrect redaction, streaming/CLI bug, etc.)
|
|
3
|
+
title: "bug: "
|
|
4
|
+
labels: ["bug"]
|
|
5
|
+
body:
|
|
6
|
+
- type: markdown
|
|
7
|
+
attributes:
|
|
8
|
+
value: |
|
|
9
|
+
Thanks for taking the time to report a bug.
|
|
10
|
+
|
|
11
|
+
**IMPORTANT — do not paste real PII/PHI or other sensitive data into this
|
|
12
|
+
issue.** GitHub issues are public. Please reproduce the problem using
|
|
13
|
+
synthetic/fake data only, e.g. `jane@example.com`, `555-123-4567`,
|
|
14
|
+
`123-45-6789`. If the bug is a security-sensitive redaction bypass or a
|
|
15
|
+
way sensitive data can leak (rather than a general correctness bug),
|
|
16
|
+
please use [private vulnerability reporting](https://github.com/Clufy/spanguard/security/advisories/new)
|
|
17
|
+
instead of a public issue — see [SECURITY.md](https://github.com/Clufy/spanguard/blob/master/SECURITY.md).
|
|
18
|
+
|
|
19
|
+
- type: checkboxes
|
|
20
|
+
id: no-real-pii
|
|
21
|
+
attributes:
|
|
22
|
+
label: Confirmation
|
|
23
|
+
options:
|
|
24
|
+
- label: I have NOT included any real PII/PHI or other sensitive data in this report — all examples below use synthetic/fake data.
|
|
25
|
+
required: true
|
|
26
|
+
- label: This is a general bug, not a security-sensitive leak or detector bypass (if it is, I will use private vulnerability reporting instead).
|
|
27
|
+
required: true
|
|
28
|
+
|
|
29
|
+
- type: input
|
|
30
|
+
id: spanguard-version
|
|
31
|
+
attributes:
|
|
32
|
+
label: spanguard version
|
|
33
|
+
description: Output of `pip show spanguard` (or `uv pip show spanguard`), or the git commit if installed from source.
|
|
34
|
+
placeholder: "0.1.0"
|
|
35
|
+
validations:
|
|
36
|
+
required: true
|
|
37
|
+
|
|
38
|
+
- type: input
|
|
39
|
+
id: python-version
|
|
40
|
+
attributes:
|
|
41
|
+
label: Python version
|
|
42
|
+
description: Output of `python --version`.
|
|
43
|
+
placeholder: "3.12.4"
|
|
44
|
+
validations:
|
|
45
|
+
required: true
|
|
46
|
+
|
|
47
|
+
- type: textarea
|
|
48
|
+
id: repro
|
|
49
|
+
attributes:
|
|
50
|
+
label: Minimal reproduction
|
|
51
|
+
description: >
|
|
52
|
+
A minimal, self-contained code snippet that reproduces the issue, using
|
|
53
|
+
synthetic data only. Include the `Policy`/`Redactor` configuration you
|
|
54
|
+
used.
|
|
55
|
+
render: python
|
|
56
|
+
placeholder: |
|
|
57
|
+
from spanguard import Redactor, Policy
|
|
58
|
+
|
|
59
|
+
policy = Policy(mode="placeholder", entities=("EMAIL",))
|
|
60
|
+
result = Redactor(policy).redact("Contact: jane@example.com")
|
|
61
|
+
print(result.text)
|
|
62
|
+
validations:
|
|
63
|
+
required: true
|
|
64
|
+
|
|
65
|
+
- type: textarea
|
|
66
|
+
id: expected
|
|
67
|
+
attributes:
|
|
68
|
+
label: Expected behavior
|
|
69
|
+
description: What did you expect to happen?
|
|
70
|
+
validations:
|
|
71
|
+
required: true
|
|
72
|
+
|
|
73
|
+
- type: textarea
|
|
74
|
+
id: actual
|
|
75
|
+
attributes:
|
|
76
|
+
label: Actual behavior
|
|
77
|
+
description: What actually happened? Include the full output/traceback if relevant (with any sensitive data replaced by synthetic placeholders).
|
|
78
|
+
validations:
|
|
79
|
+
required: true
|
|
80
|
+
|
|
81
|
+
- type: textarea
|
|
82
|
+
id: context
|
|
83
|
+
attributes:
|
|
84
|
+
label: Additional context
|
|
85
|
+
description: Anything else relevant — e.g. whether the `[ner]` extra is installed, whether this happens in streaming mode, OS, etc.
|
|
86
|
+
validations:
|
|
87
|
+
required: false
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
blank_issues_enabled: false
|
|
2
|
+
contact_links:
|
|
3
|
+
- name: Documentation
|
|
4
|
+
url: https://clufy.github.io/spanguard
|
|
5
|
+
about: Check the docs (quickstart, recognizers, streaming, CLI, known limitations) before filing an issue — your question may already be answered there.
|
|
6
|
+
- name: Report a security vulnerability
|
|
7
|
+
url: https://github.com/Clufy/spanguard/security/advisories/new
|
|
8
|
+
about: For redaction bypasses, audit-record leaks, or other security-sensitive issues, please use private vulnerability reporting instead of a public issue.
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
name: Feature request
|
|
2
|
+
description: Suggest a new detector, redaction mode, or other enhancement for spanguard
|
|
3
|
+
title: "feat: "
|
|
4
|
+
labels: ["enhancement"]
|
|
5
|
+
body:
|
|
6
|
+
- type: markdown
|
|
7
|
+
attributes:
|
|
8
|
+
value: |
|
|
9
|
+
Thanks for suggesting an improvement to spanguard.
|
|
10
|
+
|
|
11
|
+
As with bug reports: please describe your use case using synthetic
|
|
12
|
+
examples only — do not paste real PII/PHI into this issue.
|
|
13
|
+
|
|
14
|
+
- type: textarea
|
|
15
|
+
id: problem
|
|
16
|
+
attributes:
|
|
17
|
+
label: Problem / use case
|
|
18
|
+
description: >
|
|
19
|
+
What problem are you trying to solve? What's missing today? If this is
|
|
20
|
+
about a new entity type or detector, describe the format(s) you need
|
|
21
|
+
to detect (with synthetic examples).
|
|
22
|
+
placeholder: |
|
|
23
|
+
I need to detect [entity type] in my data, e.g. text like
|
|
24
|
+
"example-synthetic-value-123" — spanguard currently has no recognizer
|
|
25
|
+
for this format.
|
|
26
|
+
validations:
|
|
27
|
+
required: true
|
|
28
|
+
|
|
29
|
+
- type: textarea
|
|
30
|
+
id: proposed-solution
|
|
31
|
+
attributes:
|
|
32
|
+
label: Proposed solution
|
|
33
|
+
description: >
|
|
34
|
+
How would you like this to work? If you're proposing a new detector,
|
|
35
|
+
note whether it could be a regex `Recognizer`, whether it needs
|
|
36
|
+
context-anchoring (like `MRN`), or whether it needs NER/model-based
|
|
37
|
+
detection.
|
|
38
|
+
validations:
|
|
39
|
+
required: true
|
|
40
|
+
|
|
41
|
+
- type: textarea
|
|
42
|
+
id: alternatives
|
|
43
|
+
attributes:
|
|
44
|
+
label: Alternatives considered
|
|
45
|
+
description: >
|
|
46
|
+
Have you considered writing a custom `Recognizer` for this yourself
|
|
47
|
+
(see the docs on Recognizers)? If so, why doesn't that work for your
|
|
48
|
+
case — e.g. does it need to be a built-in default, or shared across
|
|
49
|
+
projects?
|
|
50
|
+
validations:
|
|
51
|
+
required: false
|
|
52
|
+
|
|
53
|
+
- type: textarea
|
|
54
|
+
id: context
|
|
55
|
+
attributes:
|
|
56
|
+
label: Additional context
|
|
57
|
+
description: Anything else — links, related issues, references to similar detectors in other tools, etc.
|
|
58
|
+
validations:
|
|
59
|
+
required: false
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
## Summary
|
|
2
|
+
|
|
3
|
+
<!-- What does this change do, and why? Link any related issue. -->
|
|
4
|
+
|
|
5
|
+
## Checklist
|
|
6
|
+
|
|
7
|
+
- [ ] PR title follows [Conventional Commits](https://www.conventionalcommits.org/) (`feat: ...`, `fix: ...`, `docs: ...`, `refactor: ...`, `chore: ...`, `ci: ...`, `test: ...`).
|
|
8
|
+
- [ ] Tests added/updated for the change, including **both positive and negative cases** if this touches a `Recognizer` (see `CONTRIBUTING.md`).
|
|
9
|
+
- [ ] `uv run ruff check .` passes clean.
|
|
10
|
+
- [ ] `uv run mypy` passes clean (`strict = true` — no new untyped defs or `Any` leaks).
|
|
11
|
+
- [ ] `uv run pytest --cov=spanguard --cov-report=term-missing --cov-fail-under=90` passes and coverage is maintained (currently 96.92%; gate is 90%).
|
|
12
|
+
- [ ] Docs updated under `docs/` if this changes or adds user-facing behavior (new detector, new CLI flag, new redaction mode, etc.), including `docs/known-limitations.md` if this introduces a deliberate precision/recall trade-off.
|
|
13
|
+
- [ ] `CHANGELOG.md` updated under `[Unreleased]` if this is a user-visible change.
|
|
14
|
+
- [ ] **No real PII/PHI in tests, examples, fixtures, or this PR description** — synthetic data only (e.g. `jane@example.com`, `555-123-4567`, `123-45-6789`).
|
|
15
|
+
- [ ] If this touches redaction, audit records, or streaming: confirmed no raw matched value can reach `RedactionResult.audit`, redacted output (when it shouldn't), or CLI `scan` output.
|
|
16
|
+
|
|
17
|
+
## Additional context
|
|
18
|
+
|
|
19
|
+
<!-- Anything a reviewer needs to know: design trade-offs, alternatives considered, follow-up work, etc. -->
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
on:
|
|
3
|
+
push: { branches: [master, main] }
|
|
4
|
+
pull_request:
|
|
5
|
+
jobs:
|
|
6
|
+
test:
|
|
7
|
+
runs-on: ubuntu-latest
|
|
8
|
+
steps:
|
|
9
|
+
- uses: actions/checkout@v4
|
|
10
|
+
- uses: astral-sh/setup-uv@v5
|
|
11
|
+
- run: uv venv --python 3.12
|
|
12
|
+
- run: uv pip install -e ".[ner]" pytest pytest-cov ruff mypy
|
|
13
|
+
- run: uv run ruff check .
|
|
14
|
+
- run: uv run mypy
|
|
15
|
+
- run: uv run pytest --cov=spanguard --cov-report=term-missing --cov-fail-under=90
|
|
16
|
+
|
|
17
|
+
secrets:
|
|
18
|
+
runs-on: ubuntu-latest
|
|
19
|
+
steps:
|
|
20
|
+
- uses: actions/checkout@v4
|
|
21
|
+
with: { fetch-depth: 0 } # full history so gitleaks scans every commit
|
|
22
|
+
- uses: gitleaks/gitleaks-action@v2
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
name: Docs
|
|
2
|
+
on:
|
|
3
|
+
push: { branches: [master, main] }
|
|
4
|
+
workflow_dispatch:
|
|
5
|
+
permissions:
|
|
6
|
+
contents: read
|
|
7
|
+
pages: write
|
|
8
|
+
id-token: write
|
|
9
|
+
concurrency:
|
|
10
|
+
group: pages
|
|
11
|
+
cancel-in-progress: true # newer docs push supersedes an in-flight deploy; avoids Pages collisions
|
|
12
|
+
jobs:
|
|
13
|
+
build:
|
|
14
|
+
runs-on: ubuntu-latest
|
|
15
|
+
steps:
|
|
16
|
+
- uses: actions/checkout@v4
|
|
17
|
+
- uses: astral-sh/setup-uv@v5
|
|
18
|
+
- run: uv venv --python 3.12
|
|
19
|
+
- run: uv pip install -e ".[docs]"
|
|
20
|
+
- run: uv run mkdocs build --strict
|
|
21
|
+
- uses: actions/upload-pages-artifact@v3
|
|
22
|
+
with:
|
|
23
|
+
path: site
|
|
24
|
+
deploy:
|
|
25
|
+
needs: build
|
|
26
|
+
runs-on: ubuntu-latest
|
|
27
|
+
environment:
|
|
28
|
+
name: github-pages
|
|
29
|
+
url: ${{ steps.deployment.outputs.page_url }}
|
|
30
|
+
steps:
|
|
31
|
+
- id: deployment
|
|
32
|
+
uses: actions/deploy-pages@v4
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
name: Publish
|
|
2
|
+
on:
|
|
3
|
+
push:
|
|
4
|
+
tags: ["v*"]
|
|
5
|
+
permissions:
|
|
6
|
+
id-token: write # OIDC; NO PyPI token stored
|
|
7
|
+
contents: read
|
|
8
|
+
jobs:
|
|
9
|
+
build-and-publish:
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
environment: pypi
|
|
12
|
+
steps:
|
|
13
|
+
- uses: actions/checkout@v4
|
|
14
|
+
- uses: astral-sh/setup-uv@v5
|
|
15
|
+
- run: uv build
|
|
16
|
+
- uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# Python bytecode / caches
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*$py.class
|
|
5
|
+
.mypy_cache/
|
|
6
|
+
.ruff_cache/
|
|
7
|
+
.pytest_cache/
|
|
8
|
+
.hypothesis/
|
|
9
|
+
|
|
10
|
+
# Virtual environments
|
|
11
|
+
.venv/
|
|
12
|
+
venv/
|
|
13
|
+
env/
|
|
14
|
+
ENV/
|
|
15
|
+
|
|
16
|
+
# Packaging / build artifacts
|
|
17
|
+
*.egg-info/
|
|
18
|
+
*.egg
|
|
19
|
+
dist/
|
|
20
|
+
build/
|
|
21
|
+
wheels/
|
|
22
|
+
pip-wheel-metadata/
|
|
23
|
+
|
|
24
|
+
# Test / coverage output
|
|
25
|
+
.coverage
|
|
26
|
+
.coverage.*
|
|
27
|
+
htmlcov/
|
|
28
|
+
coverage.xml
|
|
29
|
+
.tox/
|
|
30
|
+
.nox/
|
|
31
|
+
|
|
32
|
+
# Docs build output
|
|
33
|
+
site/
|
|
34
|
+
|
|
35
|
+
# Secrets / environment
|
|
36
|
+
.env
|
|
37
|
+
.env.*
|
|
38
|
+
!.env.example
|
|
39
|
+
|
|
40
|
+
# Superpowers workflow scratch (task briefs, reports, review diffs, ledger)
|
|
41
|
+
.superpowers/
|
|
42
|
+
|
|
43
|
+
# OS / editor cruft
|
|
44
|
+
.DS_Store
|
|
45
|
+
Thumbs.db
|
|
46
|
+
.idea/
|
|
47
|
+
.vscode/
|
|
48
|
+
*.swp
|
|
49
|
+
*~
|
|
50
|
+
|
|
51
|
+
# Internal design/plan/spec docs (not part of the public site or repo)
|
|
52
|
+
docs/superpowers/
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# Extend gitleaks' default rules; only add an allowlist for known-safe fixtures.
|
|
2
|
+
[extend]
|
|
3
|
+
useDefault = true
|
|
4
|
+
|
|
5
|
+
# ponytail: allowlist the synthetic secrets in the recognizer test suite.
|
|
6
|
+
# These are non-credentials — AWS's own docs placeholder and a fabricated sk- string —
|
|
7
|
+
# used to verify detection. Add real exceptions here (by regex) only when equally certain.
|
|
8
|
+
[allowlist]
|
|
9
|
+
description = "Synthetic test fixtures, not real credentials"
|
|
10
|
+
regexes = [
|
|
11
|
+
'''AKIAIOSFODNN7EXAMPLE''',
|
|
12
|
+
'''sk-abcDEF0123456789abcDEF0123''',
|
|
13
|
+
]
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to spanguard will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [Unreleased]
|
|
9
|
+
|
|
10
|
+
## [0.1.0] - 2026-11-12
|
|
11
|
+
|
|
12
|
+
Initial release.
|
|
13
|
+
|
|
14
|
+
### Added
|
|
15
|
+
|
|
16
|
+
- **Entity detection** for `EMAIL` (Unicode-aware local part and domain),
|
|
17
|
+
`PHONE` (NANP-style, multiple separator conventions), `SSN`, `MRN`
|
|
18
|
+
(context-anchored on an `MRN` prefix), `DOB` (`mm/dd/yyyy` and ISO
|
|
19
|
+
`yyyy-mm-dd`), `CREDIT_CARD` (regex plus Luhn checksum validation across
|
|
20
|
+
standard printed groupings), and `API_KEY` (known-prefix formats such as
|
|
21
|
+
`sk-`, `AKIA`, `ghp_`, plus a high-entropy generic fallback).
|
|
22
|
+
- **Four redaction modes** on every `Policy`: `mask` (fixed-character
|
|
23
|
+
replacement, length-preserving), `placeholder` (`[TYPE]` tags), `hash`
|
|
24
|
+
(deterministic HMAC digest, reproducible with a shared key), and
|
|
25
|
+
`tokenize` (reversible placeholder tokens).
|
|
26
|
+
- **Reversible tokenization and reveal** — `Redactor.reveal()` restores the
|
|
27
|
+
original text from `tokenize`-mode output via an in-memory `Keyring`,
|
|
28
|
+
scoped to a single process/key.
|
|
29
|
+
- **`StreamRedactor`** for chunked/streaming input (e.g. live call
|
|
30
|
+
transcripts or log tails), with a documented split-equals-one-shot
|
|
31
|
+
guarantee: concatenating every `feed()` call's output plus a final
|
|
32
|
+
`flush()` equals exactly what a one-shot `redact()` on the full text would
|
|
33
|
+
produce, even when an entity straddles a chunk boundary. Enforces a
|
|
34
|
+
minimum `tail` buffer of 64 characters to prevent sub-threshold entity
|
|
35
|
+
leakage.
|
|
36
|
+
- **Optional NER extra** (`pip install spanguard[ner]`) adding a
|
|
37
|
+
spaCy-backed `SpacyRecognizer` for `PERSON` and `LOCATION` detection,
|
|
38
|
+
imported lazily so the core install stays dependency-light and fast to
|
|
39
|
+
import.
|
|
40
|
+
- **`spanguard` CLI** with `scan` (report entity-type counts only, never raw
|
|
41
|
+
values) and `redact` (write redacted text to stdout or a file, with an
|
|
42
|
+
optional `--audit-json` audit record on stderr) subcommands.
|
|
43
|
+
`-` reads stdin as UTF-8. An unknown `--entities` name, an unreadable,
|
|
44
|
+
undecodable or missing input (including closed stdin), or an unwritable
|
|
45
|
+
`--out` path exits with code 2 and a one-line `error:` message that names
|
|
46
|
+
only the path or label, never input content.
|
|
47
|
+
- **Unknown-entity warning**: `Redactor` warns (`UserWarning`, entity labels
|
|
48
|
+
only) when `Policy.entities` names a type with no recognizer, such as
|
|
49
|
+
`PERSON` without `SpacyRecognizer` or a typo like `EMIAL`, instead of
|
|
50
|
+
silently detecting nothing for it.
|
|
51
|
+
- **`StreamRedactor(max_buffer=N)`** and `StreamBufferFullError`: an opt-in
|
|
52
|
+
cap on held-back text for streams that never offer a safe split point.
|
|
53
|
+
Uncapped by default.
|
|
54
|
+
- **Leak-free audit records** — every `RedactionResult.audit` contains only
|
|
55
|
+
entity-type counts, a policy hash, and a timestamp, never a raw matched
|
|
56
|
+
value or span text; this guarantee is enforced directly by the test suite
|
|
57
|
+
(`tests/test_audit_safety.py`), not just documented.
|
|
58
|
+
- **Pluggable `Recognizer` protocol** for adding custom, domain-specific
|
|
59
|
+
detectors without forking the library.
|
|
60
|
+
- Full documentation site (mkdocs-material) covering quickstart, streaming,
|
|
61
|
+
the CLI, recognizers, and an honest known-limitations page, published to
|
|
62
|
+
[clufy.github.io/spanguard](https://clufy.github.io/spanguard).
|
|
63
|
+
- CI pipeline (ruff, mypy `--strict`, pytest with a 90% coverage gate) on
|
|
64
|
+
every push and pull request; 128 tests at 97.92% coverage as of this
|
|
65
|
+
release. Published to PyPI from GitHub Actions via OIDC Trusted
|
|
66
|
+
Publishing on `v*` tags; no PyPI token is stored anywhere.
|
|
67
|
+
|
|
68
|
+
### Performance
|
|
69
|
+
|
|
70
|
+
Measured against the July 2026 pre-release build. Redaction output is
|
|
71
|
+
byte-identical; each change is pinned by a differential test against the
|
|
72
|
+
earlier algorithm.
|
|
73
|
+
|
|
74
|
+
| Path | Pre-release | 0.1.0 |
|
|
75
|
+
|---|---|---|
|
|
76
|
+
| `Redactor.redact`, 40k spans | 84.6 s | 0.84 s |
|
|
77
|
+
| `EmailRecognizer`, 8k-char token with no spaces | 844 ms (quadratic) | 0.6 ms (linear) |
|
|
78
|
+
| `Keyring.reveal`, 30k issued tokens | 11.2 s | 44 ms |
|
|
79
|
+
| `StreamRedactor`, 200 KB in 4 KB chunks | 304 ms | 194 ms |
|
|
80
|
+
| `StreamRedactor`, 1 MB with no whitespace | quadratic | linear |
|
|
81
|
+
| `StreamRedactor`, 200 KB whose only whitespace is inside a phone number | 8.49 s | 0.08 s |
|
|
82
|
+
| `benchmarks/throughput.py` (1 MB corpus) | 1.54 MB/s | 2.15 MB/s |
|
|
83
|
+
|
|
84
|
+
Overlap resolution is O(n log n), email detection cannot backtrack
|
|
85
|
+
quadratically on long whitespace-free runs (a denial-of-service vector),
|
|
86
|
+
`Keyring.reveal` is linear in the text, and `StreamRedactor` never re-detects
|
|
87
|
+
text it has already verified or split points it has already tried.
|
|
88
|
+
|
|
89
|
+
[unreleased]: https://github.com/Clufy/spanguard/compare/v0.1.0...HEAD
|
|
90
|
+
[0.1.0]: https://github.com/Clufy/spanguard/releases/tag/v0.1.0
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
cff-version: 1.2.0
|
|
2
|
+
message: "If you use spanguard in your work, please cite it using the metadata below."
|
|
3
|
+
type: software
|
|
4
|
+
title: spanguard
|
|
5
|
+
abstract: >-
|
|
6
|
+
Detect and redact PII/PHI in text and streaming transcripts, with leak-free
|
|
7
|
+
audit records, reversible tokenization, an optional NER extra, and a CLI.
|
|
8
|
+
authors:
|
|
9
|
+
- name: "spanguard authors"
|
|
10
|
+
alias: "Clufy"
|
|
11
|
+
repository-code: "https://github.com/Clufy/spanguard"
|
|
12
|
+
url: "https://clufy.github.io/spanguard"
|
|
13
|
+
license: MIT
|
|
14
|
+
version: 0.1.0
|
|
15
|
+
date-released: "2026-07-06"
|
|
16
|
+
keywords:
|
|
17
|
+
- pii
|
|
18
|
+
- phi
|
|
19
|
+
- redaction
|
|
20
|
+
- privacy
|
|
21
|
+
- compliance
|
|
22
|
+
- nlp
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
# Contributor Covenant Code of Conduct
|
|
2
|
+
|
|
3
|
+
## Our Pledge
|
|
4
|
+
|
|
5
|
+
We as members, contributors, and leaders pledge to make participation in our
|
|
6
|
+
community a harassment-free experience for everyone, regardless of age, body
|
|
7
|
+
size, visible or invisible disability, ethnicity, sex characteristics, gender
|
|
8
|
+
identity and expression, level of experience, education, socio-economic
|
|
9
|
+
status, nationality, personal appearance, race, caste, color, religion, or
|
|
10
|
+
sexual identity and orientation.
|
|
11
|
+
|
|
12
|
+
We pledge to act and interact in ways that contribute to an open, welcoming,
|
|
13
|
+
diverse, inclusive, and healthy community.
|
|
14
|
+
|
|
15
|
+
## Our Standards
|
|
16
|
+
|
|
17
|
+
Examples of behavior that contributes to a positive environment for our
|
|
18
|
+
community include:
|
|
19
|
+
|
|
20
|
+
* Demonstrating empathy and kindness toward other people
|
|
21
|
+
* Being respectful of differing opinions, viewpoints, and experiences
|
|
22
|
+
* Giving and gracefully accepting constructive feedback
|
|
23
|
+
* Accepting responsibility and apologizing to those affected by our mistakes,
|
|
24
|
+
and learning from the experience
|
|
25
|
+
* Focusing on what is best not just for us as individuals, but for the
|
|
26
|
+
overall community
|
|
27
|
+
|
|
28
|
+
Examples of unacceptable behavior include:
|
|
29
|
+
|
|
30
|
+
* The use of sexualized language or imagery, and sexual attention or advances
|
|
31
|
+
of any kind
|
|
32
|
+
* Trolling, insulting or derogatory comments, and personal or political
|
|
33
|
+
attacks
|
|
34
|
+
* Public or private harassment
|
|
35
|
+
* Publishing others' private information, such as a physical or email
|
|
36
|
+
address, without their explicit permission
|
|
37
|
+
* Other conduct which could reasonably be considered inappropriate in a
|
|
38
|
+
professional setting
|
|
39
|
+
|
|
40
|
+
## Enforcement Responsibilities
|
|
41
|
+
|
|
42
|
+
Community leaders are responsible for clarifying and enforcing our standards
|
|
43
|
+
of acceptable behavior and will take appropriate and fair corrective action
|
|
44
|
+
in response to any behavior that they deem inappropriate, threatening,
|
|
45
|
+
offensive, or harmful.
|
|
46
|
+
|
|
47
|
+
Community leaders have the right and responsibility to remove, edit, or
|
|
48
|
+
reject comments, commits, code, wiki edits, issues, and other contributions
|
|
49
|
+
that are not aligned to this Code of Conduct, and will communicate reasons
|
|
50
|
+
for moderation decisions when appropriate.
|
|
51
|
+
|
|
52
|
+
## Scope
|
|
53
|
+
|
|
54
|
+
This Code of Conduct applies within all community spaces, and also applies
|
|
55
|
+
when an individual is officially representing the community in public
|
|
56
|
+
spaces. Examples of representing our community include using an official
|
|
57
|
+
e-mail address, posting via an official social media account, or acting as
|
|
58
|
+
an appointed representative at an online or offline event.
|
|
59
|
+
|
|
60
|
+
## Enforcement
|
|
61
|
+
|
|
62
|
+
spanguard does not currently publish a dedicated enforcement email address.
|
|
63
|
+
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
|
64
|
+
reported by:
|
|
65
|
+
|
|
66
|
+
* Opening a private [Security Advisory](https://github.com/Clufy/spanguard/security/advisories/new)
|
|
67
|
+
on the repository (via the **Security** tab → **Report a vulnerability**),
|
|
68
|
+
which is visible only to the maintainer and is not public — this mechanism
|
|
69
|
+
works for conduct reports as well as security reports; or
|
|
70
|
+
* Contacting the maintainer directly via their GitHub account,
|
|
71
|
+
[@Clufy](https://github.com/Clufy).
|
|
72
|
+
|
|
73
|
+
All complaints will be reviewed and investigated promptly and fairly.
|
|
74
|
+
|
|
75
|
+
All community leaders are obligated to respect the privacy and security of
|
|
76
|
+
the reporter of any incident.
|
|
77
|
+
|
|
78
|
+
## Enforcement Guidelines
|
|
79
|
+
|
|
80
|
+
Community leaders will follow these Community Impact Guidelines in
|
|
81
|
+
determining the consequences for any action they deem in violation of this
|
|
82
|
+
Code of Conduct:
|
|
83
|
+
|
|
84
|
+
### 1. Correction
|
|
85
|
+
|
|
86
|
+
**Community Impact**: Use of inappropriate language or other behavior deemed
|
|
87
|
+
unprofessional or unwelcome in the community.
|
|
88
|
+
|
|
89
|
+
**Consequence**: A private, written warning from community leaders,
|
|
90
|
+
providing clarity around the nature of the violation and an explanation of
|
|
91
|
+
why the behavior was inappropriate. A public apology may be requested.
|
|
92
|
+
|
|
93
|
+
### 2. Warning
|
|
94
|
+
|
|
95
|
+
**Community Impact**: A violation through a single incident or series of
|
|
96
|
+
actions.
|
|
97
|
+
|
|
98
|
+
**Consequence**: A warning with consequences for continued behavior. No
|
|
99
|
+
interaction with the people involved, including unsolicited interaction with
|
|
100
|
+
those enforcing the Code of Conduct, for a specified period of time. This
|
|
101
|
+
includes avoiding interactions in community spaces as well as external
|
|
102
|
+
channels like social media. Violating these terms may lead to a temporary or
|
|
103
|
+
permanent ban.
|
|
104
|
+
|
|
105
|
+
### 3. Temporary Ban
|
|
106
|
+
|
|
107
|
+
**Community Impact**: A serious violation of community standards, including
|
|
108
|
+
sustained inappropriate behavior.
|
|
109
|
+
|
|
110
|
+
**Consequence**: A temporary ban from any sort of interaction or public
|
|
111
|
+
communication with the community for a specified period of time. No public
|
|
112
|
+
or private interaction with the people involved, including unsolicited
|
|
113
|
+
interaction with those enforcing the Code of Conduct, is allowed during this
|
|
114
|
+
period. Violating these terms may lead to a permanent ban.
|
|
115
|
+
|
|
116
|
+
### 4. Permanent Ban
|
|
117
|
+
|
|
118
|
+
**Community Impact**: Demonstrating a pattern of violation of community
|
|
119
|
+
standards, including sustained inappropriate behavior, harassment of an
|
|
120
|
+
individual, or aggression toward or disparagement of classes of individuals.
|
|
121
|
+
|
|
122
|
+
**Consequence**: A permanent ban from any sort of public interaction within
|
|
123
|
+
the community.
|
|
124
|
+
|
|
125
|
+
## Attribution
|
|
126
|
+
|
|
127
|
+
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
|
|
128
|
+
version 2.1, available at
|
|
129
|
+
[https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1].
|
|
130
|
+
|
|
131
|
+
Community Impact Guidelines were inspired by
|
|
132
|
+
[Mozilla's code of conduct enforcement ladder][Mozilla CoC].
|
|
133
|
+
|
|
134
|
+
For answers to common questions about this code of conduct, see the FAQ at
|
|
135
|
+
[https://www.contributor-covenant.org/faq][FAQ]. Translations are available
|
|
136
|
+
at [https://www.contributor-covenant.org/translations][translations].
|
|
137
|
+
|
|
138
|
+
[homepage]: https://www.contributor-covenant.org
|
|
139
|
+
[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html
|
|
140
|
+
[Mozilla CoC]: https://github.com/mozilla/diversity
|
|
141
|
+
[FAQ]: https://www.contributor-covenant.org/faq
|
|
142
|
+
[translations]: https://www.contributor-covenant.org/translations
|