ciphergate-cli 0.12.0__tar.gz → 0.13.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.12.0 → ciphergate_cli-0.13.0}/CHANGELOG.md +15 -1
- {ciphergate_cli-0.12.0 → ciphergate_cli-0.13.0}/PKG-INFO +2 -2
- {ciphergate_cli-0.12.0 → ciphergate_cli-0.13.0}/README.md +1 -1
- {ciphergate_cli-0.12.0 → ciphergate_cli-0.13.0}/docs/detector-coverage.md +1 -1
- {ciphergate_cli-0.12.0 → ciphergate_cli-0.13.0}/src/ciphergate/__init__.py +1 -1
- {ciphergate_cli-0.12.0 → ciphergate_cli-0.13.0}/src/ciphergate/detectors/payment_card.py +26 -5
- {ciphergate_cli-0.12.0 → ciphergate_cli-0.13.0}/tests/unit/test_detectors.py +50 -4
- {ciphergate_cli-0.12.0 → ciphergate_cli-0.13.0}/.gitignore +0 -0
- {ciphergate_cli-0.12.0 → ciphergate_cli-0.13.0}/LICENSE +0 -0
- {ciphergate_cli-0.12.0 → ciphergate_cli-0.13.0}/NOTICE +0 -0
- {ciphergate_cli-0.12.0 → ciphergate_cli-0.13.0}/SECURITY.md +0 -0
- {ciphergate_cli-0.12.0 → ciphergate_cli-0.13.0}/docs/architecture.md +0 -0
- {ciphergate_cli-0.12.0 → ciphergate_cli-0.13.0}/docs/claude-code-tooling.md +0 -0
- {ciphergate_cli-0.12.0 → ciphergate_cli-0.13.0}/docs/examples.md +0 -0
- {ciphergate_cli-0.12.0 → ciphergate_cli-0.13.0}/docs/limitations.md +0 -0
- {ciphergate_cli-0.12.0 → ciphergate_cli-0.13.0}/docs/policy-schema.md +0 -0
- {ciphergate_cli-0.12.0 → ciphergate_cli-0.13.0}/docs/security-and-privacy.md +0 -0
- {ciphergate_cli-0.12.0 → ciphergate_cli-0.13.0}/docs/threat-model.md +0 -0
- {ciphergate_cli-0.12.0 → ciphergate_cli-0.13.0}/examples/expected/sample.dev_local_dev.csv +0 -0
- {ciphergate_cli-0.12.0 → ciphergate_cli-0.13.0}/examples/expected/sample.dev_local_dev.json +0 -0
- {ciphergate_cli-0.12.0 → ciphergate_cli-0.13.0}/examples/expected/sample.dev_local_dev.jsonl +0 -0
- {ciphergate_cli-0.12.0 → ciphergate_cli-0.13.0}/examples/expected/sample.dev_local_dev.log +0 -0
- {ciphergate_cli-0.12.0 → ciphergate_cli-0.13.0}/examples/sample.csv +0 -0
- {ciphergate_cli-0.12.0 → ciphergate_cli-0.13.0}/examples/sample.json +0 -0
- {ciphergate_cli-0.12.0 → ciphergate_cli-0.13.0}/examples/sample.jsonl +0 -0
- {ciphergate_cli-0.12.0 → ciphergate_cli-0.13.0}/examples/sample.log +0 -0
- {ciphergate_cli-0.12.0 → ciphergate_cli-0.13.0}/policies/dev_local.yml +0 -0
- {ciphergate_cli-0.12.0 → ciphergate_cli-0.13.0}/policies/developer_logs.yml +0 -0
- {ciphergate_cli-0.12.0 → ciphergate_cli-0.13.0}/policies/external_ai.yml +0 -0
- {ciphergate_cli-0.12.0 → ciphergate_cli-0.13.0}/policies/financial_template.yml +0 -0
- {ciphergate_cli-0.12.0 → ciphergate_cli-0.13.0}/policies/healthcare_template.yml +0 -0
- {ciphergate_cli-0.12.0 → ciphergate_cli-0.13.0}/policies/local_debug.yml +0 -0
- {ciphergate_cli-0.12.0 → ciphergate_cli-0.13.0}/policies/multi_environment.yml +0 -0
- {ciphergate_cli-0.12.0 → ciphergate_cli-0.13.0}/policies/prd_external_ai.yml +0 -0
- {ciphergate_cli-0.12.0 → ciphergate_cli-0.13.0}/policies/stg_test.yml +0 -0
- {ciphergate_cli-0.12.0 → ciphergate_cli-0.13.0}/policies/test_dataset.yml +0 -0
- {ciphergate_cli-0.12.0 → ciphergate_cli-0.13.0}/pyproject.toml +0 -0
- {ciphergate_cli-0.12.0 → ciphergate_cli-0.13.0}/scripts/benchmark/README.md +0 -0
- {ciphergate_cli-0.12.0 → ciphergate_cli-0.13.0}/src/ciphergate/__main__.py +0 -0
- {ciphergate_cli-0.12.0 → ciphergate_cli-0.13.0}/src/ciphergate/cli.py +0 -0
- {ciphergate_cli-0.12.0 → ciphergate_cli-0.13.0}/src/ciphergate/config.py +0 -0
- {ciphergate_cli-0.12.0 → ciphergate_cli-0.13.0}/src/ciphergate/constants.py +0 -0
- {ciphergate_cli-0.12.0 → ciphergate_cli-0.13.0}/src/ciphergate/detectors/__init__.py +0 -0
- {ciphergate_cli-0.12.0 → ciphergate_cli-0.13.0}/src/ciphergate/detectors/base.py +0 -0
- {ciphergate_cli-0.12.0 → ciphergate_cli-0.13.0}/src/ciphergate/detectors/custom_regex.py +0 -0
- {ciphergate_cli-0.12.0 → ciphergate_cli-0.13.0}/src/ciphergate/detectors/dates.py +0 -0
- {ciphergate_cli-0.12.0 → ciphergate_cli-0.13.0}/src/ciphergate/detectors/email.py +0 -0
- {ciphergate_cli-0.12.0 → ciphergate_cli-0.13.0}/src/ciphergate/detectors/field_names.py +0 -0
- {ciphergate_cli-0.12.0 → ciphergate_cli-0.13.0}/src/ciphergate/detectors/iban.py +0 -0
- {ciphergate_cli-0.12.0 → ciphergate_cli-0.13.0}/src/ciphergate/detectors/ip_address.py +0 -0
- {ciphergate_cli-0.12.0 → ciphergate_cli-0.13.0}/src/ciphergate/detectors/mac_address.py +0 -0
- {ciphergate_cli-0.12.0 → ciphergate_cli-0.13.0}/src/ciphergate/detectors/network.py +0 -0
- {ciphergate_cli-0.12.0 → ciphergate_cli-0.13.0}/src/ciphergate/detectors/phone.py +0 -0
- {ciphergate_cli-0.12.0 → ciphergate_cli-0.13.0}/src/ciphergate/detectors/presidio_ner.py +0 -0
- {ciphergate_cli-0.12.0 → ciphergate_cli-0.13.0}/src/ciphergate/detectors/regex_safety.py +0 -0
- {ciphergate_cli-0.12.0 → ciphergate_cli-0.13.0}/src/ciphergate/detectors/registry.py +0 -0
- {ciphergate_cli-0.12.0 → ciphergate_cli-0.13.0}/src/ciphergate/detectors/secrets.py +0 -0
- {ciphergate_cli-0.12.0 → ciphergate_cli-0.13.0}/src/ciphergate/detectors/ssn.py +0 -0
- {ciphergate_cli-0.12.0 → ciphergate_cli-0.13.0}/src/ciphergate/errors.py +0 -0
- {ciphergate_cli-0.12.0 → ciphergate_cli-0.13.0}/src/ciphergate/models/__init__.py +0 -0
- {ciphergate_cli-0.12.0 → ciphergate_cli-0.13.0}/src/ciphergate/models/context.py +0 -0
- {ciphergate_cli-0.12.0 → ciphergate_cli-0.13.0}/src/ciphergate/models/finding.py +0 -0
- {ciphergate_cli-0.12.0 → ciphergate_cli-0.13.0}/src/ciphergate/models/policy.py +0 -0
- {ciphergate_cli-0.12.0 → ciphergate_cli-0.13.0}/src/ciphergate/models/report.py +0 -0
- {ciphergate_cli-0.12.0 → ciphergate_cli-0.13.0}/src/ciphergate/pipeline/__init__.py +0 -0
- {ciphergate_cli-0.12.0 → ciphergate_cli-0.13.0}/src/ciphergate/pipeline/atomic_output.py +0 -0
- {ciphergate_cli-0.12.0 → ciphergate_cli-0.13.0}/src/ciphergate/pipeline/batch.py +0 -0
- {ciphergate_cli-0.12.0 → ciphergate_cli-0.13.0}/src/ciphergate/pipeline/inspector.py +0 -0
- {ciphergate_cli-0.12.0 → ciphergate_cli-0.13.0}/src/ciphergate/pipeline/paths.py +0 -0
- {ciphergate_cli-0.12.0 → ciphergate_cli-0.13.0}/src/ciphergate/pipeline/planner.py +0 -0
- {ciphergate_cli-0.12.0 → ciphergate_cli-0.13.0}/src/ciphergate/pipeline/rescan.py +0 -0
- {ciphergate_cli-0.12.0 → ciphergate_cli-0.13.0}/src/ciphergate/pipeline/sanitizer.py +0 -0
- {ciphergate_cli-0.12.0 → ciphergate_cli-0.13.0}/src/ciphergate/pipeline/stdio.py +0 -0
- {ciphergate_cli-0.12.0 → ciphergate_cli-0.13.0}/src/ciphergate/pipeline/streams.py +0 -0
- {ciphergate_cli-0.12.0 → ciphergate_cli-0.13.0}/src/ciphergate/policy/__init__.py +0 -0
- {ciphergate_cli-0.12.0 → ciphergate_cli-0.13.0}/src/ciphergate/policy/context.py +0 -0
- {ciphergate_cli-0.12.0 → ciphergate_cli-0.13.0}/src/ciphergate/policy/engine.py +0 -0
- {ciphergate_cli-0.12.0 → ciphergate_cli-0.13.0}/src/ciphergate/policy/loader.py +0 -0
- {ciphergate_cli-0.12.0 → ciphergate_cli-0.13.0}/src/ciphergate/policy/precedence.py +0 -0
- {ciphergate_cli-0.12.0 → ciphergate_cli-0.13.0}/src/ciphergate/policy/templates.py +0 -0
- {ciphergate_cli-0.12.0 → ciphergate_cli-0.13.0}/src/ciphergate/policy/validator.py +0 -0
- {ciphergate_cli-0.12.0 → ciphergate_cli-0.13.0}/src/ciphergate/py.typed +0 -0
- {ciphergate_cli-0.12.0 → ciphergate_cli-0.13.0}/src/ciphergate/readers/__init__.py +0 -0
- {ciphergate_cli-0.12.0 → ciphergate_cli-0.13.0}/src/ciphergate/readers/accesslog_reader.py +0 -0
- {ciphergate_cli-0.12.0 → ciphergate_cli-0.13.0}/src/ciphergate/readers/base.py +0 -0
- {ciphergate_cli-0.12.0 → ciphergate_cli-0.13.0}/src/ciphergate/readers/csv_reader.py +0 -0
- {ciphergate_cli-0.12.0 → ciphergate_cli-0.13.0}/src/ciphergate/readers/json_reader.py +0 -0
- {ciphergate_cli-0.12.0 → ciphergate_cli-0.13.0}/src/ciphergate/readers/jsonl_reader.py +0 -0
- {ciphergate_cli-0.12.0 → ciphergate_cli-0.13.0}/src/ciphergate/readers/jsonlog_reader.py +0 -0
- {ciphergate_cli-0.12.0 → ciphergate_cli-0.13.0}/src/ciphergate/readers/line_structure.py +0 -0
- {ciphergate_cli-0.12.0 → ciphergate_cli-0.13.0}/src/ciphergate/readers/logfmt_reader.py +0 -0
- {ciphergate_cli-0.12.0 → ciphergate_cli-0.13.0}/src/ciphergate/readers/stacktrace_reader.py +0 -0
- {ciphergate_cli-0.12.0 → ciphergate_cli-0.13.0}/src/ciphergate/readers/syslog_reader.py +0 -0
- {ciphergate_cli-0.12.0 → ciphergate_cli-0.13.0}/src/ciphergate/readers/text_reader.py +0 -0
- {ciphergate_cli-0.12.0 → ciphergate_cli-0.13.0}/src/ciphergate/reporting/__init__.py +0 -0
- {ciphergate_cli-0.12.0 → ciphergate_cli-0.13.0}/src/ciphergate/reporting/audit.py +0 -0
- {ciphergate_cli-0.12.0 → ciphergate_cli-0.13.0}/src/ciphergate/reporting/next_steps.py +0 -0
- {ciphergate_cli-0.12.0 → ciphergate_cli-0.13.0}/src/ciphergate/reporting/safe_text.py +0 -0
- {ciphergate_cli-0.12.0 → ciphergate_cli-0.13.0}/src/ciphergate/reporting/serializer.py +0 -0
- {ciphergate_cli-0.12.0 → ciphergate_cli-0.13.0}/src/ciphergate/reporting/terminal.py +0 -0
- {ciphergate_cli-0.12.0 → ciphergate_cli-0.13.0}/src/ciphergate/templates/starter_policy.yml +0 -0
- {ciphergate_cli-0.12.0 → ciphergate_cli-0.13.0}/src/ciphergate/transforms/__init__.py +0 -0
- {ciphergate_cli-0.12.0 → ciphergate_cli-0.13.0}/src/ciphergate/transforms/date_shift.py +0 -0
- {ciphergate_cli-0.12.0 → ciphergate_cli-0.13.0}/src/ciphergate/transforms/generalize.py +0 -0
- {ciphergate_cli-0.12.0 → ciphergate_cli-0.13.0}/src/ciphergate/transforms/keys.py +0 -0
- {ciphergate_cli-0.12.0 → ciphergate_cli-0.13.0}/src/ciphergate/transforms/pseudonymize.py +0 -0
- {ciphergate_cli-0.12.0 → ciphergate_cli-0.13.0}/src/ciphergate/transforms/redact.py +0 -0
- {ciphergate_cli-0.12.0 → ciphergate_cli-0.13.0}/src/ciphergate/transforms/registry.py +0 -0
- {ciphergate_cli-0.12.0 → ciphergate_cli-0.13.0}/src/ciphergate/transforms/replace.py +0 -0
- {ciphergate_cli-0.12.0 → ciphergate_cli-0.13.0}/tests/__init__.py +0 -0
- {ciphergate_cli-0.12.0 → ciphergate_cli-0.13.0}/tests/conftest.py +0 -0
- {ciphergate_cli-0.12.0 → ciphergate_cli-0.13.0}/tests/integration/__init__.py +0 -0
- {ciphergate_cli-0.12.0 → ciphergate_cli-0.13.0}/tests/integration/test_api_batch_streams.py +0 -0
- {ciphergate_cli-0.12.0 → ciphergate_cli-0.13.0}/tests/integration/test_atomic_output.py +0 -0
- {ciphergate_cli-0.12.0 → ciphergate_cli-0.13.0}/tests/integration/test_batch.py +0 -0
- {ciphergate_cli-0.12.0 → ciphergate_cli-0.13.0}/tests/integration/test_cli_e2e.py +0 -0
- {ciphergate_cli-0.12.0 → ciphergate_cli-0.13.0}/tests/integration/test_domain_templates.py +0 -0
- {ciphergate_cli-0.12.0 → ciphergate_cli-0.13.0}/tests/integration/test_environment_matrix.py +0 -0
- {ciphergate_cli-0.12.0 → ciphergate_cli-0.13.0}/tests/integration/test_pipelines.py +0 -0
- {ciphergate_cli-0.12.0 → ciphergate_cli-0.13.0}/tests/integration/test_stdio.py +0 -0
- {ciphergate_cli-0.12.0 → ciphergate_cli-0.13.0}/tests/regression/__init__.py +0 -0
- {ciphergate_cli-0.12.0 → ciphergate_cli-0.13.0}/tests/regression/test_known_cases.py +0 -0
- {ciphergate_cli-0.12.0 → ciphergate_cli-0.13.0}/tests/security/__init__.py +0 -0
- {ciphergate_cli-0.12.0 → ciphergate_cli-0.13.0}/tests/security/test_key_file.py +0 -0
- {ciphergate_cli-0.12.0 → ciphergate_cli-0.13.0}/tests/security/test_no_network.py +0 -0
- {ciphergate_cli-0.12.0 → ciphergate_cli-0.13.0}/tests/security/test_no_raw_value_leaks.py +0 -0
- {ciphergate_cli-0.12.0 → ciphergate_cli-0.13.0}/tests/security/test_path_safety.py +0 -0
- {ciphergate_cli-0.12.0 → ciphergate_cli-0.13.0}/tests/security/test_policy_fail_closed.py +0 -0
- {ciphergate_cli-0.12.0 → ciphergate_cli-0.13.0}/tests/security/test_review_findings.py +0 -0
- {ciphergate_cli-0.12.0 → ciphergate_cli-0.13.0}/tests/tooling/__init__.py +0 -0
- {ciphergate_cli-0.12.0 → ciphergate_cli-0.13.0}/tests/tooling/test_benchmark.py +0 -0
- {ciphergate_cli-0.12.0 → ciphergate_cli-0.13.0}/tests/tooling/test_ci_supply_chain.py +0 -0
- {ciphergate_cli-0.12.0 → ciphergate_cli-0.13.0}/tests/tooling/test_claude_tooling.py +0 -0
- {ciphergate_cli-0.12.0 → ciphergate_cli-0.13.0}/tests/tooling/test_git_tooling.py +0 -0
- {ciphergate_cli-0.12.0 → ciphergate_cli-0.13.0}/tests/tooling/test_publish_workflow.py +0 -0
- {ciphergate_cli-0.12.0 → ciphergate_cli-0.13.0}/tests/unit/__init__.py +0 -0
- {ciphergate_cli-0.12.0 → ciphergate_cli-0.13.0}/tests/unit/test_accesslog.py +0 -0
- {ciphergate_cli-0.12.0 → ciphergate_cli-0.13.0}/tests/unit/test_jsonlog.py +0 -0
- {ciphergate_cli-0.12.0 → ciphergate_cli-0.13.0}/tests/unit/test_logfmt.py +0 -0
- {ciphergate_cli-0.12.0 → ciphergate_cli-0.13.0}/tests/unit/test_next_steps.py +0 -0
- {ciphergate_cli-0.12.0 → ciphergate_cli-0.13.0}/tests/unit/test_policy_engine.py +0 -0
- {ciphergate_cli-0.12.0 → ciphergate_cli-0.13.0}/tests/unit/test_policy_precedence.py +0 -0
- {ciphergate_cli-0.12.0 → ciphergate_cli-0.13.0}/tests/unit/test_policy_templates.py +0 -0
- {ciphergate_cli-0.12.0 → ciphergate_cli-0.13.0}/tests/unit/test_policy_validation.py +0 -0
- {ciphergate_cli-0.12.0 → ciphergate_cli-0.13.0}/tests/unit/test_presidio_ner.py +0 -0
- {ciphergate_cli-0.12.0 → ciphergate_cli-0.13.0}/tests/unit/test_properties.py +0 -0
- {ciphergate_cli-0.12.0 → ciphergate_cli-0.13.0}/tests/unit/test_readers.py +0 -0
- {ciphergate_cli-0.12.0 → ciphergate_cli-0.13.0}/tests/unit/test_stacktrace.py +0 -0
- {ciphergate_cli-0.12.0 → ciphergate_cli-0.13.0}/tests/unit/test_syslog.py +0 -0
- {ciphergate_cli-0.12.0 → ciphergate_cli-0.13.0}/tests/unit/test_transforms.py +0 -0
|
@@ -6,6 +6,19 @@ versioning: [PEP 440](https://peps.python.org/pep-0440/) with SemVer meaning (pr
|
|
|
6
6
|
|
|
7
7
|
## [Unreleased]
|
|
8
8
|
|
|
9
|
+
## [0.13.0] - 2026-11-02
|
|
10
|
+
|
|
11
|
+
Fewer random IDs are reported as payment cards. Cards with an unrecognised prefix and no card
|
|
12
|
+
keyword nearby are no longer reported, so check that your data is still covered.
|
|
13
|
+
|
|
14
|
+
### Changed
|
|
15
|
+
|
|
16
|
+
- `luhn_card` reports fewer random IDs as cards. A Luhn-valid number now needs a known
|
|
17
|
+
issuer prefix or a card keyword nearby, and an issuer-prefixed number right after an ID
|
|
18
|
+
word such as `invoice` or `batch id` needs a card keyword. JCB, UnionPay, Diners Club,
|
|
19
|
+
Mir and Discover 644-649 prefixes are now recognised. Cards with other prefixes and no
|
|
20
|
+
keyword are no longer reported.
|
|
21
|
+
|
|
9
22
|
## [0.12.0] - 2026-11-02
|
|
10
23
|
|
|
11
24
|
Blocked runs now say what to do next, and directory runs no longer hide files they never
|
|
@@ -350,7 +363,8 @@ Pre-release (alpha). Licensed under Apache-2.0. Not a final release: the securit
|
|
|
350
363
|
See `docs/limitations.md`. Deferred: optional NER/Presidio detectors, stdin/stdout streaming,
|
|
351
364
|
directory processing, structured log parsers, key files, reversible token vault, lossy decoding.
|
|
352
365
|
|
|
353
|
-
[Unreleased]: https://github.com/aashishkumar-tech/ciphergate/compare/v0.
|
|
366
|
+
[Unreleased]: https://github.com/aashishkumar-tech/ciphergate/compare/v0.13.0...HEAD
|
|
367
|
+
[0.13.0]: https://github.com/aashishkumar-tech/ciphergate/compare/v0.12.0...v0.13.0
|
|
354
368
|
[0.12.0]: https://github.com/aashishkumar-tech/ciphergate/compare/v0.11.0...v0.12.0
|
|
355
369
|
[0.11.0]: https://github.com/aashishkumar-tech/ciphergate/compare/v0.10.0...v0.11.0
|
|
356
370
|
[0.10.0]: https://github.com/aashishkumar-tech/ciphergate/compare/v0.9.0...v0.10.0
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: ciphergate-cli
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.13.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.13.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.
|
|
@@ -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.13.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.
|
|
@@ -12,7 +12,7 @@ are engineering estimates mapped to bands (`low` < 0.5 <= `medium` < 0.8 <= `hig
|
|
|
12
12
|
| `email_regex` | `pii.email` | `alice.test@example.com`, `alice.test%40example.com` | `a..b@example.com`, `user@localhost` | 0.9 | ASCII only; no IDN; obfuscated forms (`alice at example dot com`) missed; `%40` is the only percent-encoding recognised outside `accesslog` |
|
|
13
13
|
| `phone_pattern` | `pii.phone` | `+1 202 555 0143` (high), `(202) 555-0143` (medium) | dates, IPs, card numbers, repeated digits, < 7 digits | 0.3 bare digits, 0.55–0.8 prefixed/formatted, +0.2 keyword | no numbering-plan validation; IDs can look like phones (low band) |
|
|
14
14
|
| `ip_address` | `pii.ip_address` (global), `network.telemetry` (private/loopback/link-local) | `8.8.8.8`, `10.0.0.1`, `2606:4700::1111`, `fe80::1%eth0` | `256.1.1.1`, `std::vector`, `12:30:45` | 0.85 / 0.8 | version strings like `1.2.3.4` match; documentation ranges flagged with evidence |
|
|
15
|
-
| `luhn_card` | `pii.payment_card` | `4111 1111 1111 1111` | Luhn-invalid, mixed separators, repeated digits | 0.
|
|
15
|
+
| `luhn_card` | `pii.payment_card` | `4111 1111 1111 1111` | Luhn-invalid, mixed separators, repeated digits | 0.8 issuer prefix (Visa, Mastercard, Amex, Discover, JCB, UnionPay, Diners, Mir), +0.15 keyword; 0.55 keyword without prefix; labelled CVV/CVC values 0.85 | a Luhn-valid number with no issuer prefix and no card keyword is not reported, nor is one right after an ID word (`invoice`, `batch id`, `order`) without a card keyword, so such cards are missed (for example Maestro without a keyword); cards split across fields missed |
|
|
16
16
|
| `mac_address` | `pii.device_identifier` | `00:1A:2B:3C:4D:5E`, `00-1a-2b-3c-4d-5e`, `001a.2b3c.4d5e` | all-zero, broadcast, mixed separators, `12:30:45` | 0.85, 0.6 when all digits | bare 12-hex values are missed; digit-only IDs in this shape are flagged |
|
|
17
17
|
| `iban` | `pii.bank_account` | a mod-97-valid IBAN, compact or space-grouped | wrong check digits, lowercase | 0.9 | country length and national format are not checked; about 1 in 97 random strings of this shape pass; lowercase forms are missed |
|
|
18
18
|
| `ssn_pattern` | `pii.government_id` | `219-09-9999` | never-issued areas (`000`, `666`, `9xx`), group `00`, serial `0000`, undashed | 0.8, 0.95 with a keyword | US only; undashed numbers are missed; other dashed IDs in this shape are flagged |
|
|
@@ -24,7 +24,7 @@ Supported public API (everything else is internal and may change without notice)
|
|
|
24
24
|
rescan_directory(root, policy, context=None, *, recursive=False, strict=None) -> BatchReport
|
|
25
25
|
"""
|
|
26
26
|
|
|
27
|
-
__version__ = "0.
|
|
27
|
+
__version__ = "0.13.0"
|
|
28
28
|
|
|
29
29
|
from ciphergate.config import RuntimeSettings
|
|
30
30
|
from ciphergate.constants import (
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
"""Payment-card-like detector: 13-19 digits, Luhn-valid, scored by issuer prefix and context.
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
Long numeric identifiers pass Luhn roughly 10% of the time by chance, so a Luhn-valid run is
|
|
4
|
+
reported only with a known issuer prefix or a card keyword nearby. An issuer-prefixed run right
|
|
5
|
+
after an identifier word such as "invoice" or "batch id" is also skipped unless a card keyword
|
|
6
|
+
is present. Cards with other prefixes (for example Maestro) and no keyword are missed.
|
|
5
7
|
"""
|
|
6
8
|
|
|
7
9
|
from __future__ import annotations
|
|
@@ -16,6 +18,12 @@ _CVV_RE = re.compile(
|
|
|
16
18
|
r"(?i)(?<![a-z0-9])(?:cvv2?|cvc2?)(?:\\?[\"'])?\s{0,3}[:=]\s{0,3}(?:\\?[\"'])?(\d{3,4})(?!\d)"
|
|
17
19
|
)
|
|
18
20
|
_KEYWORDS = ("card", "visa", "mastercard", "amex", "pan", "cc", "credit", "debit", "payment")
|
|
21
|
+
# Words that label a number as some other identifier. Whole words only: a substring match
|
|
22
|
+
# would let "paid" (which contains "id") hide a real card.
|
|
23
|
+
_ID_WORD_RE = re.compile(
|
|
24
|
+
r"(?i)(?<![a-z])(?:id|invoice|order|batch|total|trace|request|ref|seq|sequence|ticket|"
|
|
25
|
+
r"build|job|txn|transaction)(?![a-z])"
|
|
26
|
+
)
|
|
19
27
|
|
|
20
28
|
|
|
21
29
|
def luhn_valid(digits: str) -> bool:
|
|
@@ -38,8 +46,15 @@ def known_issuer_prefix(digits: str) -> bool:
|
|
|
38
46
|
return len(digits) == 16
|
|
39
47
|
if two in (34, 37):
|
|
40
48
|
return len(digits) == 15
|
|
41
|
-
|
|
49
|
+
three = int(digits[:3])
|
|
50
|
+
if digits.startswith("6011") or two == 65 or 644 <= three <= 649:
|
|
42
51
|
return len(digits) in (16, 19)
|
|
52
|
+
if 3528 <= four <= 3589 or two == 62: # JCB, UnionPay
|
|
53
|
+
return 16 <= len(digits) <= 19
|
|
54
|
+
if 300 <= three <= 305 or two in (36, 38, 39): # Diners Club
|
|
55
|
+
return 14 <= len(digits) <= 19
|
|
56
|
+
if 2200 <= four <= 2204: # Mir
|
|
57
|
+
return 16 <= len(digits) <= 19
|
|
43
58
|
return False
|
|
44
59
|
|
|
45
60
|
|
|
@@ -71,12 +86,18 @@ class PaymentCardDetector:
|
|
|
71
86
|
seps = {c for c in raw if c in " -"}
|
|
72
87
|
if len(seps) > 1:
|
|
73
88
|
continue
|
|
89
|
+
issuer = known_issuer_prefix(digits)
|
|
90
|
+
keyword = preceding_context(text, m.start(), _KEYWORDS, window=32)
|
|
91
|
+
if not keyword and (
|
|
92
|
+
not issuer or _ID_WORD_RE.search(text[max(0, m.start() - 16) : m.start()])
|
|
93
|
+
):
|
|
94
|
+
continue
|
|
74
95
|
evidence = ["pattern_match", "checksum_valid"]
|
|
75
96
|
confidence = 0.4
|
|
76
|
-
if
|
|
97
|
+
if issuer:
|
|
77
98
|
confidence = 0.8
|
|
78
99
|
evidence.append("issuer_prefix")
|
|
79
|
-
if
|
|
100
|
+
if keyword:
|
|
80
101
|
confidence += 0.15
|
|
81
102
|
evidence.append("context_keyword")
|
|
82
103
|
out.append(
|
|
@@ -158,12 +158,58 @@ def test_card_luhn_invalid_ignored() -> None:
|
|
|
158
158
|
assert PaymentCardDetector().detect(item("4111 1111 1111 1112")) == []
|
|
159
159
|
|
|
160
160
|
|
|
161
|
-
def
|
|
162
|
-
# 18-digit Luhn-valid number without issuer prefix or context.
|
|
161
|
+
def test_luhn_valid_identifier_without_prefix_or_keyword_is_ignored() -> None:
|
|
162
|
+
# 18-digit Luhn-valid number without issuer prefix or context: a random ID, not a card.
|
|
163
163
|
digits = "100000000000000008"
|
|
164
164
|
assert luhn_valid(digits)
|
|
165
|
-
|
|
166
|
-
|
|
165
|
+
assert PaymentCardDetector().detect(item(f"order {digits}")) == []
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
def test_card_keyword_still_finds_a_number_without_issuer_prefix() -> None:
|
|
169
|
+
f = PaymentCardDetector().detect(item("card 100000000000000008"))
|
|
170
|
+
assert f and f[0].band == "medium" and "context_keyword" in f[0].evidence
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
@pytest.mark.parametrize(
|
|
174
|
+
"text",
|
|
175
|
+
["invoice total 4111111111111111 units", "batch id 4111111111111111 queued"],
|
|
176
|
+
)
|
|
177
|
+
def test_issuer_prefixed_identifier_after_an_id_word_is_ignored(text: str) -> None:
|
|
178
|
+
assert PaymentCardDetector().detect(item(text)) == []
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
@pytest.mark.parametrize(
|
|
182
|
+
"text",
|
|
183
|
+
[
|
|
184
|
+
"user paid with 4111 1111 1111 1111",
|
|
185
|
+
"valid 4111111111111111",
|
|
186
|
+
"orderly 4111111111111111",
|
|
187
|
+
"rebuilds 4111111111111111",
|
|
188
|
+
],
|
|
189
|
+
)
|
|
190
|
+
def test_id_words_match_whole_words_only(text: str) -> None:
|
|
191
|
+
# "paid" contains "id": a substring match hid real cards (caught by the leak test).
|
|
192
|
+
assert PaymentCardDetector().detect(item(text))
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
def test_card_keyword_wins_over_an_id_word() -> None:
|
|
196
|
+
assert PaymentCardDetector().detect(item("order paid by card 4111111111111111"))
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
@pytest.mark.parametrize(
|
|
200
|
+
"digits",
|
|
201
|
+
[
|
|
202
|
+
"3530111333300000", # JCB
|
|
203
|
+
"6200000000000005", # UnionPay
|
|
204
|
+
"36227206271667", # Diners Club
|
|
205
|
+
"6445644564456445", # Discover 644-649
|
|
206
|
+
"2200000000000004", # Mir
|
|
207
|
+
],
|
|
208
|
+
)
|
|
209
|
+
def test_more_issuer_prefixes(digits: str) -> None:
|
|
210
|
+
assert luhn_valid(digits)
|
|
211
|
+
f = PaymentCardDetector().detect(item(f"charged {digits}"))
|
|
212
|
+
assert f and "issuer_prefix" in f[0].evidence
|
|
167
213
|
|
|
168
214
|
|
|
169
215
|
def test_card_mixed_separators_and_repeated_digits_ignored() -> None:
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{ciphergate_cli-0.12.0 → ciphergate_cli-0.13.0}/examples/expected/sample.dev_local_dev.jsonl
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{ciphergate_cli-0.12.0 → ciphergate_cli-0.13.0}/tests/integration/test_environment_matrix.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|