ciphergate-cli 0.7.0__tar.gz → 0.8.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.7.0 → ciphergate_cli-0.8.0}/CHANGELOG.md +23 -1
- {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/PKG-INFO +27 -2
- {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/README.md +26 -1
- {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/pyproject.toml +3 -0
- {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/src/ciphergate/__init__.py +6 -1
- {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/src/ciphergate/cli.py +15 -2
- ciphergate_cli-0.8.0/src/ciphergate/policy/templates.py +42 -0
- {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/tests/integration/test_cli_e2e.py +30 -0
- ciphergate_cli-0.8.0/tests/unit/test_policy_templates.py +55 -0
- {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/.gitignore +0 -0
- {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/LICENSE +0 -0
- {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/NOTICE +0 -0
- {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/SECURITY.md +0 -0
- {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/docs/architecture.md +0 -0
- {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/docs/claude-code-tooling.md +0 -0
- {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/docs/detector-coverage.md +0 -0
- {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/docs/examples.md +0 -0
- {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/docs/limitations.md +0 -0
- {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/docs/policy-schema.md +0 -0
- {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/docs/security-and-privacy.md +0 -0
- {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/docs/threat-model.md +0 -0
- {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/examples/expected/sample.dev_local_dev.csv +0 -0
- {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/examples/expected/sample.dev_local_dev.json +0 -0
- {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/examples/expected/sample.dev_local_dev.jsonl +0 -0
- {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/examples/expected/sample.dev_local_dev.log +0 -0
- {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/examples/sample.csv +0 -0
- {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/examples/sample.json +0 -0
- {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/examples/sample.jsonl +0 -0
- {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/examples/sample.log +0 -0
- {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/policies/dev_local.yml +0 -0
- {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/policies/developer_logs.yml +0 -0
- {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/policies/external_ai.yml +0 -0
- {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/policies/financial_template.yml +0 -0
- {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/policies/healthcare_template.yml +0 -0
- {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/policies/local_debug.yml +0 -0
- {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/policies/multi_environment.yml +0 -0
- {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/policies/prd_external_ai.yml +0 -0
- {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/policies/stg_test.yml +0 -0
- {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/policies/test_dataset.yml +0 -0
- {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/src/ciphergate/__main__.py +0 -0
- {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/src/ciphergate/config.py +0 -0
- {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/src/ciphergate/constants.py +0 -0
- {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/src/ciphergate/detectors/__init__.py +0 -0
- {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/src/ciphergate/detectors/base.py +0 -0
- {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/src/ciphergate/detectors/custom_regex.py +0 -0
- {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/src/ciphergate/detectors/dates.py +0 -0
- {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/src/ciphergate/detectors/email.py +0 -0
- {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/src/ciphergate/detectors/field_names.py +0 -0
- {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/src/ciphergate/detectors/ip_address.py +0 -0
- {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/src/ciphergate/detectors/network.py +0 -0
- {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/src/ciphergate/detectors/payment_card.py +0 -0
- {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/src/ciphergate/detectors/phone.py +0 -0
- {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/src/ciphergate/detectors/presidio_ner.py +0 -0
- {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/src/ciphergate/detectors/regex_safety.py +0 -0
- {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/src/ciphergate/detectors/registry.py +0 -0
- {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/src/ciphergate/detectors/secrets.py +0 -0
- {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/src/ciphergate/errors.py +0 -0
- {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/src/ciphergate/models/__init__.py +0 -0
- {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/src/ciphergate/models/context.py +0 -0
- {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/src/ciphergate/models/finding.py +0 -0
- {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/src/ciphergate/models/policy.py +0 -0
- {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/src/ciphergate/models/report.py +0 -0
- {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/src/ciphergate/pipeline/__init__.py +0 -0
- {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/src/ciphergate/pipeline/atomic_output.py +0 -0
- {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/src/ciphergate/pipeline/batch.py +0 -0
- {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/src/ciphergate/pipeline/inspector.py +0 -0
- {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/src/ciphergate/pipeline/paths.py +0 -0
- {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/src/ciphergate/pipeline/planner.py +0 -0
- {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/src/ciphergate/pipeline/rescan.py +0 -0
- {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/src/ciphergate/pipeline/sanitizer.py +0 -0
- {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/src/ciphergate/pipeline/stdio.py +0 -0
- {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/src/ciphergate/pipeline/streams.py +0 -0
- {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/src/ciphergate/policy/__init__.py +0 -0
- {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/src/ciphergate/policy/context.py +0 -0
- {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/src/ciphergate/policy/engine.py +0 -0
- {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/src/ciphergate/policy/loader.py +0 -0
- {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/src/ciphergate/policy/precedence.py +0 -0
- {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/src/ciphergate/policy/validator.py +0 -0
- {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/src/ciphergate/py.typed +0 -0
- {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/src/ciphergate/readers/__init__.py +0 -0
- {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/src/ciphergate/readers/accesslog_reader.py +0 -0
- {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/src/ciphergate/readers/base.py +0 -0
- {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/src/ciphergate/readers/csv_reader.py +0 -0
- {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/src/ciphergate/readers/json_reader.py +0 -0
- {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/src/ciphergate/readers/jsonl_reader.py +0 -0
- {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/src/ciphergate/readers/jsonlog_reader.py +0 -0
- {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/src/ciphergate/readers/line_structure.py +0 -0
- {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/src/ciphergate/readers/logfmt_reader.py +0 -0
- {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/src/ciphergate/readers/stacktrace_reader.py +0 -0
- {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/src/ciphergate/readers/syslog_reader.py +0 -0
- {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/src/ciphergate/readers/text_reader.py +0 -0
- {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/src/ciphergate/reporting/__init__.py +0 -0
- {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/src/ciphergate/reporting/audit.py +0 -0
- {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/src/ciphergate/reporting/safe_text.py +0 -0
- {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/src/ciphergate/reporting/serializer.py +0 -0
- {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/src/ciphergate/reporting/terminal.py +0 -0
- {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/src/ciphergate/templates/starter_policy.yml +0 -0
- {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/src/ciphergate/transforms/__init__.py +0 -0
- {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/src/ciphergate/transforms/date_shift.py +0 -0
- {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/src/ciphergate/transforms/generalize.py +0 -0
- {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/src/ciphergate/transforms/keys.py +0 -0
- {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/src/ciphergate/transforms/pseudonymize.py +0 -0
- {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/src/ciphergate/transforms/redact.py +0 -0
- {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/src/ciphergate/transforms/registry.py +0 -0
- {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/src/ciphergate/transforms/replace.py +0 -0
- {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/tests/__init__.py +0 -0
- {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/tests/conftest.py +0 -0
- {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/tests/integration/__init__.py +0 -0
- {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/tests/integration/test_api_batch_streams.py +0 -0
- {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/tests/integration/test_atomic_output.py +0 -0
- {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/tests/integration/test_batch.py +0 -0
- {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/tests/integration/test_domain_templates.py +0 -0
- {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/tests/integration/test_environment_matrix.py +0 -0
- {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/tests/integration/test_pipelines.py +0 -0
- {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/tests/integration/test_stdio.py +0 -0
- {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/tests/regression/__init__.py +0 -0
- {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/tests/regression/test_known_cases.py +0 -0
- {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/tests/security/__init__.py +0 -0
- {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/tests/security/test_key_file.py +0 -0
- {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/tests/security/test_no_network.py +0 -0
- {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/tests/security/test_no_raw_value_leaks.py +0 -0
- {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/tests/security/test_path_safety.py +0 -0
- {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/tests/security/test_policy_fail_closed.py +0 -0
- {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/tests/security/test_review_findings.py +0 -0
- {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/tests/tooling/__init__.py +0 -0
- {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/tests/tooling/test_ci_supply_chain.py +0 -0
- {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/tests/tooling/test_claude_tooling.py +0 -0
- {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/tests/tooling/test_git_tooling.py +0 -0
- {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/tests/tooling/test_publish_workflow.py +0 -0
- {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/tests/unit/__init__.py +0 -0
- {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/tests/unit/test_accesslog.py +0 -0
- {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/tests/unit/test_detectors.py +0 -0
- {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/tests/unit/test_jsonlog.py +0 -0
- {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/tests/unit/test_logfmt.py +0 -0
- {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/tests/unit/test_policy_engine.py +0 -0
- {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/tests/unit/test_policy_precedence.py +0 -0
- {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/tests/unit/test_policy_validation.py +0 -0
- {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/tests/unit/test_presidio_ner.py +0 -0
- {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/tests/unit/test_properties.py +0 -0
- {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/tests/unit/test_readers.py +0 -0
- {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/tests/unit/test_stacktrace.py +0 -0
- {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/tests/unit/test_syslog.py +0 -0
- {ciphergate_cli-0.7.0 → ciphergate_cli-0.8.0}/tests/unit/test_transforms.py +0 -0
|
@@ -6,6 +6,26 @@ versioning: [PEP 440](https://peps.python.org/pep-0440/) with SemVer meaning (pr
|
|
|
6
6
|
|
|
7
7
|
## [Unreleased]
|
|
8
8
|
|
|
9
|
+
## [0.8.0] - 2026-10-30
|
|
10
|
+
|
|
11
|
+
Adds the packaged policy templates to the Python API. Existing behaviour is unchanged.
|
|
12
|
+
|
|
13
|
+
### Added
|
|
14
|
+
|
|
15
|
+
- Python API for the packaged policy templates: `load_template(name)` returns a `Policy` and
|
|
16
|
+
`template_names()` lists the names. An unknown name raises `UsageError`.
|
|
17
|
+
|
|
18
|
+
## [0.7.1] - 2026-10-30
|
|
19
|
+
|
|
20
|
+
Makes the starter policy templates available after `pip install`. Existing commands and
|
|
21
|
+
template contents are unchanged.
|
|
22
|
+
|
|
23
|
+
### Added
|
|
24
|
+
|
|
25
|
+
- The starter policy templates now ship in the wheel. `ciphergate init --template NAME` writes
|
|
26
|
+
one to a file and `ciphergate init --list-templates` lists them, so a `pip install` no longer
|
|
27
|
+
needs a repository clone to get them. Plain `ciphergate init` is unchanged.
|
|
28
|
+
|
|
9
29
|
## [0.7.0] - 2026-10-30
|
|
10
30
|
|
|
11
31
|
First release published to PyPI. CLI, API and policy behaviour are unchanged, except for the
|
|
@@ -259,7 +279,9 @@ Pre-release (alpha). Licensed under Apache-2.0. Not a final release: the securit
|
|
|
259
279
|
See `docs/limitations.md`. Deferred: optional NER/Presidio detectors, stdin/stdout streaming,
|
|
260
280
|
directory processing, structured log parsers, key files, reversible token vault, lossy decoding.
|
|
261
281
|
|
|
262
|
-
[Unreleased]: https://github.com/aashishkumar-tech/ciphergate/compare/v0.
|
|
282
|
+
[Unreleased]: https://github.com/aashishkumar-tech/ciphergate/compare/v0.8.0...HEAD
|
|
283
|
+
[0.8.0]: https://github.com/aashishkumar-tech/ciphergate/compare/v0.7.1...v0.8.0
|
|
284
|
+
[0.7.1]: https://github.com/aashishkumar-tech/ciphergate/compare/v0.7.0...v0.7.1
|
|
263
285
|
[0.7.0]: https://github.com/aashishkumar-tech/ciphergate/compare/v0.6.1...v0.7.0
|
|
264
286
|
[0.6.1]: https://github.com/aashishkumar-tech/ciphergate/compare/v0.6.0...v0.6.1
|
|
265
287
|
[0.6.0]: https://github.com/aashishkumar-tech/ciphergate/compare/v0.5.0...v0.6.0
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: ciphergate-cli
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.8.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.8.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.
|
|
@@ -117,8 +117,24 @@ ciphergate --version
|
|
|
117
117
|
|
|
118
118
|
Runtime dependencies: `typer`, `pydantic`, `PyYAML`.
|
|
119
119
|
|
|
120
|
+
## Quick start after `pip install`
|
|
121
|
+
|
|
122
|
+
The policy templates ship with the package. Copy one, review it, then run it on your own file:
|
|
123
|
+
|
|
124
|
+
```bash
|
|
125
|
+
ciphergate init --list-templates
|
|
126
|
+
ciphergate init --template developer_logs --output developer_logs.yml
|
|
127
|
+
ciphergate validate-policy developer_logs.yml
|
|
128
|
+
ciphergate inspect app.log --policy developer_logs.yml --environment dev --destination local_dev
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
Every template is a starting point that requires review for your data and destination.
|
|
132
|
+
|
|
120
133
|
## Quick start (synthetic examples)
|
|
121
134
|
|
|
135
|
+
These commands run from a clone of this repository, because `examples/` is not in the package.
|
|
136
|
+
`policies/` in a clone holds the same templates that `ciphergate init --template` writes.
|
|
137
|
+
|
|
122
138
|
```bash
|
|
123
139
|
# 1. Start from a template (refuses to overwrite without --force) and validate it.
|
|
124
140
|
ciphergate init --output ciphergate-policy.yml
|
|
@@ -478,6 +494,15 @@ report = sanitize_file("app.log", "app.clean.log", policy,
|
|
|
478
494
|
print(report.status, report.exit_code)
|
|
479
495
|
```
|
|
480
496
|
|
|
497
|
+
After `pip install`, there is no `policies/` folder. Load a packaged template by name instead;
|
|
498
|
+
`template_names()` lists them. Templates require review for your data and destination.
|
|
499
|
+
|
|
500
|
+
```python
|
|
501
|
+
from ciphergate import load_template, template_names
|
|
502
|
+
print(template_names())
|
|
503
|
+
policy = load_template("developer_logs")
|
|
504
|
+
```
|
|
505
|
+
|
|
481
506
|
Streams and directories use the same pipeline and the same safety rules as the CLI:
|
|
482
507
|
|
|
483
508
|
```python
|
|
@@ -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.8.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.
|
|
@@ -78,8 +78,24 @@ ciphergate --version
|
|
|
78
78
|
|
|
79
79
|
Runtime dependencies: `typer`, `pydantic`, `PyYAML`.
|
|
80
80
|
|
|
81
|
+
## Quick start after `pip install`
|
|
82
|
+
|
|
83
|
+
The policy templates ship with the package. Copy one, review it, then run it on your own file:
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
ciphergate init --list-templates
|
|
87
|
+
ciphergate init --template developer_logs --output developer_logs.yml
|
|
88
|
+
ciphergate validate-policy developer_logs.yml
|
|
89
|
+
ciphergate inspect app.log --policy developer_logs.yml --environment dev --destination local_dev
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
Every template is a starting point that requires review for your data and destination.
|
|
93
|
+
|
|
81
94
|
## Quick start (synthetic examples)
|
|
82
95
|
|
|
96
|
+
These commands run from a clone of this repository, because `examples/` is not in the package.
|
|
97
|
+
`policies/` in a clone holds the same templates that `ciphergate init --template` writes.
|
|
98
|
+
|
|
83
99
|
```bash
|
|
84
100
|
# 1. Start from a template (refuses to overwrite without --force) and validate it.
|
|
85
101
|
ciphergate init --output ciphergate-policy.yml
|
|
@@ -439,6 +455,15 @@ report = sanitize_file("app.log", "app.clean.log", policy,
|
|
|
439
455
|
print(report.status, report.exit_code)
|
|
440
456
|
```
|
|
441
457
|
|
|
458
|
+
After `pip install`, there is no `policies/` folder. Load a packaged template by name instead;
|
|
459
|
+
`template_names()` lists them. Templates require review for your data and destination.
|
|
460
|
+
|
|
461
|
+
```python
|
|
462
|
+
from ciphergate import load_template, template_names
|
|
463
|
+
print(template_names())
|
|
464
|
+
policy = load_template("developer_logs")
|
|
465
|
+
```
|
|
466
|
+
|
|
442
467
|
Streams and directories use the same pipeline and the same safety rules as the CLI:
|
|
443
468
|
|
|
444
469
|
```python
|
|
@@ -62,6 +62,9 @@ path = "src/ciphergate/__init__.py"
|
|
|
62
62
|
|
|
63
63
|
[tool.hatch.build.targets.wheel]
|
|
64
64
|
packages = ["src/ciphergate"]
|
|
65
|
+
# policies/ stays the one source of the starter templates; `ciphergate init --template` reads them.
|
|
66
|
+
[tool.hatch.build.targets.wheel.force-include]
|
|
67
|
+
"policies" = "ciphergate/policies"
|
|
65
68
|
|
|
66
69
|
[tool.hatch.build.targets.sdist]
|
|
67
70
|
include = [
|
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
Supported public API (everything else is internal and may change without notice)::
|
|
4
4
|
|
|
5
5
|
load_policy(path) -> Policy
|
|
6
|
+
load_template(name) -> Policy # a packaged template; review it before relying on it
|
|
7
|
+
template_names() -> list[str]
|
|
6
8
|
validate_policy(path_or_policy) -> ValidationResult
|
|
7
9
|
inspect_file(path, policy, context=None) -> RunReport
|
|
8
10
|
plan_file(path, policy, context=None) -> RunReport
|
|
@@ -22,7 +24,7 @@ Supported public API (everything else is internal and may change without notice)
|
|
|
22
24
|
rescan_directory(root, policy, context=None, *, recursive=False, strict=None) -> BatchReport
|
|
23
25
|
"""
|
|
24
26
|
|
|
25
|
-
__version__ = "0.
|
|
27
|
+
__version__ = "0.8.0"
|
|
26
28
|
|
|
27
29
|
from ciphergate.config import RuntimeSettings
|
|
28
30
|
from ciphergate.constants import (
|
|
@@ -55,6 +57,7 @@ from ciphergate.pipeline.streams import (
|
|
|
55
57
|
rescan_stream,
|
|
56
58
|
sanitize_stream,
|
|
57
59
|
)
|
|
60
|
+
from ciphergate.policy.templates import load_template, template_names
|
|
58
61
|
from ciphergate.policy.validator import (
|
|
59
62
|
Policy,
|
|
60
63
|
ValidationResult,
|
|
@@ -82,6 +85,7 @@ __all__ = [
|
|
|
82
85
|
"inspect_file",
|
|
83
86
|
"inspect_stream",
|
|
84
87
|
"load_policy",
|
|
88
|
+
"load_template",
|
|
85
89
|
"plan_directory",
|
|
86
90
|
"plan_file",
|
|
87
91
|
"plan_stream",
|
|
@@ -91,5 +95,6 @@ __all__ = [
|
|
|
91
95
|
"sanitize_directory",
|
|
92
96
|
"sanitize_file",
|
|
93
97
|
"sanitize_stream",
|
|
98
|
+
"template_names",
|
|
94
99
|
"validate_policy",
|
|
95
100
|
]
|
|
@@ -74,6 +74,7 @@ from ciphergate.pipeline.stdio import (
|
|
|
74
74
|
)
|
|
75
75
|
from ciphergate.policy.context import evaluate_gates, resolve_context
|
|
76
76
|
from ciphergate.policy.engine import PolicyEngine
|
|
77
|
+
from ciphergate.policy.templates import read_template, template_names
|
|
77
78
|
from ciphergate.policy.validator import load_policy, validate_policy
|
|
78
79
|
from ciphergate.reporting.safe_text import escape_control
|
|
79
80
|
from ciphergate.reporting.serializer import (
|
|
@@ -351,6 +352,13 @@ def init_command(
|
|
|
351
352
|
output: Annotated[
|
|
352
353
|
Path, typer.Option("--output", "-o", help="Where to write the policy.")
|
|
353
354
|
] = Path("ciphergate-policy.yml"),
|
|
355
|
+
template: Annotated[
|
|
356
|
+
str | None,
|
|
357
|
+
typer.Option("--template", "-t", help="Named template (see --list-templates)."),
|
|
358
|
+
] = None,
|
|
359
|
+
list_templates: Annotated[
|
|
360
|
+
bool, typer.Option("--list-templates", help="List named templates and exit.")
|
|
361
|
+
] = False,
|
|
354
362
|
force: ForceOpt = False,
|
|
355
363
|
quiet: QuietOpt = False,
|
|
356
364
|
debug: DebugOpt = False,
|
|
@@ -358,12 +366,17 @@ def init_command(
|
|
|
358
366
|
"""Write a starter policy template (it MUST be reviewed before real use)."""
|
|
359
367
|
|
|
360
368
|
def body() -> int:
|
|
361
|
-
|
|
369
|
+
if list_templates:
|
|
370
|
+
sys.stdout.write("".join(f"{name}\n" for name in template_names()))
|
|
371
|
+
return 0
|
|
362
372
|
text = (
|
|
363
|
-
|
|
373
|
+
read_template(template)
|
|
374
|
+
if template is not None
|
|
375
|
+
else resources.files("ciphergate")
|
|
364
376
|
.joinpath("templates/starter_policy.yml")
|
|
365
377
|
.read_text(encoding="utf-8")
|
|
366
378
|
)
|
|
379
|
+
validate_output_path(output, protected=[], force=force, label="policy")
|
|
367
380
|
with AtomicOutput(output, force=force) as out:
|
|
368
381
|
out.stream.write(text)
|
|
369
382
|
out.publish()
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"""Starter policy templates shipped with the package. Every template requires review before use."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from importlib import resources
|
|
6
|
+
from importlib.resources.abc import Traversable
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
|
|
9
|
+
from ciphergate.errors import UsageError
|
|
10
|
+
from ciphergate.policy.validator import Policy, load_policy_text
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def _templates_dir() -> Traversable:
|
|
14
|
+
packaged = resources.files("ciphergate").joinpath("policies")
|
|
15
|
+
if packaged.is_dir():
|
|
16
|
+
return packaged
|
|
17
|
+
# Editable install: the build copies policies/ into wheels only, so use the source checkout.
|
|
18
|
+
return Path(__file__).resolve().parents[3] / "policies"
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def template_names() -> list[str]:
|
|
22
|
+
"""Names accepted by `read_template`, sorted."""
|
|
23
|
+
return sorted(
|
|
24
|
+
entry.name.removesuffix(".yml")
|
|
25
|
+
for entry in _templates_dir().iterdir()
|
|
26
|
+
if entry.name.endswith(".yml")
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def read_template(name: str) -> str:
|
|
31
|
+
"""Return a template's text. `name` is only compared to known names, never used as a path."""
|
|
32
|
+
if name not in template_names():
|
|
33
|
+
raise UsageError(
|
|
34
|
+
"unknown policy template; run: ciphergate init --list-templates",
|
|
35
|
+
code="unknown_policy_template",
|
|
36
|
+
)
|
|
37
|
+
return _templates_dir().joinpath(f"{name}.yml").read_text(encoding="utf-8")
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def load_template(name: str) -> Policy:
|
|
41
|
+
"""Load a named template as a `Policy`. It is a starting point that requires review."""
|
|
42
|
+
return load_policy_text(read_template(name), source_name=f"{name}.yml")
|
|
@@ -40,6 +40,36 @@ def test_init_refuses_overwrite(tmp_path: Path) -> None:
|
|
|
40
40
|
assert invoke("init", "--output", target, "--force").exit_code == 0
|
|
41
41
|
|
|
42
42
|
|
|
43
|
+
def test_init_lists_every_policy_template() -> None:
|
|
44
|
+
r = invoke("init", "--list-templates")
|
|
45
|
+
assert r.exit_code == 0
|
|
46
|
+
assert r.stdout.split() == sorted(p.stem for p in POLICIES.glob("*.yml"))
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
@pytest.mark.parametrize("policy", sorted(POLICIES.glob("*.yml")), ids=lambda p: p.stem)
|
|
50
|
+
def test_init_template_writes_a_valid_copy(tmp_path: Path, policy: Path) -> None:
|
|
51
|
+
target = tmp_path / "p.yml"
|
|
52
|
+
r = invoke("init", "--template", policy.stem, "--output", target)
|
|
53
|
+
assert r.exit_code == 0 and "template" in r.stdout.lower()
|
|
54
|
+
assert target.read_text(encoding="utf-8") == policy.read_text(encoding="utf-8")
|
|
55
|
+
assert invoke("validate-policy", target).exit_code == 0
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def test_init_template_refuses_overwrite(tmp_path: Path) -> None:
|
|
59
|
+
target = tmp_path / "p.yml"
|
|
60
|
+
target.write_text("keep", encoding="utf-8")
|
|
61
|
+
assert invoke("init", "--template", "dev_local", "--output", target).exit_code == 5
|
|
62
|
+
assert target.read_text(encoding="utf-8") == "keep"
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
@pytest.mark.parametrize("name", ["nope", "../pyproject", "dev_local.yml", ""])
|
|
66
|
+
def test_init_unknown_template_fails_and_writes_nothing(tmp_path: Path, name: str) -> None:
|
|
67
|
+
target = tmp_path / "p.yml"
|
|
68
|
+
r = invoke("init", "--template", name, "--output", target)
|
|
69
|
+
assert r.exit_code == 2
|
|
70
|
+
assert "--list-templates" in r.output and not target.exists()
|
|
71
|
+
|
|
72
|
+
|
|
43
73
|
def test_validate_policy_exit_codes(tmp_path: Path) -> None:
|
|
44
74
|
bad = tmp_path / "bad.yml"
|
|
45
75
|
bad.write_text(
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
"""Packaged policy templates through the public Python API."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
import pytest
|
|
8
|
+
|
|
9
|
+
import ciphergate
|
|
10
|
+
from ciphergate import (
|
|
11
|
+
ContextInput,
|
|
12
|
+
RunStatus,
|
|
13
|
+
load_policy,
|
|
14
|
+
load_template,
|
|
15
|
+
sanitize_file,
|
|
16
|
+
template_names,
|
|
17
|
+
)
|
|
18
|
+
from ciphergate.errors import CipherGateError, UsageError
|
|
19
|
+
from tests.conftest import EXAMPLES, POLICIES
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def test_public_exports() -> None:
|
|
23
|
+
for name in ("load_template", "template_names"):
|
|
24
|
+
assert name in ciphergate.__all__ and hasattr(ciphergate, name)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def test_names_match_the_policy_files() -> None:
|
|
28
|
+
assert template_names() == sorted(p.stem for p in POLICIES.glob("*.yml"))
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
@pytest.mark.parametrize("name", template_names())
|
|
32
|
+
def test_load_template_matches_the_policy_file(name: str) -> None:
|
|
33
|
+
policy = load_template(name)
|
|
34
|
+
assert policy.name == load_policy(POLICIES / f"{name}.yml").name
|
|
35
|
+
assert policy.source_name == f"{name}.yml"
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
@pytest.mark.parametrize("name", ["nope", "../pyproject", "dev_local.yml", ""])
|
|
39
|
+
def test_unknown_template_is_rejected(name: str) -> None:
|
|
40
|
+
with pytest.raises(UsageError) as err:
|
|
41
|
+
load_template(name)
|
|
42
|
+
assert err.value.code == "unknown_policy_template"
|
|
43
|
+
assert isinstance(err.value, CipherGateError)
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def test_loaded_template_still_blocks_and_publishes_nothing(tmp_path: Path) -> None:
|
|
47
|
+
output = tmp_path / "out.log"
|
|
48
|
+
report = sanitize_file(
|
|
49
|
+
EXAMPLES / "sample.log",
|
|
50
|
+
output,
|
|
51
|
+
load_template("prd_external_ai"),
|
|
52
|
+
ContextInput(),
|
|
53
|
+
)
|
|
54
|
+
assert report.status == RunStatus.BLOCKED and report.exit_code == 1
|
|
55
|
+
assert not output.exists()
|
|
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
|
|
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
|