mixin-sensitivity 0.4.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.
- mixin_sensitivity-0.4.0/.github/workflows/ci.yml +45 -0
- mixin_sensitivity-0.4.0/.github/workflows/cleanup-guard.yml +30 -0
- mixin_sensitivity-0.4.0/.github/workflows/dto-strict.yml +35 -0
- mixin_sensitivity-0.4.0/.github/workflows/no-ai-attribution.yml +73 -0
- mixin_sensitivity-0.4.0/.github/workflows/publish.yml +33 -0
- mixin_sensitivity-0.4.0/.github/workflows/ruff.yml +29 -0
- mixin_sensitivity-0.4.0/.gitignore +131 -0
- mixin_sensitivity-0.4.0/CHANGELOG.md +167 -0
- mixin_sensitivity-0.4.0/CODE_OF_CONDUCT.md +31 -0
- mixin_sensitivity-0.4.0/CONTRIBUTING.md +65 -0
- mixin_sensitivity-0.4.0/LICENSE +201 -0
- mixin_sensitivity-0.4.0/PKG-INFO +472 -0
- mixin_sensitivity-0.4.0/README.md +448 -0
- mixin_sensitivity-0.4.0/SECURITY.md +20 -0
- mixin_sensitivity-0.4.0/docs/STATUS.md +55 -0
- mixin_sensitivity-0.4.0/docs/apps/decorators/compliance.md +98 -0
- mixin_sensitivity-0.4.0/docs/apps/decorators/policies.md +293 -0
- mixin_sensitivity-0.4.0/docs/apps/decorators/sensitive.md +238 -0
- mixin_sensitivity-0.4.0/docs/apps/diagrams.md +195 -0
- mixin_sensitivity-0.4.0/docs/architecture/architecture.md +246 -0
- mixin_sensitivity-0.4.0/docs/audits/.gitkeep +0 -0
- mixin_sensitivity-0.4.0/docs/audits/2026-06-09-decorator-layer-security-audit.md +194 -0
- mixin_sensitivity-0.4.0/docs/reviews/.gitkeep +0 -0
- mixin_sensitivity-0.4.0/docs/reviews/2026-06-09-decorator-layer-review.md +256 -0
- mixin_sensitivity-0.4.0/mixin_sensitivity/__init__.py +35 -0
- mixin_sensitivity-0.4.0/mixin_sensitivity/common/__init__.py +1 -0
- mixin_sensitivity-0.4.0/mixin_sensitivity/common/constants/__init__.py +1 -0
- mixin_sensitivity-0.4.0/mixin_sensitivity/common/constants/metadata.py +9 -0
- mixin_sensitivity-0.4.0/mixin_sensitivity/config/__init__.py +1 -0
- mixin_sensitivity-0.4.0/mixin_sensitivity/config/_version.py +3 -0
- mixin_sensitivity-0.4.0/mixin_sensitivity/decorators/__init__.py +1 -0
- mixin_sensitivity-0.4.0/mixin_sensitivity/decorators/classes/__init__.py +1 -0
- mixin_sensitivity-0.4.0/mixin_sensitivity/decorators/classes/compliance/__init__.py +8 -0
- mixin_sensitivity-0.4.0/mixin_sensitivity/decorators/classes/compliance/compliance_objects.py +27 -0
- mixin_sensitivity-0.4.0/mixin_sensitivity/decorators/classes/pci_aware/__init__.py +7 -0
- mixin_sensitivity-0.4.0/mixin_sensitivity/decorators/classes/pci_aware/pci_aware_objects.py +25 -0
- mixin_sensitivity-0.4.0/mixin_sensitivity/decorators/classes/phi_aware/__init__.py +7 -0
- mixin_sensitivity-0.4.0/mixin_sensitivity/decorators/classes/phi_aware/phi_aware_objects.py +25 -0
- mixin_sensitivity-0.4.0/mixin_sensitivity/decorators/classes/pii_aware/__init__.py +7 -0
- mixin_sensitivity-0.4.0/mixin_sensitivity/decorators/classes/pii_aware/pii_aware_objects.py +25 -0
- mixin_sensitivity-0.4.0/mixin_sensitivity/decorators/classes/secret_aware/__init__.py +7 -0
- mixin_sensitivity-0.4.0/mixin_sensitivity/decorators/classes/secret_aware/secret_aware_objects.py +25 -0
- mixin_sensitivity-0.4.0/mixin_sensitivity/decorators/constants/__init__.py +6 -0
- mixin_sensitivity-0.4.0/mixin_sensitivity/decorators/constants/sensitive.py +20 -0
- mixin_sensitivity-0.4.0/mixin_sensitivity/decorators/sensitive/__init__.py +9 -0
- mixin_sensitivity-0.4.0/mixin_sensitivity/decorators/sensitive/sensitive_client.py +47 -0
- mixin_sensitivity-0.4.0/mixin_sensitivity/decorators/sensitive/sensitive_objects.py +54 -0
- mixin_sensitivity-0.4.0/mixin_sensitivity/decorators/tests/__init__.py +1 -0
- mixin_sensitivity-0.4.0/mixin_sensitivity/decorators/tests/test_compliance/__init__.py +1 -0
- mixin_sensitivity-0.4.0/mixin_sensitivity/decorators/tests/test_compliance/test_compliance_objects.py +80 -0
- mixin_sensitivity-0.4.0/mixin_sensitivity/decorators/tests/test_pci_aware/__init__.py +1 -0
- mixin_sensitivity-0.4.0/mixin_sensitivity/decorators/tests/test_pci_aware/conftest.py +40 -0
- mixin_sensitivity-0.4.0/mixin_sensitivity/decorators/tests/test_pci_aware/test_pci_aware_objects.py +142 -0
- mixin_sensitivity-0.4.0/mixin_sensitivity/decorators/tests/test_phi_aware/__init__.py +1 -0
- mixin_sensitivity-0.4.0/mixin_sensitivity/decorators/tests/test_phi_aware/conftest.py +40 -0
- mixin_sensitivity-0.4.0/mixin_sensitivity/decorators/tests/test_phi_aware/test_phi_aware_objects.py +159 -0
- mixin_sensitivity-0.4.0/mixin_sensitivity/decorators/tests/test_pii_aware/__init__.py +1 -0
- mixin_sensitivity-0.4.0/mixin_sensitivity/decorators/tests/test_pii_aware/conftest.py +40 -0
- mixin_sensitivity-0.4.0/mixin_sensitivity/decorators/tests/test_pii_aware/test_pii_aware_objects.py +152 -0
- mixin_sensitivity-0.4.0/mixin_sensitivity/decorators/tests/test_secret_aware/__init__.py +1 -0
- mixin_sensitivity-0.4.0/mixin_sensitivity/decorators/tests/test_secret_aware/conftest.py +40 -0
- mixin_sensitivity-0.4.0/mixin_sensitivity/decorators/tests/test_secret_aware/test_secret_aware_objects.py +154 -0
- mixin_sensitivity-0.4.0/mixin_sensitivity/decorators/tests/test_sensitive/__init__.py +1 -0
- mixin_sensitivity-0.4.0/mixin_sensitivity/decorators/tests/test_sensitive/conftest.py +160 -0
- mixin_sensitivity-0.4.0/mixin_sensitivity/decorators/tests/test_sensitive/test_sensitive_client.py +386 -0
- mixin_sensitivity-0.4.0/mixin_sensitivity/decorators/tests/test_sensitive/test_sensitive_objects.py +280 -0
- mixin_sensitivity-0.4.0/mixin_sensitivity/py.typed +0 -0
- mixin_sensitivity-0.4.0/mixin_sensitivity/services/__init__.py +1 -0
- mixin_sensitivity-0.4.0/mixin_sensitivity/services/classify/__init__.py +10 -0
- mixin_sensitivity-0.4.0/mixin_sensitivity/services/classify/classify_objects.py +57 -0
- mixin_sensitivity-0.4.0/mixin_sensitivity/services/tests/__init__.py +1 -0
- mixin_sensitivity-0.4.0/mixin_sensitivity/services/tests/test_classify/__init__.py +1 -0
- mixin_sensitivity-0.4.0/mixin_sensitivity/services/tests/test_classify/conftest.py +39 -0
- mixin_sensitivity-0.4.0/mixin_sensitivity/services/tests/test_classify/test_classify_objects.py +224 -0
- mixin_sensitivity-0.4.0/pyproject.toml +90 -0
- mixin_sensitivity-0.4.0/uv.lock +478 -0
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
pull_request:
|
|
5
|
+
push:
|
|
6
|
+
branches: [main]
|
|
7
|
+
|
|
8
|
+
concurrency:
|
|
9
|
+
group: ${{ github.workflow }}-${{ github.ref }}
|
|
10
|
+
cancel-in-progress: true
|
|
11
|
+
|
|
12
|
+
permissions:
|
|
13
|
+
contents: read
|
|
14
|
+
|
|
15
|
+
jobs:
|
|
16
|
+
test:
|
|
17
|
+
runs-on: ubuntu-latest
|
|
18
|
+
strategy:
|
|
19
|
+
matrix:
|
|
20
|
+
python-version: ["3.11", "3.12"]
|
|
21
|
+
steps:
|
|
22
|
+
- uses: actions/checkout@v4
|
|
23
|
+
- uses: astral-sh/setup-uv@v4
|
|
24
|
+
with:
|
|
25
|
+
enable-cache: true
|
|
26
|
+
- name: Install Python
|
|
27
|
+
run: uv python install ${{ matrix.python-version }}
|
|
28
|
+
- name: Sync dependencies
|
|
29
|
+
run: uv sync --all-extras
|
|
30
|
+
- name: Test
|
|
31
|
+
run: uv run pytest -v --cov --cov-fail-under=95
|
|
32
|
+
|
|
33
|
+
mypy:
|
|
34
|
+
runs-on: ubuntu-latest
|
|
35
|
+
steps:
|
|
36
|
+
- uses: actions/checkout@v4
|
|
37
|
+
- uses: astral-sh/setup-uv@v4
|
|
38
|
+
with:
|
|
39
|
+
enable-cache: true
|
|
40
|
+
- name: Install Python 3.12
|
|
41
|
+
run: uv python install 3.12
|
|
42
|
+
- name: Sync dependencies
|
|
43
|
+
run: uv sync --all-extras
|
|
44
|
+
- name: Type-check
|
|
45
|
+
run: uv run mypy mixin_sensitivity/
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
name: Cleanup Guard
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main, "refactor/*", "feat/*"]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [main, "refactor/*", "feat/*"]
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
cleanup-guard:
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
steps:
|
|
13
|
+
- uses: actions/checkout@v4
|
|
14
|
+
- name: Check audit and review artifacts
|
|
15
|
+
run: |
|
|
16
|
+
audit_count=$(find docs/audits -maxdepth 1 -name "*.md" -type f | wc -l)
|
|
17
|
+
review_count=$(find docs/reviews -maxdepth 1 -name "*.md" -type f | wc -l)
|
|
18
|
+
|
|
19
|
+
if [ "$audit_count" -eq 0 ]; then
|
|
20
|
+
echo "❌ FAIL: docs/audits/ contains no .md files (expected security audit)"
|
|
21
|
+
exit 1
|
|
22
|
+
fi
|
|
23
|
+
|
|
24
|
+
if [ "$review_count" -eq 0 ]; then
|
|
25
|
+
echo "❌ FAIL: docs/reviews/ contains no .md files (expected code review)"
|
|
26
|
+
exit 1
|
|
27
|
+
fi
|
|
28
|
+
|
|
29
|
+
echo "✓ Audit count: $audit_count"
|
|
30
|
+
echo "✓ Review count: $review_count"
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
name: dto-strict
|
|
2
|
+
|
|
3
|
+
# DTO-discipline lint (frozen+slots, facade-ban, R001-R008 rules).
|
|
4
|
+
# Lint step: whole package (native test-file exemption via R007/R008 rules).
|
|
5
|
+
# LOC-cap step: source dirs only (test density exempt from cap per review record).
|
|
6
|
+
|
|
7
|
+
on:
|
|
8
|
+
pull_request:
|
|
9
|
+
push:
|
|
10
|
+
branches: [main]
|
|
11
|
+
|
|
12
|
+
concurrency:
|
|
13
|
+
group: ${{ github.workflow }}-${{ github.ref }}
|
|
14
|
+
cancel-in-progress: true
|
|
15
|
+
|
|
16
|
+
permissions:
|
|
17
|
+
contents: read
|
|
18
|
+
|
|
19
|
+
jobs:
|
|
20
|
+
lint:
|
|
21
|
+
runs-on: ubuntu-latest
|
|
22
|
+
steps:
|
|
23
|
+
- uses: actions/checkout@v4
|
|
24
|
+
- uses: astral-sh/setup-uv@v4
|
|
25
|
+
with:
|
|
26
|
+
enable-cache: true
|
|
27
|
+
- name: Install Python 3.12
|
|
28
|
+
run: uv python install 3.12
|
|
29
|
+
- name: Run dto-strict
|
|
30
|
+
run: uvx --python 3.12 dto-strict@0.4.0 mixin_sensitivity/ --format github
|
|
31
|
+
- name: Check LOC cap
|
|
32
|
+
run: |
|
|
33
|
+
for dir in mixin_sensitivity/common mixin_sensitivity/config mixin_sensitivity/decorators/classes mixin_sensitivity/decorators/constants mixin_sensitivity/decorators/sensitive mixin_sensitivity/services/classify; do
|
|
34
|
+
uvx --python 3.12 dto-strict@0.4.0 loc-cap "$dir"
|
|
35
|
+
done
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
name: Attribution Gate
|
|
2
|
+
|
|
3
|
+
# Server-side enforcement: rejects pushes/PRs whose commit messages contain
|
|
4
|
+
# Claude/Anthropic attribution. Catches GitHub squash-merge propagation that
|
|
5
|
+
# bypasses local commit-msg, pre-push, and PreToolUse hooks.
|
|
6
|
+
#
|
|
7
|
+
# Per-repo copy of the canonical attribution gate.
|
|
8
|
+
|
|
9
|
+
on:
|
|
10
|
+
push:
|
|
11
|
+
pull_request:
|
|
12
|
+
|
|
13
|
+
permissions:
|
|
14
|
+
contents: read
|
|
15
|
+
pull-requests: read
|
|
16
|
+
|
|
17
|
+
jobs:
|
|
18
|
+
scan:
|
|
19
|
+
name: Scan commit messages for attribution
|
|
20
|
+
runs-on: ubuntu-latest
|
|
21
|
+
steps:
|
|
22
|
+
- name: Checkout (full history)
|
|
23
|
+
uses: actions/checkout@v4
|
|
24
|
+
with:
|
|
25
|
+
fetch-depth: 0
|
|
26
|
+
|
|
27
|
+
- name: Determine commit range
|
|
28
|
+
id: range
|
|
29
|
+
env:
|
|
30
|
+
EVENT_NAME: ${{ github.event_name }}
|
|
31
|
+
PR_BASE: ${{ github.event.pull_request.base.sha }}
|
|
32
|
+
PR_HEAD: ${{ github.event.pull_request.head.sha }}
|
|
33
|
+
PUSH_BEFORE: ${{ github.event.before }}
|
|
34
|
+
PUSH_AFTER: ${{ github.event.after }}
|
|
35
|
+
run: |
|
|
36
|
+
set -euo pipefail
|
|
37
|
+
if [ "$EVENT_NAME" = "pull_request" ]; then
|
|
38
|
+
BASE="$PR_BASE"
|
|
39
|
+
HEAD="$PR_HEAD"
|
|
40
|
+
else
|
|
41
|
+
BASE="$PUSH_BEFORE"
|
|
42
|
+
HEAD="$PUSH_AFTER"
|
|
43
|
+
if [ "$BASE" = "0000000000000000000000000000000000000000" ] || [ -z "$BASE" ]; then
|
|
44
|
+
git fetch origin main:refs/remotes/origin/main 2>/dev/null || true
|
|
45
|
+
BASE=$(git merge-base origin/main "$HEAD" 2>/dev/null || git rev-list --max-parents=0 "$HEAD" | tail -1)
|
|
46
|
+
fi
|
|
47
|
+
fi
|
|
48
|
+
echo "base=$BASE" >> "$GITHUB_OUTPUT"
|
|
49
|
+
echo "head=$HEAD" >> "$GITHUB_OUTPUT"
|
|
50
|
+
|
|
51
|
+
- name: Scan for Claude/Anthropic attribution
|
|
52
|
+
env:
|
|
53
|
+
BASE: ${{ steps.range.outputs.base }}
|
|
54
|
+
HEAD: ${{ steps.range.outputs.head }}
|
|
55
|
+
PATTERN: 'co-authored-by.*(claude|anthropic)|generated with claude|@claude|noreply@anthropic|🤖'
|
|
56
|
+
run: |
|
|
57
|
+
set -uo pipefail
|
|
58
|
+
OFFENDING=""
|
|
59
|
+
for sha in $(git rev-list "$BASE..$HEAD"); do
|
|
60
|
+
MSG=$(git log -1 --pretty=format:"%B" "$sha")
|
|
61
|
+
if echo "$MSG" | grep -qiE "$PATTERN"; then
|
|
62
|
+
AUTHOR=$(git log -1 --pretty=format:"%an" "$sha")
|
|
63
|
+
SUBJECT=$(git log -1 --pretty=format:"%s" "$sha")
|
|
64
|
+
OFFENDING="${OFFENDING} ${sha:0:8} | author=${AUTHOR} | ${SUBJECT}"$'\n'
|
|
65
|
+
fi
|
|
66
|
+
done
|
|
67
|
+
if [ -n "$OFFENDING" ]; then
|
|
68
|
+
echo "::error::Claude/Anthropic attribution detected in commit messages"
|
|
69
|
+
echo "Offending commits in range $BASE..$HEAD:"
|
|
70
|
+
printf '%s' "$OFFENDING"
|
|
71
|
+
exit 1
|
|
72
|
+
fi
|
|
73
|
+
echo "OK: no attribution detected in $BASE..$HEAD"
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
name: Publish to PyPI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
release:
|
|
5
|
+
types: [published]
|
|
6
|
+
|
|
7
|
+
jobs:
|
|
8
|
+
publish:
|
|
9
|
+
name: Build + publish
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
environment: pypi # second-layer GitHub deployment-environment gate
|
|
12
|
+
permissions:
|
|
13
|
+
id-token: write # OIDC trusted publisher
|
|
14
|
+
contents: read
|
|
15
|
+
|
|
16
|
+
steps:
|
|
17
|
+
- name: Checkout
|
|
18
|
+
uses: actions/checkout@v4
|
|
19
|
+
|
|
20
|
+
- name: Set up Python
|
|
21
|
+
uses: actions/setup-python@v5
|
|
22
|
+
with:
|
|
23
|
+
python-version: '3.12'
|
|
24
|
+
|
|
25
|
+
- name: Install build deps
|
|
26
|
+
run: python -m pip install --upgrade build
|
|
27
|
+
|
|
28
|
+
- name: Build sdist + wheel
|
|
29
|
+
run: python -m build
|
|
30
|
+
|
|
31
|
+
- name: Publish via OIDC
|
|
32
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
33
|
+
# No password/token — OIDC trusted publisher does the auth
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
name: Ruff
|
|
2
|
+
|
|
3
|
+
# Ruff lint + format check. Pinned to 0.15.14 to match local version.
|
|
4
|
+
# Format drift was previously undetected; this gate prevents future drift.
|
|
5
|
+
|
|
6
|
+
on:
|
|
7
|
+
pull_request:
|
|
8
|
+
push:
|
|
9
|
+
branches: [main]
|
|
10
|
+
|
|
11
|
+
concurrency:
|
|
12
|
+
group: ${{ github.workflow }}-${{ github.ref }}
|
|
13
|
+
cancel-in-progress: true
|
|
14
|
+
|
|
15
|
+
permissions:
|
|
16
|
+
contents: read
|
|
17
|
+
|
|
18
|
+
jobs:
|
|
19
|
+
ruff:
|
|
20
|
+
runs-on: ubuntu-latest
|
|
21
|
+
steps:
|
|
22
|
+
- uses: actions/checkout@v4
|
|
23
|
+
- uses: astral-sh/setup-uv@v4
|
|
24
|
+
with:
|
|
25
|
+
enable-cache: true
|
|
26
|
+
- name: Ruff lint
|
|
27
|
+
run: uvx ruff@0.15.14 check mixin_sensitivity/
|
|
28
|
+
- name: Ruff format check
|
|
29
|
+
run: uvx ruff@0.15.14 format --check mixin_sensitivity/
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
# Byte-compiled / optimized / DLL files
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*$py.class
|
|
5
|
+
|
|
6
|
+
# C extensions
|
|
7
|
+
*.so
|
|
8
|
+
|
|
9
|
+
# Distribution / packaging
|
|
10
|
+
.Python
|
|
11
|
+
build/
|
|
12
|
+
develop-eggs/
|
|
13
|
+
dist/
|
|
14
|
+
downloads/
|
|
15
|
+
eggs/
|
|
16
|
+
.eggs/
|
|
17
|
+
lib/
|
|
18
|
+
lib64/
|
|
19
|
+
parts/
|
|
20
|
+
sdist/
|
|
21
|
+
var/
|
|
22
|
+
wheels/
|
|
23
|
+
pip-wheel-metadata/
|
|
24
|
+
share/python-wheels/
|
|
25
|
+
*.egg-info/
|
|
26
|
+
.installed.cfg
|
|
27
|
+
*.egg
|
|
28
|
+
MANIFEST
|
|
29
|
+
|
|
30
|
+
# PyInstaller
|
|
31
|
+
*.manifest
|
|
32
|
+
*.spec
|
|
33
|
+
|
|
34
|
+
# Installer logs
|
|
35
|
+
pip-log.txt
|
|
36
|
+
pip-delete-this-directory.txt
|
|
37
|
+
|
|
38
|
+
# Unit test / coverage reports
|
|
39
|
+
htmlcov/
|
|
40
|
+
.tox/
|
|
41
|
+
.nox/
|
|
42
|
+
.coverage
|
|
43
|
+
.coverage.*
|
|
44
|
+
.cache
|
|
45
|
+
nosetests.xml
|
|
46
|
+
coverage.xml
|
|
47
|
+
*.cover
|
|
48
|
+
*.py,cover
|
|
49
|
+
.hypothesis/
|
|
50
|
+
.pytest_cache/
|
|
51
|
+
|
|
52
|
+
# Translations
|
|
53
|
+
*.mo
|
|
54
|
+
*.pot
|
|
55
|
+
|
|
56
|
+
# Django stuff:
|
|
57
|
+
*.log
|
|
58
|
+
local_settings.py
|
|
59
|
+
db.sqlite3
|
|
60
|
+
db.sqlite3-journal
|
|
61
|
+
|
|
62
|
+
# Flask stuff:
|
|
63
|
+
instance/
|
|
64
|
+
.webassets-cache
|
|
65
|
+
|
|
66
|
+
# Scrapy stuff:
|
|
67
|
+
.scrapy
|
|
68
|
+
|
|
69
|
+
# Sphinx documentation
|
|
70
|
+
docs/_build/
|
|
71
|
+
|
|
72
|
+
# PyBuilder
|
|
73
|
+
target/
|
|
74
|
+
|
|
75
|
+
# Jupyter Notebook
|
|
76
|
+
.ipynb_checkpoints
|
|
77
|
+
|
|
78
|
+
# IPython
|
|
79
|
+
profile_default/
|
|
80
|
+
ipython_config.py
|
|
81
|
+
|
|
82
|
+
# pyenv
|
|
83
|
+
.python-version
|
|
84
|
+
|
|
85
|
+
# pipenv
|
|
86
|
+
Pipfile.lock
|
|
87
|
+
|
|
88
|
+
# PEP 582
|
|
89
|
+
__pypackages__/
|
|
90
|
+
|
|
91
|
+
# Celery stuff
|
|
92
|
+
celerybeat-schedule
|
|
93
|
+
celerybeat.pid
|
|
94
|
+
|
|
95
|
+
# SageMath parsed files
|
|
96
|
+
*.sage.py
|
|
97
|
+
|
|
98
|
+
# Environments
|
|
99
|
+
.env
|
|
100
|
+
.venv
|
|
101
|
+
env/
|
|
102
|
+
venv/
|
|
103
|
+
ENV/
|
|
104
|
+
env.bak/
|
|
105
|
+
venv.bak/
|
|
106
|
+
|
|
107
|
+
# Spyder project settings
|
|
108
|
+
.spyderproject
|
|
109
|
+
.spyproject
|
|
110
|
+
|
|
111
|
+
# Rope project settings
|
|
112
|
+
.ropeproject
|
|
113
|
+
|
|
114
|
+
# mkdocs documentation
|
|
115
|
+
/site
|
|
116
|
+
|
|
117
|
+
# mypy
|
|
118
|
+
.mypy_cache/
|
|
119
|
+
.dmypy.json
|
|
120
|
+
dmypy.json
|
|
121
|
+
|
|
122
|
+
# Pyre type checker
|
|
123
|
+
.pyre/
|
|
124
|
+
|
|
125
|
+
# IDE
|
|
126
|
+
.vscode/
|
|
127
|
+
.idea/
|
|
128
|
+
*.swp
|
|
129
|
+
*.swo
|
|
130
|
+
*~
|
|
131
|
+
.DS_Store
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [Unreleased]
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
### Changed
|
|
13
|
+
|
|
14
|
+
### Deprecated
|
|
15
|
+
|
|
16
|
+
### Removed
|
|
17
|
+
|
|
18
|
+
### Fixed
|
|
19
|
+
|
|
20
|
+
### Security
|
|
21
|
+
|
|
22
|
+
## [0.4.0] - 2026-07-06
|
|
23
|
+
|
|
24
|
+
### Changed
|
|
25
|
+
|
|
26
|
+
- Package renamed from `sensitivity-mixin` to `mixin-sensitivity` following the suite's category-first naming convention. Import root changes from `sensitivity_mixin` to `mixin_sensitivity`; all public symbols (`Sensitivity`, `classify`, `@sensitive`, `@classified`, `@phi_aware` backward compatibility) are unchanged.
|
|
27
|
+
- GitHub repository renamed to `jekhator/mixin-sensitivity`; the old URL redirects.
|
|
28
|
+
- The `sensitivity-mixin` distribution remains live on PyPI at 0.3.1; existing pins keep working. New releases publish as `mixin-sensitivity`.
|
|
29
|
+
|
|
30
|
+
## [0.3.1] - 2026-06-10
|
|
31
|
+
|
|
32
|
+
### Fixed
|
|
33
|
+
|
|
34
|
+
- Documentation code examples used uppercase sensitivity metadata values that raise ValueError at runtime; all examples corrected to the lowercase enum values and verified by execution.
|
|
35
|
+
|
|
36
|
+
### Added
|
|
37
|
+
|
|
38
|
+
- README badges; contributing guide, security policy, and code of conduct; changelog project URL.
|
|
39
|
+
|
|
40
|
+
## [0.3.0] - 2026-06-10
|
|
41
|
+
|
|
42
|
+
### BREAKING CHANGES
|
|
43
|
+
|
|
44
|
+
Package renamed from `pii-aware-mixin` to `sensitivity-mixin`. Decorator and API scope broadened to support multiple sensitivity classifications.
|
|
45
|
+
|
|
46
|
+
### Changed
|
|
47
|
+
|
|
48
|
+
- **Package name:** `pii-aware-mixin` → `sensitivity-mixin` (import: `sensitivity_mixin`)
|
|
49
|
+
- **Decorator:** `@phi_aware` → `@sensitive` (works with broadened taxonomy)
|
|
50
|
+
- **Metadata key:** `"phi"` → `"sensitivity"` with values: `"PHI"`, `"PII"`, `"PCI"`, `"SECRET"`
|
|
51
|
+
- **Default placeholder:** `<phi:redacted>` → `***` (simpler, more readable)
|
|
52
|
+
- **API:** `SensitiveDecorator` accepts optional `policies` tuple for per-class masking customization
|
|
53
|
+
|
|
54
|
+
### Added
|
|
55
|
+
|
|
56
|
+
- `@sensitive` decorator with optional policy wiring via `SensitiveDecorator(policies=...)`
|
|
57
|
+
- `classify(instance)` → `SensitivityProfile`: introspect field-level sensitivity
|
|
58
|
+
- `Sensitivity` enum (PHI, PII, PCI, SECRET): taxonomy-driven classification
|
|
59
|
+
- `SensitivityProfile` value object with methods: `has()`, `fields_of()`, `sensitivity_of()`, `is_empty`
|
|
60
|
+
- `Compliance` enum (HIPAA, GDPR, PCI_DSS, NONE): governance regime labels for policies
|
|
61
|
+
- `ClassMakerAware` protocol: contract all sensitivity policies implement
|
|
62
|
+
- Four policy value objects: `PhiPolicyAware`, `PiiPolicyAware`, `PciPolicyAware`, `SecretPolicyAware`
|
|
63
|
+
- `SensitiveFieldSet` value object: field introspection + policy-driven masking
|
|
64
|
+
|
|
65
|
+
### Removed
|
|
66
|
+
|
|
67
|
+
- `mask_for_logging()` helper: use `repr()` on decorated instances instead
|
|
68
|
+
- `to_dict()` helper: use `dataclasses.asdict()` directly
|
|
69
|
+
- Previous masking strategy removed; focus shifted to classification + repr-layer policies
|
|
70
|
+
|
|
71
|
+
### Documentation
|
|
72
|
+
|
|
73
|
+
- `docs/apps/decorators/sensitive.md`: @sensitive decorator and SensitiveFieldSet
|
|
74
|
+
- `docs/apps/decorators/compliance.md`: Compliance enum and ClassMakerAware protocol
|
|
75
|
+
- `docs/apps/decorators/policies.md`: Four policy value objects with usage examples
|
|
76
|
+
|
|
77
|
+
### Why
|
|
78
|
+
|
|
79
|
+
Broadened scope from PHI-only masking to a taxonomy-driven sensitivity framework supporting healthcare (PHI), personal data (PII), payment data (PCI), and secrets. Per-class policy value objects enable compliance-aware customization (HIPAA, GDPR, PCI-DSS) while keeping the decorator simple by default.
|
|
80
|
+
|
|
81
|
+
## [0.2.0] - 2026-05-21
|
|
82
|
+
|
|
83
|
+
### BREAKING CHANGES
|
|
84
|
+
|
|
85
|
+
API redesigned from mixin-inheritance to decorator-based. Consumers must migrate.
|
|
86
|
+
|
|
87
|
+
### Removed
|
|
88
|
+
|
|
89
|
+
- `PiiAwareMixin` class: replaced by `@phi_aware` decorator
|
|
90
|
+
- `ReprMixin` class: functionality folded into `@phi_aware` decorator
|
|
91
|
+
- `ToDictMixin` class: replaced by module-level `to_dict()` helper
|
|
92
|
+
- Requirement for `repr=False` in consumer dataclasses (decorator works on plain `@dataclass(frozen=True, slots=True)`)
|
|
93
|
+
|
|
94
|
+
### Added
|
|
95
|
+
|
|
96
|
+
- `@phi_aware` decorator: injects PHI-masking `__repr__` on frozen dataclasses
|
|
97
|
+
- `mask_for_logging(instance)` module-level helper: returns dict with PHI fields masked
|
|
98
|
+
- `to_dict(instance)` module-level helper: alias for `dataclasses.asdict()` for consistency
|
|
99
|
+
- Field metadata key renamed `"sensitive"` → `"phi"` (healthcare-aligned)
|
|
100
|
+
- 26 comprehensive tests covering decorator, helpers, edge cases, integration
|
|
101
|
+
|
|
102
|
+
### Why
|
|
103
|
+
|
|
104
|
+
Compatible with the canonical "no mixin inheritance on data DTOs" pattern established in downstream QHCG project (2026-05-09).
|
|
105
|
+
|
|
106
|
+
v0.1.0 mixin-inheritance API (`class User(PiiAwareMixin, ReprMixin, ToDictMixin)`) caused R003 false-positives in dto-strict v0.2.x when consumers tried to adopt the "no mixin inheritance" rule. The new decorator API allows clean adoption:
|
|
107
|
+
- No inheritance, no MRO issues
|
|
108
|
+
- Works on plain frozen dataclasses (no `repr=False` boilerplate)
|
|
109
|
+
- Explicit module-level helpers (not instance methods)
|
|
110
|
+
- Compatible with static analysis tools
|
|
111
|
+
|
|
112
|
+
### Migration Guide
|
|
113
|
+
|
|
114
|
+
**v0.1.0 (old, deprecated):**
|
|
115
|
+
```python
|
|
116
|
+
from pii_aware_mixin import PiiAwareMixin, ReprMixin, ToDictMixin
|
|
117
|
+
|
|
118
|
+
@dataclass(frozen=True, slots=True, repr=False)
|
|
119
|
+
class User(PiiAwareMixin, ReprMixin, ToDictMixin):
|
|
120
|
+
id: int
|
|
121
|
+
api_token: str = field(metadata={"sensitive": True})
|
|
122
|
+
|
|
123
|
+
user = User(id=1, api_token="sk-123")
|
|
124
|
+
user.mask_for_logging() # instance method
|
|
125
|
+
repr(user) # mixin's __repr__
|
|
126
|
+
user.to_dict() # instance method
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
**v0.2.0 (new):**
|
|
130
|
+
```python
|
|
131
|
+
from pii_aware_mixin import phi_aware, mask_for_logging, to_dict
|
|
132
|
+
|
|
133
|
+
@phi_aware
|
|
134
|
+
@dataclass(frozen=True, slots=True)
|
|
135
|
+
class User:
|
|
136
|
+
id: int
|
|
137
|
+
api_token: str = field(metadata={"phi": True})
|
|
138
|
+
|
|
139
|
+
user = User(id=1, api_token="sk-123")
|
|
140
|
+
mask_for_logging(user) # module-level helper
|
|
141
|
+
repr(user) # decorator's __repr__
|
|
142
|
+
to_dict(user) # module-level helper
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
**Changes per file:**
|
|
146
|
+
1. Remove mixin inheritance from class definition
|
|
147
|
+
2. Add `@phi_aware` decorator above `@dataclass`
|
|
148
|
+
3. Remove `repr=False` from `@dataclass` (no longer needed)
|
|
149
|
+
4. Change `metadata={"sensitive": True}` → `metadata={"phi": True}`
|
|
150
|
+
5. Replace `instance.mask_for_logging()` → `mask_for_logging(instance)`
|
|
151
|
+
6. Replace `instance.to_dict()` → `to_dict(instance)`
|
|
152
|
+
|
|
153
|
+
## [0.1.0] - 2026-05-18
|
|
154
|
+
|
|
155
|
+
### Added
|
|
156
|
+
|
|
157
|
+
- `PiiAwareMixin`: metadata-driven masking via `mask_for_logging()` instance method
|
|
158
|
+
- `ReprMixin`: structured `__repr__()` with automatic field masking
|
|
159
|
+
- `ToDictMixin`: JSON serialization via `to_dict()` and `from_dict()` instance methods
|
|
160
|
+
- Field metadata key `"sensitive"` to mark fields for masking
|
|
161
|
+
- Comprehensive tests for all three mixins and composition patterns
|
|
162
|
+
- README with quick start, API reference, logging integration examples
|
|
163
|
+
|
|
164
|
+
### Design
|
|
165
|
+
|
|
166
|
+
Three composable mixin classes for frozen dataclasses, using Python's MRO for clean composition.
|
|
167
|
+
All work independently or together. Full-mask strategy: sensitive fields become `<MASKED>` in logs and `<masked: sensitive>` in reprs.
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# Code of Conduct
|
|
2
|
+
|
|
3
|
+
## My Commitment
|
|
4
|
+
|
|
5
|
+
I maintain this project solo, and I am committed to keeping it a welcoming space for everyone who participates in issues, pull requests, and discussions. I pledge to treat every participant with respect and dignity, regardless of age, body size, caste, disability, ethnicity, sex characteristics, gender, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, sexual identity and orientation, or any other dimension of diversity.
|
|
6
|
+
|
|
7
|
+
## Standards
|
|
8
|
+
|
|
9
|
+
Behavior that keeps this a positive space:
|
|
10
|
+
|
|
11
|
+
- Welcoming and inclusive language
|
|
12
|
+
- Respect for differing viewpoints and experiences
|
|
13
|
+
- Gracefully accepting constructive criticism
|
|
14
|
+
- Focusing on what is best for the project
|
|
15
|
+
- Empathy toward other participants
|
|
16
|
+
|
|
17
|
+
Behavior I will not accept:
|
|
18
|
+
|
|
19
|
+
- Harassment or insulting or derogatory comments
|
|
20
|
+
- Personal attacks
|
|
21
|
+
- Public or private harassment
|
|
22
|
+
- Publishing someone's private information without consent
|
|
23
|
+
- Conduct that could reasonably be considered inappropriate
|
|
24
|
+
|
|
25
|
+
## Enforcement
|
|
26
|
+
|
|
27
|
+
Report unacceptable behavior to me through GitHub, either by opening an issue or, where discretion is warranted, through private vulnerability reporting. I review and act on every report promptly and fairly, and I respect the privacy and security of anyone who reports.
|
|
28
|
+
|
|
29
|
+
## Attribution
|
|
30
|
+
|
|
31
|
+
This Code of Conduct is adapted from the Contributor Covenant, version 2.1, available at https://www.contributor-covenant.org/version/2/1/code_of_conduct.html.
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# Contributing
|
|
2
|
+
|
|
3
|
+
Thank you for your interest in contributing! I welcome pull requests, bug reports, and feature suggestions.
|
|
4
|
+
|
|
5
|
+
## Development Setup
|
|
6
|
+
|
|
7
|
+
### Prerequisites
|
|
8
|
+
|
|
9
|
+
- Python 3.11+
|
|
10
|
+
- [uv](https://docs.astral.sh/uv/) for dependency management
|
|
11
|
+
|
|
12
|
+
### Getting Started
|
|
13
|
+
|
|
14
|
+
1. Clone the repository:
|
|
15
|
+
```bash
|
|
16
|
+
git clone https://github.com/jekhator/mixin-sensitivity.git
|
|
17
|
+
cd mixin-sensitivity
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
2. Sync dependencies with uv:
|
|
21
|
+
```bash
|
|
22
|
+
uv sync --all-extras
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
3. Run tests to verify setup:
|
|
26
|
+
```bash
|
|
27
|
+
uv run pytest
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Code Quality
|
|
31
|
+
|
|
32
|
+
### Testing
|
|
33
|
+
|
|
34
|
+
Run the test suite:
|
|
35
|
+
```bash
|
|
36
|
+
uv run pytest
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
For coverage:
|
|
40
|
+
```bash
|
|
41
|
+
uv run pytest --cov
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
### Linting and Formatting
|
|
45
|
+
|
|
46
|
+
Format code with ruff:
|
|
47
|
+
```bash
|
|
48
|
+
uv run ruff format
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Check for linting issues:
|
|
52
|
+
```bash
|
|
53
|
+
uv run ruff check
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## Pull Requests
|
|
57
|
+
|
|
58
|
+
- Open PRs against the `main` branch.
|
|
59
|
+
- Include a clear description of the change and the problem it solves.
|
|
60
|
+
- Ensure all tests pass and code is formatted before requesting review.
|
|
61
|
+
- The CI pipeline will verify linting, formatting, and test coverage.
|
|
62
|
+
|
|
63
|
+
## License
|
|
64
|
+
|
|
65
|
+
By contributing, you agree that your contributions will be licensed under the Apache 2.0 License.
|