ciphergate-cli 0.16.0__tar.gz → 0.17.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.
- {ciphergate_cli-0.16.0 → ciphergate_cli-0.17.0}/CHANGELOG.md +39 -1
- {ciphergate_cli-0.16.0 → ciphergate_cli-0.17.0}/PKG-INFO +28 -3
- {ciphergate_cli-0.16.0 → ciphergate_cli-0.17.0}/README.md +27 -2
- {ciphergate_cli-0.16.0 → ciphergate_cli-0.17.0}/docs/architecture.md +1 -0
- {ciphergate_cli-0.16.0 → ciphergate_cli-0.17.0}/docs/limitations.md +17 -0
- {ciphergate_cli-0.16.0 → ciphergate_cli-0.17.0}/docs/policy-schema.md +72 -0
- ciphergate_cli-0.17.0/policies/hospital_us_override.yml +46 -0
- ciphergate_cli-0.17.0/policies/packs/eu_gdpr.yml +78 -0
- ciphergate_cli-0.17.0/policies/packs/healthcare_baseline.yml +109 -0
- ciphergate_cli-0.17.0/policies/packs/india_dpdp.yml +97 -0
- ciphergate_cli-0.17.0/policies/packs/us_hipaa.yml +80 -0
- {ciphergate_cli-0.16.0 → ciphergate_cli-0.17.0}/src/ciphergate/__init__.py +1 -1
- {ciphergate_cli-0.16.0 → ciphergate_cli-0.17.0}/src/ciphergate/cli.py +4 -0
- {ciphergate_cli-0.16.0 → ciphergate_cli-0.17.0}/src/ciphergate/models/policy.py +5 -0
- {ciphergate_cli-0.16.0 → ciphergate_cli-0.17.0}/src/ciphergate/pipeline/review.py +9 -2
- {ciphergate_cli-0.16.0 → ciphergate_cli-0.17.0}/src/ciphergate/policy/engine.py +5 -0
- ciphergate_cli-0.17.0/src/ciphergate/policy/layers.py +508 -0
- {ciphergate_cli-0.16.0 → ciphergate_cli-0.17.0}/src/ciphergate/policy/loader.py +11 -0
- {ciphergate_cli-0.16.0 → ciphergate_cli-0.17.0}/src/ciphergate/policy/templates.py +1 -12
- {ciphergate_cli-0.16.0 → ciphergate_cli-0.17.0}/src/ciphergate/policy/validator.py +33 -2
- {ciphergate_cli-0.16.0 → ciphergate_cli-0.17.0}/src/ciphergate/reporting/audit.py +14 -0
- {ciphergate_cli-0.16.0 → ciphergate_cli-0.17.0}/src/ciphergate/reporting/terminal.py +2 -0
- {ciphergate_cli-0.16.0 → ciphergate_cli-0.17.0}/src/ciphergate/transforms/registry.py +5 -3
- {ciphergate_cli-0.16.0 → ciphergate_cli-0.17.0}/tests/integration/test_cli_e2e.py +18 -0
- ciphergate_cli-0.17.0/tests/unit/test_policy_layers.py +438 -0
- {ciphergate_cli-0.16.0 → ciphergate_cli-0.17.0}/.gitignore +0 -0
- {ciphergate_cli-0.16.0 → ciphergate_cli-0.17.0}/LICENSE +0 -0
- {ciphergate_cli-0.16.0 → ciphergate_cli-0.17.0}/NOTICE +0 -0
- {ciphergate_cli-0.16.0 → ciphergate_cli-0.17.0}/SECURITY.md +0 -0
- {ciphergate_cli-0.16.0 → ciphergate_cli-0.17.0}/docs/claude-code-tooling.md +0 -0
- {ciphergate_cli-0.16.0 → ciphergate_cli-0.17.0}/docs/detector-coverage.md +0 -0
- {ciphergate_cli-0.16.0 → ciphergate_cli-0.17.0}/docs/examples.md +0 -0
- {ciphergate_cli-0.16.0 → ciphergate_cli-0.17.0}/docs/security-and-privacy.md +0 -0
- {ciphergate_cli-0.16.0 → ciphergate_cli-0.17.0}/docs/threat-model.md +0 -0
- {ciphergate_cli-0.16.0 → ciphergate_cli-0.17.0}/examples/expected/sample.dev_local_dev.csv +0 -0
- {ciphergate_cli-0.16.0 → ciphergate_cli-0.17.0}/examples/expected/sample.dev_local_dev.json +0 -0
- {ciphergate_cli-0.16.0 → ciphergate_cli-0.17.0}/examples/expected/sample.dev_local_dev.jsonl +0 -0
- {ciphergate_cli-0.16.0 → ciphergate_cli-0.17.0}/examples/expected/sample.dev_local_dev.log +0 -0
- {ciphergate_cli-0.16.0 → ciphergate_cli-0.17.0}/examples/sample.csv +0 -0
- {ciphergate_cli-0.16.0 → ciphergate_cli-0.17.0}/examples/sample.json +0 -0
- {ciphergate_cli-0.16.0 → ciphergate_cli-0.17.0}/examples/sample.jsonl +0 -0
- {ciphergate_cli-0.16.0 → ciphergate_cli-0.17.0}/examples/sample.log +0 -0
- {ciphergate_cli-0.16.0 → ciphergate_cli-0.17.0}/policies/dev_local.yml +0 -0
- {ciphergate_cli-0.16.0 → ciphergate_cli-0.17.0}/policies/developer_logs.yml +0 -0
- {ciphergate_cli-0.16.0 → ciphergate_cli-0.17.0}/policies/external_ai.yml +0 -0
- {ciphergate_cli-0.16.0 → ciphergate_cli-0.17.0}/policies/financial_template.yml +0 -0
- {ciphergate_cli-0.16.0 → ciphergate_cli-0.17.0}/policies/healthcare_template.yml +0 -0
- {ciphergate_cli-0.16.0 → ciphergate_cli-0.17.0}/policies/local_debug.yml +0 -0
- {ciphergate_cli-0.16.0 → ciphergate_cli-0.17.0}/policies/multi_environment.yml +0 -0
- {ciphergate_cli-0.16.0 → ciphergate_cli-0.17.0}/policies/prd_external_ai.yml +0 -0
- {ciphergate_cli-0.16.0 → ciphergate_cli-0.17.0}/policies/stg_test.yml +0 -0
- {ciphergate_cli-0.16.0 → ciphergate_cli-0.17.0}/policies/test_dataset.yml +0 -0
- {ciphergate_cli-0.16.0 → ciphergate_cli-0.17.0}/pyproject.toml +0 -0
- {ciphergate_cli-0.16.0 → ciphergate_cli-0.17.0}/scripts/benchmark/README.md +0 -0
- {ciphergate_cli-0.16.0 → ciphergate_cli-0.17.0}/src/ciphergate/__main__.py +0 -0
- {ciphergate_cli-0.16.0 → ciphergate_cli-0.17.0}/src/ciphergate/config.py +0 -0
- {ciphergate_cli-0.16.0 → ciphergate_cli-0.17.0}/src/ciphergate/constants.py +0 -0
- {ciphergate_cli-0.16.0 → ciphergate_cli-0.17.0}/src/ciphergate/detectors/__init__.py +0 -0
- {ciphergate_cli-0.16.0 → ciphergate_cli-0.17.0}/src/ciphergate/detectors/base.py +0 -0
- {ciphergate_cli-0.16.0 → ciphergate_cli-0.17.0}/src/ciphergate/detectors/custom_regex.py +0 -0
- {ciphergate_cli-0.16.0 → ciphergate_cli-0.17.0}/src/ciphergate/detectors/dates.py +0 -0
- {ciphergate_cli-0.16.0 → ciphergate_cli-0.17.0}/src/ciphergate/detectors/email.py +0 -0
- {ciphergate_cli-0.16.0 → ciphergate_cli-0.17.0}/src/ciphergate/detectors/field_names.py +0 -0
- {ciphergate_cli-0.16.0 → ciphergate_cli-0.17.0}/src/ciphergate/detectors/iban.py +0 -0
- {ciphergate_cli-0.16.0 → ciphergate_cli-0.17.0}/src/ciphergate/detectors/ip_address.py +0 -0
- {ciphergate_cli-0.16.0 → ciphergate_cli-0.17.0}/src/ciphergate/detectors/mac_address.py +0 -0
- {ciphergate_cli-0.16.0 → ciphergate_cli-0.17.0}/src/ciphergate/detectors/network.py +0 -0
- {ciphergate_cli-0.16.0 → ciphergate_cli-0.17.0}/src/ciphergate/detectors/payment_card.py +0 -0
- {ciphergate_cli-0.16.0 → ciphergate_cli-0.17.0}/src/ciphergate/detectors/phone.py +0 -0
- {ciphergate_cli-0.16.0 → ciphergate_cli-0.17.0}/src/ciphergate/detectors/presidio_ner.py +0 -0
- {ciphergate_cli-0.16.0 → ciphergate_cli-0.17.0}/src/ciphergate/detectors/regex_safety.py +0 -0
- {ciphergate_cli-0.16.0 → ciphergate_cli-0.17.0}/src/ciphergate/detectors/registry.py +0 -0
- {ciphergate_cli-0.16.0 → ciphergate_cli-0.17.0}/src/ciphergate/detectors/secrets.py +0 -0
- {ciphergate_cli-0.16.0 → ciphergate_cli-0.17.0}/src/ciphergate/detectors/ssn.py +0 -0
- {ciphergate_cli-0.16.0 → ciphergate_cli-0.17.0}/src/ciphergate/errors.py +0 -0
- {ciphergate_cli-0.16.0 → ciphergate_cli-0.17.0}/src/ciphergate/models/__init__.py +0 -0
- {ciphergate_cli-0.16.0 → ciphergate_cli-0.17.0}/src/ciphergate/models/context.py +0 -0
- {ciphergate_cli-0.16.0 → ciphergate_cli-0.17.0}/src/ciphergate/models/finding.py +0 -0
- {ciphergate_cli-0.16.0 → ciphergate_cli-0.17.0}/src/ciphergate/models/report.py +0 -0
- {ciphergate_cli-0.16.0 → ciphergate_cli-0.17.0}/src/ciphergate/pipeline/__init__.py +0 -0
- {ciphergate_cli-0.16.0 → ciphergate_cli-0.17.0}/src/ciphergate/pipeline/atomic_output.py +0 -0
- {ciphergate_cli-0.16.0 → ciphergate_cli-0.17.0}/src/ciphergate/pipeline/batch.py +0 -0
- {ciphergate_cli-0.16.0 → ciphergate_cli-0.17.0}/src/ciphergate/pipeline/inspector.py +0 -0
- {ciphergate_cli-0.16.0 → ciphergate_cli-0.17.0}/src/ciphergate/pipeline/paths.py +0 -0
- {ciphergate_cli-0.16.0 → ciphergate_cli-0.17.0}/src/ciphergate/pipeline/planner.py +0 -0
- {ciphergate_cli-0.16.0 → ciphergate_cli-0.17.0}/src/ciphergate/pipeline/rescan.py +0 -0
- {ciphergate_cli-0.16.0 → ciphergate_cli-0.17.0}/src/ciphergate/pipeline/sanitizer.py +0 -0
- {ciphergate_cli-0.16.0 → ciphergate_cli-0.17.0}/src/ciphergate/pipeline/stdio.py +0 -0
- {ciphergate_cli-0.16.0 → ciphergate_cli-0.17.0}/src/ciphergate/pipeline/streams.py +0 -0
- {ciphergate_cli-0.16.0 → ciphergate_cli-0.17.0}/src/ciphergate/policy/__init__.py +0 -0
- {ciphergate_cli-0.16.0 → ciphergate_cli-0.17.0}/src/ciphergate/policy/context.py +0 -0
- {ciphergate_cli-0.16.0 → ciphergate_cli-0.17.0}/src/ciphergate/policy/precedence.py +0 -0
- {ciphergate_cli-0.16.0 → ciphergate_cli-0.17.0}/src/ciphergate/py.typed +0 -0
- {ciphergate_cli-0.16.0 → ciphergate_cli-0.17.0}/src/ciphergate/readers/__init__.py +0 -0
- {ciphergate_cli-0.16.0 → ciphergate_cli-0.17.0}/src/ciphergate/readers/accesslog_reader.py +0 -0
- {ciphergate_cli-0.16.0 → ciphergate_cli-0.17.0}/src/ciphergate/readers/base.py +0 -0
- {ciphergate_cli-0.16.0 → ciphergate_cli-0.17.0}/src/ciphergate/readers/csv_reader.py +0 -0
- {ciphergate_cli-0.16.0 → ciphergate_cli-0.17.0}/src/ciphergate/readers/json_reader.py +0 -0
- {ciphergate_cli-0.16.0 → ciphergate_cli-0.17.0}/src/ciphergate/readers/jsonl_reader.py +0 -0
- {ciphergate_cli-0.16.0 → ciphergate_cli-0.17.0}/src/ciphergate/readers/jsonlog_reader.py +0 -0
- {ciphergate_cli-0.16.0 → ciphergate_cli-0.17.0}/src/ciphergate/readers/line_structure.py +0 -0
- {ciphergate_cli-0.16.0 → ciphergate_cli-0.17.0}/src/ciphergate/readers/logfmt_reader.py +0 -0
- {ciphergate_cli-0.16.0 → ciphergate_cli-0.17.0}/src/ciphergate/readers/stacktrace_reader.py +0 -0
- {ciphergate_cli-0.16.0 → ciphergate_cli-0.17.0}/src/ciphergate/readers/syslog_reader.py +0 -0
- {ciphergate_cli-0.16.0 → ciphergate_cli-0.17.0}/src/ciphergate/readers/text_reader.py +0 -0
- {ciphergate_cli-0.16.0 → ciphergate_cli-0.17.0}/src/ciphergate/reporting/__init__.py +0 -0
- {ciphergate_cli-0.16.0 → ciphergate_cli-0.17.0}/src/ciphergate/reporting/next_steps.py +0 -0
- {ciphergate_cli-0.16.0 → ciphergate_cli-0.17.0}/src/ciphergate/reporting/review_page.py +0 -0
- {ciphergate_cli-0.16.0 → ciphergate_cli-0.17.0}/src/ciphergate/reporting/safe_text.py +0 -0
- {ciphergate_cli-0.16.0 → ciphergate_cli-0.17.0}/src/ciphergate/reporting/serializer.py +0 -0
- {ciphergate_cli-0.16.0 → ciphergate_cli-0.17.0}/src/ciphergate/templates/starter_policy.yml +0 -0
- {ciphergate_cli-0.16.0 → ciphergate_cli-0.17.0}/src/ciphergate/transforms/__init__.py +0 -0
- {ciphergate_cli-0.16.0 → ciphergate_cli-0.17.0}/src/ciphergate/transforms/date_shift.py +0 -0
- {ciphergate_cli-0.16.0 → ciphergate_cli-0.17.0}/src/ciphergate/transforms/generalize.py +0 -0
- {ciphergate_cli-0.16.0 → ciphergate_cli-0.17.0}/src/ciphergate/transforms/keys.py +0 -0
- {ciphergate_cli-0.16.0 → ciphergate_cli-0.17.0}/src/ciphergate/transforms/pseudonymize.py +0 -0
- {ciphergate_cli-0.16.0 → ciphergate_cli-0.17.0}/src/ciphergate/transforms/redact.py +0 -0
- {ciphergate_cli-0.16.0 → ciphergate_cli-0.17.0}/src/ciphergate/transforms/replace.py +0 -0
- {ciphergate_cli-0.16.0 → ciphergate_cli-0.17.0}/tests/__init__.py +0 -0
- {ciphergate_cli-0.16.0 → ciphergate_cli-0.17.0}/tests/conftest.py +0 -0
- {ciphergate_cli-0.16.0 → ciphergate_cli-0.17.0}/tests/integration/__init__.py +0 -0
- {ciphergate_cli-0.16.0 → ciphergate_cli-0.17.0}/tests/integration/test_api_batch_streams.py +0 -0
- {ciphergate_cli-0.16.0 → ciphergate_cli-0.17.0}/tests/integration/test_atomic_output.py +0 -0
- {ciphergate_cli-0.16.0 → ciphergate_cli-0.17.0}/tests/integration/test_batch.py +0 -0
- {ciphergate_cli-0.16.0 → ciphergate_cli-0.17.0}/tests/integration/test_domain_templates.py +0 -0
- {ciphergate_cli-0.16.0 → ciphergate_cli-0.17.0}/tests/integration/test_environment_matrix.py +0 -0
- {ciphergate_cli-0.16.0 → ciphergate_cli-0.17.0}/tests/integration/test_pipelines.py +0 -0
- {ciphergate_cli-0.16.0 → ciphergate_cli-0.17.0}/tests/integration/test_presidio_real.py +0 -0
- {ciphergate_cli-0.16.0 → ciphergate_cli-0.17.0}/tests/integration/test_review.py +0 -0
- {ciphergate_cli-0.16.0 → ciphergate_cli-0.17.0}/tests/integration/test_stdio.py +0 -0
- {ciphergate_cli-0.16.0 → ciphergate_cli-0.17.0}/tests/regression/__init__.py +0 -0
- {ciphergate_cli-0.16.0 → ciphergate_cli-0.17.0}/tests/regression/test_known_cases.py +0 -0
- {ciphergate_cli-0.16.0 → ciphergate_cli-0.17.0}/tests/security/__init__.py +0 -0
- {ciphergate_cli-0.16.0 → ciphergate_cli-0.17.0}/tests/security/test_key_file.py +0 -0
- {ciphergate_cli-0.16.0 → ciphergate_cli-0.17.0}/tests/security/test_no_network.py +0 -0
- {ciphergate_cli-0.16.0 → ciphergate_cli-0.17.0}/tests/security/test_no_raw_value_leaks.py +0 -0
- {ciphergate_cli-0.16.0 → ciphergate_cli-0.17.0}/tests/security/test_path_safety.py +0 -0
- {ciphergate_cli-0.16.0 → ciphergate_cli-0.17.0}/tests/security/test_policy_fail_closed.py +0 -0
- {ciphergate_cli-0.16.0 → ciphergate_cli-0.17.0}/tests/security/test_review_findings.py +0 -0
- {ciphergate_cli-0.16.0 → ciphergate_cli-0.17.0}/tests/security/test_review_page.py +0 -0
- {ciphergate_cli-0.16.0 → ciphergate_cli-0.17.0}/tests/tooling/__init__.py +0 -0
- {ciphergate_cli-0.16.0 → ciphergate_cli-0.17.0}/tests/tooling/test_benchmark.py +0 -0
- {ciphergate_cli-0.16.0 → ciphergate_cli-0.17.0}/tests/tooling/test_ci_supply_chain.py +0 -0
- {ciphergate_cli-0.16.0 → ciphergate_cli-0.17.0}/tests/tooling/test_claude_tooling.py +0 -0
- {ciphergate_cli-0.16.0 → ciphergate_cli-0.17.0}/tests/tooling/test_git_tooling.py +0 -0
- {ciphergate_cli-0.16.0 → ciphergate_cli-0.17.0}/tests/tooling/test_publish_workflow.py +0 -0
- {ciphergate_cli-0.16.0 → ciphergate_cli-0.17.0}/tests/unit/__init__.py +0 -0
- {ciphergate_cli-0.16.0 → ciphergate_cli-0.17.0}/tests/unit/test_accesslog.py +0 -0
- {ciphergate_cli-0.16.0 → ciphergate_cli-0.17.0}/tests/unit/test_detectors.py +0 -0
- {ciphergate_cli-0.16.0 → ciphergate_cli-0.17.0}/tests/unit/test_jsonlog.py +0 -0
- {ciphergate_cli-0.16.0 → ciphergate_cli-0.17.0}/tests/unit/test_logfmt.py +0 -0
- {ciphergate_cli-0.16.0 → ciphergate_cli-0.17.0}/tests/unit/test_next_steps.py +0 -0
- {ciphergate_cli-0.16.0 → ciphergate_cli-0.17.0}/tests/unit/test_policy_engine.py +0 -0
- {ciphergate_cli-0.16.0 → ciphergate_cli-0.17.0}/tests/unit/test_policy_precedence.py +0 -0
- {ciphergate_cli-0.16.0 → ciphergate_cli-0.17.0}/tests/unit/test_policy_templates.py +0 -0
- {ciphergate_cli-0.16.0 → ciphergate_cli-0.17.0}/tests/unit/test_policy_validation.py +0 -0
- {ciphergate_cli-0.16.0 → ciphergate_cli-0.17.0}/tests/unit/test_presidio_ner.py +0 -0
- {ciphergate_cli-0.16.0 → ciphergate_cli-0.17.0}/tests/unit/test_properties.py +0 -0
- {ciphergate_cli-0.16.0 → ciphergate_cli-0.17.0}/tests/unit/test_readers.py +0 -0
- {ciphergate_cli-0.16.0 → ciphergate_cli-0.17.0}/tests/unit/test_stacktrace.py +0 -0
- {ciphergate_cli-0.16.0 → ciphergate_cli-0.17.0}/tests/unit/test_syslog.py +0 -0
- {ciphergate_cli-0.16.0 → ciphergate_cli-0.17.0}/tests/unit/test_transforms.py +0 -0
|
@@ -6,6 +6,43 @@ versioning: [PEP 440](https://peps.python.org/pep-0440/) with SemVer meaning (pr
|
|
|
6
6
|
|
|
7
7
|
## [Unreleased]
|
|
8
8
|
|
|
9
|
+
## [0.17.0] - 2026-11-02
|
|
10
|
+
|
|
11
|
+
Policies can now be built in layers. A shipped baseline pack, a jurisdiction pack (US, EU or
|
|
12
|
+
India) and your own small file merge into one policy, and a later layer cannot quietly weaken
|
|
13
|
+
an earlier one. The packs are templates requiring review, not legal or compliance advice.
|
|
14
|
+
|
|
15
|
+
### Added
|
|
16
|
+
|
|
17
|
+
- Policy layering: `extends: [pack, ...]` builds one effective policy from shipped packs plus
|
|
18
|
+
your own file. Layers merge so that a later layer can never quietly weaken an earlier one.
|
|
19
|
+
Conflicting context pins, weaker settings or requirements, disabled detectors, and rules or
|
|
20
|
+
defaults that would override an earlier block, review or transform with something weaker
|
|
21
|
+
fail validation. A rule may weaken an earlier rule or default only by naming it in the new
|
|
22
|
+
`relaxes` field; this is reported as a warning. Earlier `hard` and `guardrail` rules can
|
|
23
|
+
never be relaxed.
|
|
24
|
+
- Shipped packs (templates requiring review): `healthcare_baseline`, a strict global healthcare
|
|
25
|
+
default, and `us_hipaa`, field classifications modelled on the HIPAA Safe Harbor identifier
|
|
26
|
+
list, with ages over 89 shown as `90+`. `us_hipaa` is not Safe Harbor or Expert Determination
|
|
27
|
+
by itself.
|
|
28
|
+
- New template `hospital_us_override`, an example hospital layer on top of `us_hipaa`.
|
|
29
|
+
- Packs `eu_gdpr` and `india_dpdp` (templates requiring review, not legal advice). `eu_gdpr`
|
|
30
|
+
blocks GDPR Art. 9 special-category fields toward external destinations and reviews them
|
|
31
|
+
elsewhere, and finds member-state identifier fields plus Italian codice fiscale values.
|
|
32
|
+
`india_dpdp` finds grouped Aadhaar numbers, ABHA numbers and addresses, PAN and Indian
|
|
33
|
+
identifier fields, and blocks SPDI-style sensitive fields toward external destinations. One
|
|
34
|
+
policy can extend several jurisdiction packs.
|
|
35
|
+
|
|
36
|
+
### Changed
|
|
37
|
+
|
|
38
|
+
- When equally protective findings overlap in one value, the more confident one now names the
|
|
39
|
+
redaction marker (for example a grouped Aadhaar number is marked `pii.government_id`, not
|
|
40
|
+
`pii.phone`). What gets removed is unchanged.
|
|
41
|
+
- Reports list the policy's layers (name, kind, source, file digest), and each decision names
|
|
42
|
+
the layer that made it. `policy.digest` now covers every layer. `validate-policy` prints the
|
|
43
|
+
layer chain and adds `layers` to its JSON output. The terminal summary and the review page
|
|
44
|
+
show the layer next to the rule id.
|
|
45
|
+
|
|
9
46
|
## [0.16.0] - 2026-11-02
|
|
10
47
|
|
|
11
48
|
`ciphergate review` writes a local HTML page that shows, as a trace, what sanitize would change
|
|
@@ -416,7 +453,8 @@ Pre-release (alpha). Licensed under Apache-2.0. Not a final release: the securit
|
|
|
416
453
|
See `docs/limitations.md`. Deferred: optional NER/Presidio detectors, stdin/stdout streaming,
|
|
417
454
|
directory processing, structured log parsers, key files, reversible token vault, lossy decoding.
|
|
418
455
|
|
|
419
|
-
[Unreleased]: https://github.com/aashishkumar-tech/ciphergate/compare/v0.
|
|
456
|
+
[Unreleased]: https://github.com/aashishkumar-tech/ciphergate/compare/v0.17.0...HEAD
|
|
457
|
+
[0.17.0]: https://github.com/aashishkumar-tech/ciphergate/compare/v0.16.0...v0.17.0
|
|
420
458
|
[0.16.0]: https://github.com/aashishkumar-tech/ciphergate/compare/v0.15.0...v0.16.0
|
|
421
459
|
[0.15.0]: https://github.com/aashishkumar-tech/ciphergate/compare/v0.14.0...v0.15.0
|
|
422
460
|
[0.14.0]: https://github.com/aashishkumar-tech/ciphergate/compare/v0.13.0...v0.14.0
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: ciphergate-cli
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.17.0
|
|
4
4
|
Summary: Local-first, environment- and destination-aware inspection and sanitization of logs and test datasets.
|
|
5
5
|
Project-URL: Homepage, https://github.com/aashishkumar-tech/ciphergate
|
|
6
6
|
Project-URL: Source, https://github.com/aashishkumar-tech/ciphergate
|
|
@@ -50,7 +50,7 @@ vendor ticket. CipherGate replaces that with a local, explainable, scriptable ga
|
|
|
50
50
|
inspect -> plan -> sanitize --apply -> rescan
|
|
51
51
|
```
|
|
52
52
|
|
|
53
|
-
> **Status: 0.
|
|
53
|
+
> **Status: 0.17.0 (alpha).** No detector finds every sensitive value. A passing run means the
|
|
54
54
|
> configured processing completed under the selected policy; it is **not** proof that the output
|
|
55
55
|
> contains no sensitive information, and it is not a compliance (GDPR, HIPAA, PCI DSS, ...) or
|
|
56
56
|
> anonymization guarantee. Policy templates must be reviewed before use.
|
|
@@ -232,10 +232,35 @@ controlled pipeline configuration.
|
|
|
232
232
|
## Shipped policy templates (review before use)
|
|
233
233
|
|
|
234
234
|
`dev_local`, `stg_test`, `prd_external_ai`, `developer_logs`, `test_dataset`, `external_ai`,
|
|
235
|
-
`healthcare_template`, `financial_template`, `local_debug`, and `multi_environment` (one policy
|
|
235
|
+
`healthcare_template`, `financial_template`, `local_debug`, `hospital_us_override` (a layered example, see below), and `multi_environment` (one policy
|
|
236
236
|
whose outcomes vary by environment *and* destination). All validate in CI. Templates named for
|
|
237
237
|
healthcare or finance are starting points, **not** HIPAA/PCI controls.
|
|
238
238
|
|
|
239
|
+
### Policy layering
|
|
240
|
+
|
|
241
|
+
A policy can `extends:` shipped packs, so a team keeps only its own differences in a small file:
|
|
242
|
+
|
|
243
|
+
```text
|
|
244
|
+
healthcare_baseline (global strict default) -> us_hipaa (US identifier list) -> your file
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
```bash
|
|
248
|
+
ciphergate init --template hospital_us_override --output my-hospital.yml
|
|
249
|
+
ciphergate validate-policy my-hospital.yml
|
|
250
|
+
# layers: healthcare_baseline -> us_hipaa -> hospital-us-override
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
A later layer can add context, detectors, classifications and stricter rules. It cannot quietly
|
|
254
|
+
weaken an earlier one: a weaker setting, a disabled detector or a rule that would override a
|
|
255
|
+
block, review or redaction with something weaker fails validation unless the rule names what it
|
|
256
|
+
relaxes (`relaxes: [rule-id]`), which is then reported as a warning. Hard and guardrail rules
|
|
257
|
+
can never be relaxed. Reports list every layer with its digest and show which layer decided
|
|
258
|
+
each value. Jurisdiction packs: `us_hipaa` (modelled on the HIPAA Safe Harbor identifier list
|
|
259
|
+
but **not** Safe Harbor or Expert Determination by itself), `eu_gdpr` (GDPR special categories
|
|
260
|
+
and member-state identifiers) and `india_dpdp` (Aadhaar, ABHA, PAN and sensitive fields). None
|
|
261
|
+
of them is legal advice or makes processing lawful. A policy can extend several. See
|
|
262
|
+
[docs/policy-schema.md](docs/policy-schema.md#layering-extends).
|
|
263
|
+
|
|
239
264
|
## Commands and exit codes
|
|
240
265
|
|
|
241
266
|
`init`, `validate-policy`, `inspect`, `plan`, `sanitize` (needs `--apply`), `rescan`, `review`
|
|
@@ -11,7 +11,7 @@ vendor ticket. CipherGate replaces that with a local, explainable, scriptable ga
|
|
|
11
11
|
inspect -> plan -> sanitize --apply -> rescan
|
|
12
12
|
```
|
|
13
13
|
|
|
14
|
-
> **Status: 0.
|
|
14
|
+
> **Status: 0.17.0 (alpha).** No detector finds every sensitive value. A passing run means the
|
|
15
15
|
> configured processing completed under the selected policy; it is **not** proof that the output
|
|
16
16
|
> contains no sensitive information, and it is not a compliance (GDPR, HIPAA, PCI DSS, ...) or
|
|
17
17
|
> anonymization guarantee. Policy templates must be reviewed before use.
|
|
@@ -193,10 +193,35 @@ controlled pipeline configuration.
|
|
|
193
193
|
## Shipped policy templates (review before use)
|
|
194
194
|
|
|
195
195
|
`dev_local`, `stg_test`, `prd_external_ai`, `developer_logs`, `test_dataset`, `external_ai`,
|
|
196
|
-
`healthcare_template`, `financial_template`, `local_debug`, and `multi_environment` (one policy
|
|
196
|
+
`healthcare_template`, `financial_template`, `local_debug`, `hospital_us_override` (a layered example, see below), and `multi_environment` (one policy
|
|
197
197
|
whose outcomes vary by environment *and* destination). All validate in CI. Templates named for
|
|
198
198
|
healthcare or finance are starting points, **not** HIPAA/PCI controls.
|
|
199
199
|
|
|
200
|
+
### Policy layering
|
|
201
|
+
|
|
202
|
+
A policy can `extends:` shipped packs, so a team keeps only its own differences in a small file:
|
|
203
|
+
|
|
204
|
+
```text
|
|
205
|
+
healthcare_baseline (global strict default) -> us_hipaa (US identifier list) -> your file
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
```bash
|
|
209
|
+
ciphergate init --template hospital_us_override --output my-hospital.yml
|
|
210
|
+
ciphergate validate-policy my-hospital.yml
|
|
211
|
+
# layers: healthcare_baseline -> us_hipaa -> hospital-us-override
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
A later layer can add context, detectors, classifications and stricter rules. It cannot quietly
|
|
215
|
+
weaken an earlier one: a weaker setting, a disabled detector or a rule that would override a
|
|
216
|
+
block, review or redaction with something weaker fails validation unless the rule names what it
|
|
217
|
+
relaxes (`relaxes: [rule-id]`), which is then reported as a warning. Hard and guardrail rules
|
|
218
|
+
can never be relaxed. Reports list every layer with its digest and show which layer decided
|
|
219
|
+
each value. Jurisdiction packs: `us_hipaa` (modelled on the HIPAA Safe Harbor identifier list
|
|
220
|
+
but **not** Safe Harbor or Expert Determination by itself), `eu_gdpr` (GDPR special categories
|
|
221
|
+
and member-state identifiers) and `india_dpdp` (Aadhaar, ABHA, PAN and sensitive fields). None
|
|
222
|
+
of them is legal advice or makes processing lawful. A policy can extend several. See
|
|
223
|
+
[docs/policy-schema.md](docs/policy-schema.md#layering-extends).
|
|
224
|
+
|
|
200
225
|
## Commands and exit codes
|
|
201
226
|
|
|
202
227
|
`init`, `validate-policy`, `inspect`, `plan`, `sanitize` (needs `--apply`), `rescan`, `review`
|
|
@@ -33,6 +33,7 @@ Report (reporting/) privacy-safe JSON / text summary
|
|
|
33
33
|
| `readers/` | Parse and write each format; limits; encodings; JSON Pointer paths | decide actions |
|
|
34
34
|
| `detectors/` | Produce candidate findings; per-run `DetectorSet` (no global registry) | decide actions |
|
|
35
35
|
| `policy/loader.py` | Safe YAML (no tags, no aliases, no duplicate keys, size limit) | execute code |
|
|
36
|
+
| `policy/layers.py` | Resolve `extends` (shipped packs by name), merge layers, reject weakening | read local files by name |
|
|
36
37
|
| `policy/validator.py` | Schema + semantic checks, compile into immutable `Policy` | — |
|
|
37
38
|
| `policy/precedence.py` | Computed precedence levels and static overlap analysis | — |
|
|
38
39
|
| `policy/context.py` | Pinned vs caller context, run-level gates | infer one context dimension from another |
|
|
@@ -17,6 +17,23 @@
|
|
|
17
17
|
- Environment, target environment and destination are declared by the caller or policy and are
|
|
18
18
|
not verified. CipherGate does not inspect cloud identity or control deployments.
|
|
19
19
|
|
|
20
|
+
## Policy layering
|
|
21
|
+
|
|
22
|
+
- `extends` names shipped packs only; a policy cannot extend a local file in this release.
|
|
23
|
+
- The weakening check compares rule selectors statically and conservatively: it can reject a
|
|
24
|
+
rule that would never actually override the earlier one (narrow the selectors or use
|
|
25
|
+
`relaxes`), and it cannot see differences that depend on detector behaviour.
|
|
26
|
+
- The `us_hipaa` pack does not implement the 3-digit ZIP allowance (whole ZIP codes are
|
|
27
|
+
redacted), cannot check the Safe Harbor "no actual knowledge" condition, does not parse
|
|
28
|
+
photos or biometric data, and cannot know every "other unique identifying number". It is
|
|
29
|
+
not Safe Harbor or Expert Determination by itself.
|
|
30
|
+
- The `eu_gdpr` pack finds most national identifiers by field name only (the Italian codice
|
|
31
|
+
fiscale also by value). It cannot decide a lawful basis, an Art. 9(2) condition or whether a
|
|
32
|
+
destination is outside the EEA.
|
|
33
|
+
- The `india_dpdp` pack finds Aadhaar numbers by value only when written in groups and does not
|
|
34
|
+
verify the Verhoeff checksum; voter ID, passport and licence numbers are found by field name
|
|
35
|
+
only. It cannot check consent, purpose limitation, retention or cross-border rules.
|
|
36
|
+
|
|
20
37
|
## Formats and processing
|
|
21
38
|
|
|
22
39
|
- JSON is parsed as a whole document (default limit 32 MiB). CSV/JSONL/text are streamed with
|
|
@@ -10,6 +10,7 @@ Validate with `ciphergate validate-policy FILE` (never reads data).
|
|
|
10
10
|
|---|---|---|---|
|
|
11
11
|
| `policy_version` | `1` | required | other versions rejected |
|
|
12
12
|
| `name` | slug `[a-z0-9][a-z0-9_.-]{0,63}` | required | also the default pseudonym namespace |
|
|
13
|
+
| `extends` | list of shipped pack names (max 4) | `[]` | build on [policy packs](#layering-extends) |
|
|
13
14
|
| `metadata` | mapping | `{}` | free-form extension namespace; not interpreted |
|
|
14
15
|
| `context` | [Context](#context) | all unset | pinned context |
|
|
15
16
|
| `requirements` | `{environment, target_environment, destination}` booleans | `true, false, true` | which context must be present (a run without a destination is still treated as untrusted; turning the requirement off warns) |
|
|
@@ -181,6 +182,77 @@ future. Requires `strict: true` and the rescan gate; validation always warns, an
|
|
|
181
182
|
expire automatically — review them. No shipped policy contains an
|
|
182
183
|
approval. Rules still apply — an approval does not allow raw data through.
|
|
183
184
|
|
|
185
|
+
## Layering (`extends`)
|
|
186
|
+
|
|
187
|
+
A policy can build on shipped **packs** instead of repeating them:
|
|
188
|
+
|
|
189
|
+
```yaml
|
|
190
|
+
policy_version: 1
|
|
191
|
+
name: my-hospital
|
|
192
|
+
extends: [us_hipaa] # us_hipaa itself extends healthcare_baseline
|
|
193
|
+
context: {environment: prd, target_environment: external, destination: external_ai}
|
|
194
|
+
classifications:
|
|
195
|
+
- {id: local-ids, field_names: [visit_number], category: pii.customer_identifier}
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
Shipped packs (`policies/packs/`, all **templates requiring review**):
|
|
199
|
+
|
|
200
|
+
| Pack | Extends | What it adds |
|
|
201
|
+
|---|---|---|
|
|
202
|
+
| `healthcare_baseline` | — | Strict default for healthcare-style data anywhere: credentials hard-blocked; clinical values blocked toward external/unknown destinations and sent to review elsewhere; identifiers redacted; DOB and clinical dates cut to the year; internal infrastructure redacted; everything else review. Needs no key. |
|
|
203
|
+
| `us_hipaa` | `healthcare_baseline` | Field classifications modelled on the 18 HIPAA Safe Harbor identifier types (geography, dates, ages, record/plan/account numbers, licenses, vehicles, devices, URLs, biometrics, photos); ages banded with 90 and over as `90+`. It is **not** Safe Harbor or Expert Determination by itself: the 3-digit ZIP allowance and the "no actual knowledge" condition are not implemented, binary photos/biometrics are not parsed, and free-text detection is incomplete. |
|
|
204
|
+
| `eu_gdpr` | `healthcare_baseline` | GDPR Art. 9 special-category fields (genetic, biometric, ethnic origin, religion, political opinion, trade union, sex life/orientation) blocked toward external/unknown destinations and reviewed elsewhere; national and health-insurance id fields from several member states plus an Italian codice fiscale value pattern; ages banded. It cannot decide a lawful basis or Chapter V transfers, and pseudonymised data is still personal data, so it redacts by default. |
|
|
205
|
+
| `india_dpdp` | `healthcare_baseline` | Aadhaar (field name, and value when grouped as `1234 5678 9012`; no Verhoeff check; unseparated 12 digits by field name only), ABHA numbers and `@abdm`/`@sbx` addresses, PAN (value and field), EPIC/passport/licence/ration card/UHID fields, PIN codes as address; SPDI-style sensitive fields blocked toward external/unknown destinations and reviewed elsewhere; ages banded. It cannot decide consent, purpose, retention or cross-border rules. |
|
|
206
|
+
|
|
207
|
+
`policies/hospital_us_override.yml` is an example third layer (`ciphergate init --template
|
|
208
|
+
hospital_us_override`). A policy may extend several jurisdiction packs at once (for example
|
|
209
|
+
`extends: [us_hipaa, eu_gdpr, india_dpdp]` for a hospital group); the strictest combination
|
|
210
|
+
applies.
|
|
211
|
+
|
|
212
|
+
**Resolution.** Names are compared to the shipped packs and never used as paths; local files
|
|
213
|
+
cannot be extended in this release. Packs may extend packs (at most 4 levels, 8 layers; cycles
|
|
214
|
+
are rejected). Layers are ordered base first, your policy last; a pack reached twice is used once.
|
|
215
|
+
Every check in this document then runs on the merged policy.
|
|
216
|
+
|
|
217
|
+
**Merge rules — a later layer can never quietly weaken an earlier one:**
|
|
218
|
+
|
|
219
|
+
| Part | Merge | Rejected (validation error) |
|
|
220
|
+
|---|---|---|
|
|
221
|
+
| `context` | later layers may pin unset fields | pinning a different value (`layer_context_conflict`); `allow_cli_override` for a field an earlier layer pinned without allowing overrides (`layer_weakens_context`) |
|
|
222
|
+
| `requirements` | any layer requiring a field requires it | setting `false` where an earlier layer has `true` (`layer_weakens_setting`) |
|
|
223
|
+
| `settings` | stricter value kept: `strict`, `fail_on_parse_error`, `scan_json_keys`, `rescan.enabled` (true), `unknown_sensitive_behavior` (`block` > `review`), `csv_duplicate_headers` (`reject`), `malformed_record_action` (unset); `rescan.fail_on_categories` lists are combined (an unset value contributes every high-risk category) | a weaker value (`layer_weakens_setting`) |
|
|
224
|
+
| `detectors` | custom detectors and internal host suffixes are added; enabling Presidio is allowed | disabling a detector or Presidio that an earlier layer runs, or turning off `reject_risky_regex` (`layer_weakens_detectors` / `layer_weakens_setting`) |
|
|
225
|
+
| `custom_categories`, `transfers` | combined | — |
|
|
226
|
+
| `classifications`, `rules` | combined | reusing an id from an earlier layer (`duplicate_id`) |
|
|
227
|
+
| `defaults` | a later default replaces an earlier one for the same selector | a later default that would take over a category from an earlier default with a weaker action (`layer_weakens_default`) |
|
|
228
|
+
| `name`, `metadata` | the last layer's | — |
|
|
229
|
+
|
|
230
|
+
**Rules.** Actions are ranked `block` > `review` > any transform > `allow`. A later rule that
|
|
231
|
+
[outranks](#precedence-deterministic) an earlier rule it may overlap with and picks a lower-ranked
|
|
232
|
+
action fails with `layer_weakens_rule`. To do it on purpose, name the earlier rule in
|
|
233
|
+
`relaxes`:
|
|
234
|
+
|
|
235
|
+
```yaml
|
|
236
|
+
rules:
|
|
237
|
+
- id: hospital-keep-state
|
|
238
|
+
priority: 300
|
|
239
|
+
match: {categories: [location.coarse], field_names: [state]}
|
|
240
|
+
action: allow
|
|
241
|
+
relaxes: ["defaults:location.*"] # or an earlier rule id
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
Each relaxation is printed as a validation warning and appears in every run report's warnings.
|
|
245
|
+
`hard` and `guardrail` rules from an earlier layer can **never** be relaxed. A later `allow` rule
|
|
246
|
+
over categories an earlier non-allow default handles needs `relaxes: ["defaults:<selector>"]`.
|
|
247
|
+
Swapping one transform for another (for example `pseudonymize` instead of `redact`) needs no
|
|
248
|
+
`relaxes`. `relaxes` is rejected in a policy without `extends`, and must name a rule or default
|
|
249
|
+
from an earlier layer.
|
|
250
|
+
|
|
251
|
+
**Provenance.** `validate-policy` prints the layer chain. Reports add `policy.layers` (each
|
|
252
|
+
layer's name, kind, source and file digest) and each decision's `layer`; `policy.digest` covers
|
|
253
|
+
every layer in order, so editing any pack changes it. With `--path-display redacted` your own
|
|
254
|
+
policy file name is hidden; shipped pack names are kept.
|
|
255
|
+
|
|
184
256
|
## Invalid examples
|
|
185
257
|
|
|
186
258
|
```yaml
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# TEMPLATE REQUIRING REVIEW. Example of the third policy layer: one hospital's own small file on
|
|
2
|
+
# top of the shipped packs (healthcare_baseline -> us_hipaa -> this file). Replace the synthetic
|
|
3
|
+
# formats and field names below with your own. Using it does NOT perform HIPAA Safe Harbor or
|
|
4
|
+
# Expert Determination and is not legal or compliance advice.
|
|
5
|
+
#
|
|
6
|
+
# A layer like this can add context, detectors, classifications and stricter rules. It cannot
|
|
7
|
+
# quietly weaken the packs: a weaker setting or a rule that would override a pack's block,
|
|
8
|
+
# review or redaction with something weaker fails validation unless it names that rule (or
|
|
9
|
+
# default) in `relaxes`, which is then printed as a warning and recorded in every report.
|
|
10
|
+
policy_version: 1
|
|
11
|
+
name: hospital-us-override
|
|
12
|
+
extends: [us_hipaa]
|
|
13
|
+
metadata:
|
|
14
|
+
description: "Example hospital layer for PRD logs going to an external AI assistant. Template requiring review."
|
|
15
|
+
context:
|
|
16
|
+
environment: prd
|
|
17
|
+
target_environment: external
|
|
18
|
+
destination: external_ai
|
|
19
|
+
use_case: debugging
|
|
20
|
+
requirements:
|
|
21
|
+
target_environment: true
|
|
22
|
+
detectors:
|
|
23
|
+
custom:
|
|
24
|
+
# Synthetic local record-number format; replace with the hospital's real format.
|
|
25
|
+
- id: hospital-record-number
|
|
26
|
+
category: pii.customer_identifier
|
|
27
|
+
pattern: '\bHRN-[0-9]{8}\b'
|
|
28
|
+
confidence: 0.9
|
|
29
|
+
classifications:
|
|
30
|
+
- id: hospital-local-ids
|
|
31
|
+
field_names: [visit_number, encounter_id, bed_assignment, wristband_id]
|
|
32
|
+
category: pii.customer_identifier
|
|
33
|
+
- id: hospital-state
|
|
34
|
+
field_names: [state, state_code]
|
|
35
|
+
category: location.coarse
|
|
36
|
+
rules:
|
|
37
|
+
# Safe Harbor keeps the state; the baseline redacts all locations by default, so keeping it
|
|
38
|
+
# is a deliberate, recorded relaxation.
|
|
39
|
+
- id: hospital-keep-state
|
|
40
|
+
priority: 300
|
|
41
|
+
match:
|
|
42
|
+
categories: [location.coarse]
|
|
43
|
+
field_names: [state, state_code]
|
|
44
|
+
action: allow
|
|
45
|
+
relaxes: ["defaults:location.*"]
|
|
46
|
+
reason: state_is_kept_for_regional_debugging
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# TEMPLATE REQUIRING REVIEW. Layering pack for EU healthcare-style data, built on
|
|
2
|
+
# healthcare_baseline. It is modelled on GDPR concepts but using it does NOT make processing
|
|
3
|
+
# lawful and is not legal or compliance advice. GDPR has no fixed identifier list like HIPAA
|
|
4
|
+
# Safe Harbor: whether data is personal depends on whether a person is identifiable by any
|
|
5
|
+
# means reasonably likely to be used (Recital 26). Detection is incomplete, especially in free
|
|
6
|
+
# text and in languages other than English.
|
|
7
|
+
#
|
|
8
|
+
# What this pack adds on top of the baseline:
|
|
9
|
+
# * Art. 9 special categories (health is already covered by the baseline; this adds genetic,
|
|
10
|
+
# biometric, racial or ethnic origin, political opinions, religious or philosophical
|
|
11
|
+
# beliefs, trade union membership, sex life and sexual orientation fields): blocked toward
|
|
12
|
+
# external/unknown destinations, human review everywhere else;
|
|
13
|
+
# * national and health-insurance identifier fields from several member states, plus a value
|
|
14
|
+
# pattern for the Italian codice fiscale; other national numbers are found by field name only;
|
|
15
|
+
# * ages banded by 10 (90 and over as "90+"), like the other jurisdiction packs.
|
|
16
|
+
#
|
|
17
|
+
# Things this pack cannot decide for you:
|
|
18
|
+
# * a lawful basis and an Art. 9(2) condition for the processing;
|
|
19
|
+
# * Chapter V international transfers: an `external_ai` or `external_vendor` destination may
|
|
20
|
+
# be outside the EEA, and CipherGate does not know where a destination is;
|
|
21
|
+
# * whether a keyed pseudonym is acceptable: pseudonymised data is still personal data
|
|
22
|
+
# (Art. 4(5), Recital 26), so this pack redacts rather than pseudonymizes by default.
|
|
23
|
+
policy_version: 1
|
|
24
|
+
name: eu-gdpr
|
|
25
|
+
extends: [healthcare_baseline]
|
|
26
|
+
metadata:
|
|
27
|
+
description: "EU healthcare pack modelled on GDPR special categories and member-state identifiers. Template requiring review; not legal advice."
|
|
28
|
+
pack_version: 1
|
|
29
|
+
custom_categories:
|
|
30
|
+
- pii.age
|
|
31
|
+
- pii.special_category
|
|
32
|
+
- pii.biometric_identifier
|
|
33
|
+
detectors:
|
|
34
|
+
custom:
|
|
35
|
+
# Italian codice fiscale: 6 letters, 2 digits, month letter, 2 digits, letter, 3 digits, letter.
|
|
36
|
+
- id: eu-it-codice-fiscale
|
|
37
|
+
category: pii.government_id
|
|
38
|
+
pattern: '\b[A-Z]{6}[0-9]{2}[ABCDEHLMPRST][0-9]{2}[A-Z][0-9]{3}[A-Z]\b'
|
|
39
|
+
confidence: 0.85
|
|
40
|
+
classifications:
|
|
41
|
+
- id: eu-special-categories
|
|
42
|
+
field_names: [ethnicity, ethnic_origin, race, racial_origin, religion, religious_belief, philosophical_belief, political_opinion, political_party, party_affiliation, trade_union, union_membership, sexual_orientation, sex_life, gender_identity, genetic_data, genotype, dna_sequence, genetic_test_result]
|
|
43
|
+
category: pii.special_category
|
|
44
|
+
- id: eu-biometrics
|
|
45
|
+
field_names: [fingerprint, face_template, facial_image, iris_scan, retina_scan, voiceprint, biometric_template]
|
|
46
|
+
category: pii.biometric_identifier
|
|
47
|
+
- id: eu-national-ids
|
|
48
|
+
field_names: [codice_fiscale, steuer_id, steueridentifikationsnummer, bsn, burgerservicenummer, nir, numero_securite_sociale, dni, nie, nif, pesel, personnummer, henkilotunnus, cpr_number, rodne_cislo, ppsn, ehic_number, health_insurance_number, krankenversicherungsnummer, carte_vitale_number]
|
|
49
|
+
category: pii.government_id
|
|
50
|
+
- id: eu-age
|
|
51
|
+
field_names: [age, patient_age, age_years, alter, edad, eta]
|
|
52
|
+
category: pii.age
|
|
53
|
+
rules:
|
|
54
|
+
- id: eu-block-special-categories-external
|
|
55
|
+
priority: 800
|
|
56
|
+
match:
|
|
57
|
+
destinations: [external_vendor, external_ai, public_repository, unknown]
|
|
58
|
+
categories: [pii.special_category, pii.biometric_identifier]
|
|
59
|
+
action: block
|
|
60
|
+
reason: special_category_data_needs_review_before_external_release
|
|
61
|
+
|
|
62
|
+
- id: eu-review-special-categories
|
|
63
|
+
priority: 400
|
|
64
|
+
match:
|
|
65
|
+
categories: [pii.special_category, pii.biometric_identifier]
|
|
66
|
+
action: review
|
|
67
|
+
reason: special_category_data_requires_human_decision
|
|
68
|
+
|
|
69
|
+
- id: eu-age-band
|
|
70
|
+
priority: 300
|
|
71
|
+
match:
|
|
72
|
+
categories: [pii.age]
|
|
73
|
+
action: generalize
|
|
74
|
+
params:
|
|
75
|
+
strategy: age_band
|
|
76
|
+
band_size: 10
|
|
77
|
+
max_age: 90
|
|
78
|
+
reason: ages_banded
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
# TEMPLATE REQUIRING REVIEW. Layering pack: a strict starting point for healthcare-style data
|
|
2
|
+
# in any country. Name it in `extends:` (or extend a jurisdiction pack such as us_hipaa) and add
|
|
3
|
+
# your own file on top. It is NOT legal or compliance advice and NOT a de-identification method:
|
|
4
|
+
# clinical free text, rare conditions, small geographies and combinations of quasi-identifiers
|
|
5
|
+
# can remain identifying, and detection can miss values. Expert review is still needed.
|
|
6
|
+
#
|
|
7
|
+
# What it does, for every destination:
|
|
8
|
+
# * credential findings are hard-blocked (no later layer can weaken that);
|
|
9
|
+
# * clinical values (diagnosis, medication, ...): blocked toward external/unknown destinations,
|
|
10
|
+
# human review everywhere else;
|
|
11
|
+
# * direct identifiers (pii.*, patient ids, health/financial/employment fields): redacted;
|
|
12
|
+
# * dates of birth and clinical dates: reduced to the year;
|
|
13
|
+
# * internal infrastructure (hostnames, internal URLs/IPs, trace ids): redacted;
|
|
14
|
+
# * anything else that looks sensitive: review.
|
|
15
|
+
# No key is needed. Later layers may swap a transform for another (for example keyed
|
|
16
|
+
# pseudonyms), but weakening a block/review or allowing a value needs an explicit `relaxes`.
|
|
17
|
+
policy_version: 1
|
|
18
|
+
name: healthcare-baseline
|
|
19
|
+
metadata:
|
|
20
|
+
description: "Strict global healthcare baseline for policy layering. Template requiring review; not a compliance claim."
|
|
21
|
+
pack_version: 1
|
|
22
|
+
context:
|
|
23
|
+
domain_profile: healthcare
|
|
24
|
+
requirements:
|
|
25
|
+
environment: true
|
|
26
|
+
destination: true
|
|
27
|
+
settings:
|
|
28
|
+
strict: true
|
|
29
|
+
scan_json_keys: true
|
|
30
|
+
fail_on_parse_error: true
|
|
31
|
+
unknown_sensitive_behavior: review
|
|
32
|
+
custom_categories:
|
|
33
|
+
- health.clinical_date
|
|
34
|
+
classifications:
|
|
35
|
+
- id: baseline-clinical-dates
|
|
36
|
+
field_names: [admission_date, discharge_date, visit_date, procedure_date, encounter_date, death_date, date_of_death]
|
|
37
|
+
category: health.clinical_date
|
|
38
|
+
confidence: 0.9
|
|
39
|
+
- id: baseline-clinical-values
|
|
40
|
+
field_names: [diagnoses, problem_list, lab_result, test_result, symptoms, treatment, chief_complaint]
|
|
41
|
+
category: health.phi
|
|
42
|
+
confidence: 0.8
|
|
43
|
+
rules:
|
|
44
|
+
- id: baseline-block-credentials
|
|
45
|
+
hard: true
|
|
46
|
+
priority: 1000
|
|
47
|
+
match:
|
|
48
|
+
categories: [credential.secret]
|
|
49
|
+
action: block
|
|
50
|
+
reason: credentials_never_in_health_data
|
|
51
|
+
|
|
52
|
+
- id: baseline-block-clinical-values-external
|
|
53
|
+
priority: 800
|
|
54
|
+
match:
|
|
55
|
+
destinations: [external_vendor, external_ai, public_repository, unknown]
|
|
56
|
+
categories: [health.phi]
|
|
57
|
+
field_names: [diagnosis, diagnoses, medication, medications, condition, allergies, prescription, clinical_notes, icd_code, procedure_code, problem_list, lab_result, test_result, symptoms, treatment, chief_complaint]
|
|
58
|
+
action: block
|
|
59
|
+
reason: clinical_values_need_expert_review_before_external_release
|
|
60
|
+
|
|
61
|
+
- id: baseline-review-clinical-values
|
|
62
|
+
priority: 400
|
|
63
|
+
match:
|
|
64
|
+
categories: [health.phi]
|
|
65
|
+
field_names: [diagnosis, diagnoses, medication, medications, condition, allergies, prescription, clinical_notes, icd_code, procedure_code, problem_list, lab_result, test_result, symptoms, treatment, chief_complaint]
|
|
66
|
+
action: review
|
|
67
|
+
reason: clinical_values_require_human_decision
|
|
68
|
+
|
|
69
|
+
- id: baseline-dob-year
|
|
70
|
+
priority: 300
|
|
71
|
+
match:
|
|
72
|
+
categories: [pii.date_of_birth]
|
|
73
|
+
action: generalize
|
|
74
|
+
params:
|
|
75
|
+
strategy: year
|
|
76
|
+
reason: keep_birth_year_only
|
|
77
|
+
|
|
78
|
+
- id: baseline-clinical-dates-year
|
|
79
|
+
priority: 300
|
|
80
|
+
match:
|
|
81
|
+
categories: [health.clinical_date]
|
|
82
|
+
action: generalize
|
|
83
|
+
params:
|
|
84
|
+
strategy: year
|
|
85
|
+
reason: keep_clinical_year_only
|
|
86
|
+
|
|
87
|
+
- id: baseline-redact-identifiers
|
|
88
|
+
priority: 200
|
|
89
|
+
match:
|
|
90
|
+
categories: [pii.*, health.phi, financial.sensitive, employment.sensitive]
|
|
91
|
+
action: redact
|
|
92
|
+
reason: remove_identifier
|
|
93
|
+
|
|
94
|
+
- id: baseline-redact-infrastructure
|
|
95
|
+
priority: 200
|
|
96
|
+
match:
|
|
97
|
+
categories: [network.telemetry, security.internal]
|
|
98
|
+
action: redact
|
|
99
|
+
reason: remove_internal_infrastructure_detail
|
|
100
|
+
|
|
101
|
+
defaults:
|
|
102
|
+
location.*:
|
|
103
|
+
action: redact
|
|
104
|
+
identifier.*:
|
|
105
|
+
action: redact
|
|
106
|
+
proprietary.*:
|
|
107
|
+
action: review
|
|
108
|
+
"*":
|
|
109
|
+
action: review
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
# TEMPLATE REQUIRING REVIEW. Layering pack for Indian healthcare-style data, built on
|
|
2
|
+
# healthcare_baseline. Its field list draws on the Digital Personal Data Protection Act, 2023,
|
|
3
|
+
# the sensitive personal data list in the IT (SPDI) Rules, 2011 and identifiers used by the
|
|
4
|
+
# Ayushman Bharat Digital Mission (ABDM). Using it does NOT establish that processing meets the
|
|
5
|
+
# DPDP Act, its rules, the Aadhaar Act or ABDM policies, and it is not legal or compliance
|
|
6
|
+
# advice. Detection is incomplete, especially in free text and in Indian languages.
|
|
7
|
+
#
|
|
8
|
+
# What this pack adds on top of the baseline:
|
|
9
|
+
# * Aadhaar numbers: by field name, and by value when written in groups ("1234 5678 9012" or
|
|
10
|
+
# with hyphens). An unseparated 12-digit Aadhaar is found by field name only (too many log
|
|
11
|
+
# values are 12-digit numbers), and the Verhoeff checksum is not checked. Aadhaar is fully
|
|
12
|
+
# redacted, not masked to the last 4 digits;
|
|
13
|
+
# * ABHA (health account) numbers ("12-3456-7890-1234") and ABHA addresses ("name@abdm",
|
|
14
|
+
# "name@sbx") by value and field name;
|
|
15
|
+
# * PAN by value (the 4th letter must be a valid holder type) and field name;
|
|
16
|
+
# * voter ID (EPIC), passport, driving licence, ration card, UHID/IP/OP numbers by field name;
|
|
17
|
+
# * PIN codes treated as address; SPDI-style sensitive fields (sexual orientation, biometric
|
|
18
|
+
# data) blocked toward external/unknown destinations and reviewed elsewhere;
|
|
19
|
+
# * ages banded by 10 (90 and over as "90+"), like the other jurisdiction packs.
|
|
20
|
+
#
|
|
21
|
+
# Things this pack cannot decide for you: notice and consent (or another legitimate use),
|
|
22
|
+
# purpose limitation, retention, rules on processing children's data, and cross-border
|
|
23
|
+
# transfer restrictions for the destination you name.
|
|
24
|
+
policy_version: 1
|
|
25
|
+
name: india-dpdp
|
|
26
|
+
extends: [healthcare_baseline]
|
|
27
|
+
metadata:
|
|
28
|
+
description: "India healthcare pack (Aadhaar, ABHA, PAN and SPDI-style sensitive fields). Template requiring review; not legal advice."
|
|
29
|
+
pack_version: 1
|
|
30
|
+
custom_categories:
|
|
31
|
+
- pii.age
|
|
32
|
+
- pii.special_category
|
|
33
|
+
- pii.biometric_identifier
|
|
34
|
+
detectors:
|
|
35
|
+
custom:
|
|
36
|
+
- id: in-aadhaar-grouped
|
|
37
|
+
category: pii.government_id
|
|
38
|
+
pattern: '\b[2-9][0-9]{3}[ -][0-9]{4}[ -][0-9]{4}\b'
|
|
39
|
+
confidence: 0.75
|
|
40
|
+
- id: in-abha-number
|
|
41
|
+
category: health.phi
|
|
42
|
+
pattern: '\b[0-9]{2}-[0-9]{4}-[0-9]{4}-[0-9]{4}\b'
|
|
43
|
+
confidence: 0.85
|
|
44
|
+
- id: in-abha-address
|
|
45
|
+
category: health.phi
|
|
46
|
+
pattern: '\b[a-z0-9][a-z0-9._]{2,31}@(abdm|sbx)\b'
|
|
47
|
+
confidence: 0.85
|
|
48
|
+
ignore_case: true
|
|
49
|
+
- id: in-pan
|
|
50
|
+
category: pii.government_id
|
|
51
|
+
pattern: '\b[A-Z]{3}[ABCFGHJLPT][A-Z][0-9]{4}[A-Z]\b'
|
|
52
|
+
confidence: 0.8
|
|
53
|
+
classifications:
|
|
54
|
+
- id: in-government-ids
|
|
55
|
+
field_names: [aadhaar, aadhaar_number, aadhaar_no, uid, uidai_number, pan, pan_number, pan_no, epic_number, voter_id, passport_no, driving_licence, driving_licence_number, dl_number, ration_card_number]
|
|
56
|
+
category: pii.government_id
|
|
57
|
+
- id: in-health-ids
|
|
58
|
+
field_names: [abha, abha_number, abha_id, abha_address, health_id, uhid, ip_number, op_number, opd_number, ipd_number, ayushman_card_number, pmjay_id]
|
|
59
|
+
category: health.phi
|
|
60
|
+
- id: in-pin-code
|
|
61
|
+
field_names: [pincode, pin_code, postal_pin]
|
|
62
|
+
category: pii.address
|
|
63
|
+
- id: in-sensitive
|
|
64
|
+
field_names: [sexual_orientation, caste, religion, gender_identity]
|
|
65
|
+
category: pii.special_category
|
|
66
|
+
- id: in-biometrics
|
|
67
|
+
field_names: [fingerprint, iris_scan, face_template, biometric_template]
|
|
68
|
+
category: pii.biometric_identifier
|
|
69
|
+
- id: in-age
|
|
70
|
+
field_names: [age, patient_age, age_years, umar]
|
|
71
|
+
category: pii.age
|
|
72
|
+
rules:
|
|
73
|
+
- id: in-block-sensitive-external
|
|
74
|
+
priority: 800
|
|
75
|
+
match:
|
|
76
|
+
destinations: [external_vendor, external_ai, public_repository, unknown]
|
|
77
|
+
categories: [pii.special_category, pii.biometric_identifier]
|
|
78
|
+
action: block
|
|
79
|
+
reason: sensitive_personal_data_needs_review_before_external_release
|
|
80
|
+
|
|
81
|
+
- id: in-review-sensitive
|
|
82
|
+
priority: 400
|
|
83
|
+
match:
|
|
84
|
+
categories: [pii.special_category, pii.biometric_identifier]
|
|
85
|
+
action: review
|
|
86
|
+
reason: sensitive_personal_data_requires_human_decision
|
|
87
|
+
|
|
88
|
+
- id: in-age-band
|
|
89
|
+
priority: 300
|
|
90
|
+
match:
|
|
91
|
+
categories: [pii.age]
|
|
92
|
+
action: generalize
|
|
93
|
+
params:
|
|
94
|
+
strategy: age_band
|
|
95
|
+
band_size: 10
|
|
96
|
+
max_age: 90
|
|
97
|
+
reason: ages_banded
|