ciphergate-cli 0.7.0__py3-none-any.whl
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/__init__.py +95 -0
- ciphergate/__main__.py +6 -0
- ciphergate/cli.py +832 -0
- ciphergate/config.py +50 -0
- ciphergate/constants.py +274 -0
- ciphergate/detectors/__init__.py +5 -0
- ciphergate/detectors/base.py +53 -0
- ciphergate/detectors/custom_regex.py +37 -0
- ciphergate/detectors/dates.py +92 -0
- ciphergate/detectors/email.py +56 -0
- ciphergate/detectors/field_names.py +220 -0
- ciphergate/detectors/ip_address.py +85 -0
- ciphergate/detectors/network.py +42 -0
- ciphergate/detectors/payment_card.py +77 -0
- ciphergate/detectors/phone.py +67 -0
- ciphergate/detectors/presidio_ner.py +112 -0
- ciphergate/detectors/regex_safety.py +40 -0
- ciphergate/detectors/registry.py +194 -0
- ciphergate/detectors/secrets.py +130 -0
- ciphergate/errors.py +119 -0
- ciphergate/models/__init__.py +1 -0
- ciphergate/models/context.py +69 -0
- ciphergate/models/finding.py +108 -0
- ciphergate/models/policy.py +402 -0
- ciphergate/models/report.py +115 -0
- ciphergate/pipeline/__init__.py +1 -0
- ciphergate/pipeline/atomic_output.py +143 -0
- ciphergate/pipeline/batch.py +533 -0
- ciphergate/pipeline/inspector.py +282 -0
- ciphergate/pipeline/paths.py +98 -0
- ciphergate/pipeline/planner.py +192 -0
- ciphergate/pipeline/rescan.py +120 -0
- ciphergate/pipeline/sanitizer.py +392 -0
- ciphergate/pipeline/stdio.py +173 -0
- ciphergate/pipeline/streams.py +152 -0
- ciphergate/policy/__init__.py +26 -0
- ciphergate/policy/context.py +203 -0
- ciphergate/policy/engine.py +194 -0
- ciphergate/policy/loader.py +116 -0
- ciphergate/policy/precedence.py +149 -0
- ciphergate/policy/validator.py +632 -0
- ciphergate/py.typed +0 -0
- ciphergate/readers/__init__.py +113 -0
- ciphergate/readers/accesslog_reader.py +244 -0
- ciphergate/readers/base.py +131 -0
- ciphergate/readers/csv_reader.py +241 -0
- ciphergate/readers/json_reader.py +184 -0
- ciphergate/readers/jsonl_reader.py +97 -0
- ciphergate/readers/jsonlog_reader.py +178 -0
- ciphergate/readers/line_structure.py +125 -0
- ciphergate/readers/logfmt_reader.py +100 -0
- ciphergate/readers/stacktrace_reader.py +103 -0
- ciphergate/readers/syslog_reader.py +309 -0
- ciphergate/readers/text_reader.py +59 -0
- ciphergate/reporting/__init__.py +1 -0
- ciphergate/reporting/audit.py +85 -0
- ciphergate/reporting/safe_text.py +71 -0
- ciphergate/reporting/serializer.py +44 -0
- ciphergate/reporting/terminal.py +147 -0
- ciphergate/templates/starter_policy.yml +77 -0
- ciphergate/transforms/__init__.py +6 -0
- ciphergate/transforms/date_shift.py +79 -0
- ciphergate/transforms/generalize.py +107 -0
- ciphergate/transforms/keys.py +177 -0
- ciphergate/transforms/pseudonymize.py +113 -0
- ciphergate/transforms/redact.py +9 -0
- ciphergate/transforms/registry.py +201 -0
- ciphergate/transforms/replace.py +16 -0
- ciphergate_cli-0.7.0.dist-info/METADATA +531 -0
- ciphergate_cli-0.7.0.dist-info/RECORD +74 -0
- ciphergate_cli-0.7.0.dist-info/WHEEL +4 -0
- ciphergate_cli-0.7.0.dist-info/entry_points.txt +2 -0
- ciphergate_cli-0.7.0.dist-info/licenses/LICENSE +202 -0
- ciphergate_cli-0.7.0.dist-info/licenses/NOTICE +4 -0
|
@@ -0,0 +1,531 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: ciphergate-cli
|
|
3
|
+
Version: 0.7.0
|
|
4
|
+
Summary: Local-first, environment- and destination-aware inspection and sanitization of logs and test datasets.
|
|
5
|
+
Project-URL: Homepage, https://github.com/aashishkumar-tech/ciphergate
|
|
6
|
+
Project-URL: Source, https://github.com/aashishkumar-tech/ciphergate
|
|
7
|
+
Project-URL: Issues, https://github.com/aashishkumar-tech/ciphergate/issues
|
|
8
|
+
Project-URL: Changelog, https://github.com/aashishkumar-tech/ciphergate/blob/main/CHANGELOG.md
|
|
9
|
+
Author: CipherGate maintainers
|
|
10
|
+
License-Expression: Apache-2.0
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
License-File: NOTICE
|
|
13
|
+
Classifier: Development Status :: 3 - Alpha
|
|
14
|
+
Classifier: Environment :: Console
|
|
15
|
+
Classifier: Intended Audience :: Developers
|
|
16
|
+
Classifier: Operating System :: OS Independent
|
|
17
|
+
Classifier: Programming Language :: Python :: 3
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
21
|
+
Classifier: Topic :: Security
|
|
22
|
+
Classifier: Typing :: Typed
|
|
23
|
+
Requires-Python: >=3.11
|
|
24
|
+
Requires-Dist: pydantic<3.0,>=2.6
|
|
25
|
+
Requires-Dist: pyyaml<7.0,>=6.0.1
|
|
26
|
+
Requires-Dist: typer<1.0,>=0.19
|
|
27
|
+
Provides-Extra: dev
|
|
28
|
+
Requires-Dist: build>=1.2; extra == 'dev'
|
|
29
|
+
Requires-Dist: hypothesis>=6.100; extra == 'dev'
|
|
30
|
+
Requires-Dist: mypy>=1.10; extra == 'dev'
|
|
31
|
+
Requires-Dist: pre-commit>=3.5; extra == 'dev'
|
|
32
|
+
Requires-Dist: pytest-cov>=5.0; extra == 'dev'
|
|
33
|
+
Requires-Dist: pytest>=8.0; extra == 'dev'
|
|
34
|
+
Requires-Dist: ruff>=0.6; extra == 'dev'
|
|
35
|
+
Requires-Dist: types-pyyaml>=6.0; extra == 'dev'
|
|
36
|
+
Provides-Extra: ner
|
|
37
|
+
Requires-Dist: presidio-analyzer<3,>=2.2; extra == 'ner'
|
|
38
|
+
Description-Content-Type: text/markdown
|
|
39
|
+
|
|
40
|
+
# CipherGate
|
|
41
|
+
|
|
42
|
+
**Inspect and sanitize logs and test datasets before they leave their environment** — with
|
|
43
|
+
policies that depend on where the data came from (DEV/STG/PRD) *and* where it is going (local
|
|
44
|
+
debugging, internal monitoring, an external vendor or AI assistant, a public repository).
|
|
45
|
+
|
|
46
|
+
Teams often hand-redact logs or keep ad-hoc scripts before pasting data into an AI assistant or a
|
|
47
|
+
vendor ticket. CipherGate replaces that with a local, explainable, scriptable gate:
|
|
48
|
+
|
|
49
|
+
```
|
|
50
|
+
inspect -> plan -> sanitize --apply -> rescan
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
> **Status: 0.7.0 (alpha).** No detector finds every sensitive value. A passing run means the
|
|
54
|
+
> configured processing completed under the selected policy; it is **not** proof that the output
|
|
55
|
+
> contains no sensitive information, and it is not a compliance (GDPR, HIPAA, PCI DSS, ...) or
|
|
56
|
+
> anonymization guarantee. Policy templates must be reviewed before use.
|
|
57
|
+
|
|
58
|
+
## What it is / what it is not
|
|
59
|
+
|
|
60
|
+
| It is | It is not |
|
|
61
|
+
|---|---|
|
|
62
|
+
| A local CLI + Python library; no network calls, accounts or telemetry | A hosted service, dashboard, or AI classifier |
|
|
63
|
+
| Deterministic detectors + a separate, testable policy engine | A guarantee that all PII/secrets are found |
|
|
64
|
+
| Environment- **and** destination-aware rules with fail-closed defaults | Proof of where data really came from (context is *declared*) |
|
|
65
|
+
| Redaction, replacement, keyed HMAC pseudonyms, generalization, date shifting, drops | Reversible tokenization (no token vault in this release) |
|
|
66
|
+
| Atomic, no-overwrite output with a residual rescan gate | A deployment tool or cloud integration |
|
|
67
|
+
| Privacy-safe JSON/text reports with stable reason codes and exit codes | A legal/compliance determination |
|
|
68
|
+
|
|
69
|
+
## Features (implemented and tested)
|
|
70
|
+
|
|
71
|
+
- **Formats:** CSV (quoted fields, embedded newlines, BOM, CRLF, configurable delimiter), JSON
|
|
72
|
+
(whole document, JSON Pointer paths), JSONL/NDJSON (streamed), plain text/log (line based,
|
|
73
|
+
multi-line PEM private-key blocks), logfmt (`--format logfmt`: `key=value` log lines), JSON
|
|
74
|
+
in log lines (`--format jsonlog`), nginx/Apache access logs (`--format accesslog`),
|
|
75
|
+
syslog/CEF (`--format syslog`) and logs with Python/JVM/Node stack traces
|
|
76
|
+
(`--format stacktrace`), see below.
|
|
77
|
+
- **Detectors (local, deterministic):** field-name heuristics, email, phone (conservative
|
|
78
|
+
confidence), IPv4/IPv6 (stdlib-validated; private addresses as `network.telemetry`), Luhn card
|
|
79
|
+
numbers, dates (DOB only with field/context evidence), credentials/secrets (PEM, bearer/basic,
|
|
80
|
+
AWS/GitHub/Slack/Stripe-style/Google keys, JWTs, URL passwords, connection strings,
|
|
81
|
+
`password=`-style assignments), internal hostnames, policy field classifications, and custom
|
|
82
|
+
regexes with safety checks. See [docs/detector-coverage.md](docs/detector-coverage.md).
|
|
83
|
+
- **Policy engine:** strict versioned YAML schema, deterministic precedence, conflict rejection,
|
|
84
|
+
credential hard constraints, environment/destination guardrails, fail-closed fallback.
|
|
85
|
+
- **Actions:** `allow`, `redact`, `replace`, `pseudonymize` (HMAC-SHA-256, scoped, versioned),
|
|
86
|
+
`generalize` (year, year-month, age bands, coordinate rounding, numeric bins), `date_shift`
|
|
87
|
+
(keyed, per-dataset or per-entity), `drop_field`, `drop_record`, `block`, `review`.
|
|
88
|
+
- **Safe output:** two-pass processing, temp file + fsync + atomic publish, overwrite only with
|
|
89
|
+
`--force`, input/output alias protection, input-change detection, cleanup on failure/Ctrl-C.
|
|
90
|
+
- **Reports:** counts by category/detector/action/reason/rule, safe locations, effective context
|
|
91
|
+
and its source, gates, key *status* (never key material), no raw values or fingerprints.
|
|
92
|
+
|
|
93
|
+
## Install
|
|
94
|
+
|
|
95
|
+
Requires Python 3.11+.
|
|
96
|
+
|
|
97
|
+
**From PyPI** (the package is named `ciphergate-cli`; the command and `import ciphergate` stay
|
|
98
|
+
`ciphergate`):
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
python -m pip install ciphergate-cli
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
Do not run `pip install ciphergate`: that PyPI name belongs to an unrelated project. PyPI uploads
|
|
105
|
+
start with 0.7.0. Older versions are only on
|
|
106
|
+
[GitHub releases](https://github.com/aashishkumar-tech/ciphergate/releases), as a wheel, an sdist
|
|
107
|
+
and `SHA256SUMS`.
|
|
108
|
+
|
|
109
|
+
**From source:**
|
|
110
|
+
|
|
111
|
+
```bash
|
|
112
|
+
python -m venv .venv
|
|
113
|
+
# Windows: .venv\Scripts\activate macOS/Linux: source .venv/bin/activate
|
|
114
|
+
python -m pip install -e ".[dev]" # or: python -m pip install .
|
|
115
|
+
ciphergate --version
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
Runtime dependencies: `typer`, `pydantic`, `PyYAML`.
|
|
119
|
+
|
|
120
|
+
## Quick start (synthetic examples)
|
|
121
|
+
|
|
122
|
+
```bash
|
|
123
|
+
# 1. Start from a template (refuses to overwrite without --force) and validate it.
|
|
124
|
+
ciphergate init --output ciphergate-policy.yml
|
|
125
|
+
ciphergate validate-policy ciphergate-policy.yml
|
|
126
|
+
|
|
127
|
+
# 2. Inspect: never modifies the input, never writes sanitized data.
|
|
128
|
+
ciphergate inspect examples/sample.csv --policy policies/developer_logs.yml \
|
|
129
|
+
--environment dev --destination local_dev --report inspection.json
|
|
130
|
+
|
|
131
|
+
# 3. Plan: preview exactly what sanitize --apply would do.
|
|
132
|
+
ciphergate plan examples/sample.jsonl --policy policies/external_ai.yml \
|
|
133
|
+
--environment prd --target-environment external --destination external_ai
|
|
134
|
+
|
|
135
|
+
# 4. Sanitize: needs --apply; pseudonymization needs a key in the environment (see below).
|
|
136
|
+
ciphergate sanitize examples/sample.csv --policy policies/prd_external_ai.yml \
|
|
137
|
+
--environment prd --target-environment external --destination external_ai \
|
|
138
|
+
--output build/sample.sanitized.csv --report build/sample.report.json --apply
|
|
139
|
+
|
|
140
|
+
# 5. Rescan the output (read-only). Exit 7 if gating residual findings remain.
|
|
141
|
+
ciphergate rescan build/sample.sanitized.csv --policy policies/prd_external_ai.yml
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
`policies/prd_external_ai.yml` pins its context (`prd` -> `external` / `external_ai`), so the
|
|
145
|
+
context flags in steps 4–5 must match it or be omitted; a conflicting value fails safely.
|
|
146
|
+
|
|
147
|
+
A log containing a (fake) credential is **blocked** for the external destination — exit 1 and no
|
|
148
|
+
output file:
|
|
149
|
+
|
|
150
|
+
```bash
|
|
151
|
+
ciphergate sanitize examples/sample.log --policy policies/prd_external_ai.yml \
|
|
152
|
+
--output build/sample.sanitized.log --apply # exit 1, nothing written
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
Expected sanitized outputs for `policies/developer_logs.yml` (`dev` -> `local_dev`) are in
|
|
156
|
+
[examples/expected/](examples/expected/). More scenarios: [docs/examples.md](docs/examples.md).
|
|
157
|
+
|
|
158
|
+
### Pseudonymization key
|
|
159
|
+
|
|
160
|
+
Keyed actions (`pseudonymize`, `date_shift`) read key material **only** from an environment
|
|
161
|
+
variable (default `CIPHERGATE_HMAC_KEY`; never a CLI flag, never the policy file). Minimum 32
|
|
162
|
+
bytes after decoding; `hex:` and `base64:` prefixes are decoded. There is no fallback key: if it
|
|
163
|
+
is missing or weak, `plan` reports `configuration_required` and `sanitize --apply` exits 6
|
|
164
|
+
without writing output.
|
|
165
|
+
|
|
166
|
+
```bash
|
|
167
|
+
# bash/zsh — generate locally; avoid pasting real keys into shell history or files you commit
|
|
168
|
+
export CIPHERGATE_HMAC_KEY="hex:$(python -c 'import secrets; print(secrets.token_hex(32))')"
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
```powershell
|
|
172
|
+
# PowerShell
|
|
173
|
+
$env:CIPHERGATE_HMAC_KEY = "hex:" + (python -c "import secrets; print(secrets.token_hex(32))")
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
Alternatively, put the key in a file and set `CIPHERGATE_HMAC_KEY_FILE` to its path (the
|
|
177
|
+
Docker/Kubernetes secrets convention; it works for any `key_env` name plus `_FILE`):
|
|
178
|
+
|
|
179
|
+
```bash
|
|
180
|
+
umask 077 && python -c 'import secrets; print("hex:" + secrets.token_hex(32))' > ~/.ciphergate.key
|
|
181
|
+
export CIPHERGATE_HMAC_KEY_FILE=~/.ciphergate.key
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
The file must be a regular file of at most 4 KiB. On Linux/macOS it must be owned by you and not
|
|
185
|
+
accessible to group or others (`chmod 600` or `400`), otherwise the run fails with
|
|
186
|
+
`key_file_invalid`; symlinks are followed and the target is checked. On Windows CipherGate
|
|
187
|
+
cannot check the ACL and prints a warning: restrict the file to your account yourself. Setting
|
|
188
|
+
both `CIPHERGATE_HMAC_KEY` and `CIPHERGATE_HMAC_KEY_FILE` is refused.
|
|
189
|
+
|
|
190
|
+
Keep the same key to get the same pseudonyms across runs; anyone with the key can link equal
|
|
191
|
+
values. In CI, inject it from the platform's secret store. See
|
|
192
|
+
[docs/security-and-privacy.md](docs/security-and-privacy.md).
|
|
193
|
+
|
|
194
|
+
## Context: environment vs destination
|
|
195
|
+
|
|
196
|
+
| Flag | Meaning | Values |
|
|
197
|
+
|---|---|---|
|
|
198
|
+
| `--environment` (`--source-environment`) | where the data **came from** | `dev`, `stg`, `prd`, `unknown` |
|
|
199
|
+
| `--target-environment` | environment that will use the output | `dev`, `stg`, `prd`, `external`, `unknown` |
|
|
200
|
+
| `--destination` | who/what **receives** it | `local_dev`, `approved_internal`, `staging_test`, `production_monitoring`, `external_vendor`, `external_ai`, `public_repository`, `unknown` |
|
|
201
|
+
| `--profile` / `--use-case` | domain profile / purpose | see [docs/policy-schema.md](docs/policy-schema.md) |
|
|
202
|
+
|
|
203
|
+
Context can be pinned by a policy or supplied per run. Missing required context fails closed;
|
|
204
|
+
nothing defaults to `dev` or to a trusted destination. Built-in guardrails (not overridable by
|
|
205
|
+
rules): PRD -> non-production is blocked without a reviewed `transfers` approval; external,
|
|
206
|
+
unknown, missing or production targets require a strict policy with the rescan gate and enabled
|
|
207
|
+
credential detectors (and JSON key scanning for JSON input); strict policies send `unknown`
|
|
208
|
+
context to review. **Context is declared, not verified** — in CI, set it from
|
|
209
|
+
controlled pipeline configuration.
|
|
210
|
+
|
|
211
|
+
## Shipped policy templates (review before use)
|
|
212
|
+
|
|
213
|
+
`dev_local`, `stg_test`, `prd_external_ai`, `developer_logs`, `test_dataset`, `external_ai`,
|
|
214
|
+
`healthcare_template`, `financial_template`, `local_debug`, and `multi_environment` (one policy
|
|
215
|
+
whose outcomes vary by environment *and* destination). All validate in CI. Templates named for
|
|
216
|
+
healthcare or finance are starting points, **not** HIPAA/PCI controls.
|
|
217
|
+
|
|
218
|
+
## Commands and exit codes
|
|
219
|
+
|
|
220
|
+
`init`, `validate-policy`, `inspect`, `plan`, `sanitize` (needs `--apply`), `rescan`, `explain`
|
|
221
|
+
(decision for a hypothetical finding — no data needed). All support `--help`.
|
|
222
|
+
|
|
223
|
+
Common options: `--policy`, `--format csv|json|jsonl|text|logfmt|jsonlog|accesslog|syslog|stacktrace`, `--report PATH`,
|
|
224
|
+
`--report-format json|text` (JSON without `--report` prints only JSON on stdout),
|
|
225
|
+
`--max-bytes`, `--encoding` (UTF-8 default; ASCII-compatible encodings only),
|
|
226
|
+
`--path-display basename|redacted|full`, `--force`, `--quiet`, `--verbose`, `--no-color`,
|
|
227
|
+
`--debug` (frames only, never messages/values). `rescan` adds `--strict/--no-strict`.
|
|
228
|
+
|
|
229
|
+
### Optional local NER (Presidio)
|
|
230
|
+
|
|
231
|
+
Person names and places in free text are not found by the built-in pattern detectors. An
|
|
232
|
+
optional detector based on [Microsoft Presidio](https://microsoft.github.io/presidio/) can add
|
|
233
|
+
them. It is **off by default** and not part of the base install:
|
|
234
|
+
|
|
235
|
+
```bash
|
|
236
|
+
python -m pip install "ciphergate-cli[ner]"
|
|
237
|
+
python -m spacy download en_core_web_lg # one-time, explicit; CipherGate never downloads models
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
```yaml
|
|
241
|
+
detectors:
|
|
242
|
+
presidio:
|
|
243
|
+
enabled: true
|
|
244
|
+
model: en_core_web_lg # an installed spaCy model package
|
|
245
|
+
language: en
|
|
246
|
+
min_score: 0.6 # Presidio score threshold
|
|
247
|
+
entities: # Presidio entity -> CipherGate category (default shown)
|
|
248
|
+
PERSON: pii.person_name
|
|
249
|
+
LOCATION: location.coarse
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
- If the extra or the model is missing, the run fails with exit 6 (`ner_not_installed` /
|
|
253
|
+
`ner_model_missing`) and publishes nothing. It never falls back to running without NER. The
|
|
254
|
+
model is checked before Presidio is imported, because Presidio's spaCy engine would otherwise
|
|
255
|
+
download a missing model.
|
|
256
|
+
- Findings use detector ID `presidio_ner` and go through the policy like any other finding, so
|
|
257
|
+
rules decide what happens (`match: {detectors: [presidio_ner]}` works). NER never allows a
|
|
258
|
+
value, so block and review rules still win.
|
|
259
|
+
- NER misses names and flags non-names. It is slow (every string value goes through spaCy) and
|
|
260
|
+
memory-hungry, and its scores are Presidio's, not calibrated probabilities. Treat it as extra
|
|
261
|
+
recall, not proof.
|
|
262
|
+
|
|
263
|
+
### Directories (batch runs)
|
|
264
|
+
|
|
265
|
+
`inspect`, `plan`, `rescan` and `sanitize` accept a directory as INPUT:
|
|
266
|
+
|
|
267
|
+
```bash
|
|
268
|
+
ciphergate inspect logs/ --policy policies/developer_logs.yml --environment dev \
|
|
269
|
+
--destination local_dev --recursive --report build/batch.json
|
|
270
|
+
ciphergate sanitize logs/ --policy ... --output build/logs.sanitized --apply
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
- Files are chosen by extension (`.csv`, `.json`, `.jsonl`, `.ndjson`, `.txt`, `.log`), or all
|
|
274
|
+
regular files when `--format` is given. Subdirectories are processed only with
|
|
275
|
+
`--recursive`/`-r` (at most 32 levels). Symlinks, junctions, devices, FIFOs and other
|
|
276
|
+
extensions are **skipped and listed** in the report with a reason; links are never followed.
|
|
277
|
+
At most 1000 files by default (`--max-files N`); more fails the run (exit 4) before anything
|
|
278
|
+
is processed. An empty selection fails with `batch_empty` (exit 4).
|
|
279
|
+
- Each file is processed exactly like a single-file run, in sorted path order; a failing file
|
|
280
|
+
does not stop the others, so the report covers every file.
|
|
281
|
+
- `sanitize` needs a **new** output directory (an existing one is refused; `--force` is not
|
|
282
|
+
supported for directories) outside the input directory. The whole tree is built in a private
|
|
283
|
+
staging directory next to it and published with one rename **only if every file was
|
|
284
|
+
sanitized**; otherwise nothing is published and the staging directory is removed.
|
|
285
|
+
- The exit code is the first failing file's code in sorted order (0 if all succeeded). The JSON
|
|
286
|
+
report (`ciphergate.batch-report/v1`) has a summary, every file's full report and the skipped
|
|
287
|
+
entries. Paths are relative to INPUT; `--path-display redacted` replaces them with
|
|
288
|
+
`<redacted:NNNN>.ext`. `--report` must not point inside the input or output directory.
|
|
289
|
+
- Directory runs cannot use stdin/stdout. The same behaviour is available from Python as
|
|
290
|
+
`inspect_directory` / `plan_directory` / `rescan_directory` / `sanitize_directory`.
|
|
291
|
+
|
|
292
|
+
### Structured logs (logfmt)
|
|
293
|
+
|
|
294
|
+
`--format logfmt` reads `key=value` log lines such as
|
|
295
|
+
`ts=2026-01-05T10:00:00Z level=info msg="login ok" user_email=alice.test@example.com`. It is
|
|
296
|
+
never selected from the file extension (`.log` stays `text`).
|
|
297
|
+
|
|
298
|
+
- Every line is still scanned exactly like `text`, so logfmt never finds less than text mode
|
|
299
|
+
(property-tested). The parse only adds field context: a finding inside a value is attributed to
|
|
300
|
+
that field (`field_path` `/user_email`), and field-name heuristics run on the keys, so
|
|
301
|
+
`customer_name="…"` or `user_id=…` are found even when the value itself looks harmless.
|
|
302
|
+
Policy `field_names` / `field_paths` rules therefore apply to log fields.
|
|
303
|
+
- Output keeps each line as it was; only the protected spans change, so quoting and free text are
|
|
304
|
+
preserved. `drop_field` is not supported for logfmt (it becomes `review`; a credential there
|
|
305
|
+
blocks).
|
|
306
|
+
- Keys must start with a letter or `_`; values are bare or double-quoted with backslash escapes.
|
|
307
|
+
A line with broken quoting gets no field context (it is still scanned as text), and lines inside
|
|
308
|
+
a PEM private key block are never parsed as fields. JSON embedded in log lines is not parsed
|
|
309
|
+
by logfmt; use `jsonlog` for that.
|
|
310
|
+
|
|
311
|
+
### Structured logs (JSON in log lines)
|
|
312
|
+
|
|
313
|
+
`--format jsonlog` reads log lines that embed JSON objects, such as
|
|
314
|
+
`2026-01-05T10:00:00Z ERROR login failed {"user": {"email": "alice.test@example.com"}}`. Like
|
|
315
|
+
logfmt, it is never selected from the file extension.
|
|
316
|
+
|
|
317
|
+
- Every line is still scanned exactly like `text` (property-tested: jsonlog never protects fewer
|
|
318
|
+
characters than text mode). Each embedded object adds field context: findings inside a value
|
|
319
|
+
are attributed to it (`field_path` is the JSON Pointer inside the object, e.g. `/user/email`),
|
|
320
|
+
and field-name heuristics and policy classifications run on every key and cover the whole
|
|
321
|
+
value. String values and keys containing JSON escapes (`\u0040`) are also decoded and scanned.
|
|
322
|
+
- Output keeps the line as it was; only protected spans change. Replacements are JSON-escaped, a
|
|
323
|
+
replaced number/boolean/null becomes a JSON string, and every embedded object is re-parsed
|
|
324
|
+
after rewriting; if one would no longer be valid JSON, the run fails and nothing is published.
|
|
325
|
+
A match that covers only JSON punctuation (for example a credential assignment right before an
|
|
326
|
+
object) protects every value of that object.
|
|
327
|
+
- Only objects (`{...}`) are recognised, at most 16 start positions per line, nested at most
|
|
328
|
+
64 levels (the JSON depth limit). Text that is not valid JSON, and lines inside a PEM private
|
|
329
|
+
key block, get no field context (they are still scanned as text). `drop_field` is not
|
|
330
|
+
supported (it becomes `review`; a credential there blocks).
|
|
331
|
+
|
|
332
|
+
### Access logs (nginx/Apache common and combined)
|
|
333
|
+
|
|
334
|
+
`--format accesslog` reads NCSA common/combined lines, the default layout of nginx and Apache
|
|
335
|
+
httpd:
|
|
336
|
+
`192.0.2.10 - alice [05/Jan/2026:10:00:00 +0000] "GET /p?email=a%40example.com HTTP/1.1" 200 12 "-" "curl/8"`.
|
|
337
|
+
It is never selected from the file extension.
|
|
338
|
+
|
|
339
|
+
- Every line is still scanned exactly like `text` (property-tested). Fields get nginx variable
|
|
340
|
+
names as paths: `/remote_addr`, `/ident`, `/remote_user`, `/time_local`, `/request` (or
|
|
341
|
+
`/request/method`, `/request/path`, `/request/protocol`), `/status`, `/body_bytes_sent`,
|
|
342
|
+
`/http_referer` and `/http_user_agent`. Query strings of the request and the referer become
|
|
343
|
+
fields as well (`/request/query/email`), so field-name heuristics and `field_names` rules
|
|
344
|
+
apply to parameters such as `email` or `access_token`.
|
|
345
|
+
- Log escapes (`\x22`, `\"`) and percent-encoding (`%40`) are decoded and the decoded value is
|
|
346
|
+
scanned too, so `alice.test%40example.com` is found although the raw-text scan misses it.
|
|
347
|
+
- Output keeps the line as it was; only protected spans change. Replacements are
|
|
348
|
+
percent-encoded in paths and query strings and `\xHH`-escaped elsewhere, and each rewritten
|
|
349
|
+
line must still parse with all its fields, or the run fails and nothing is published.
|
|
350
|
+
- A `-` field gets no field context. Only lines that start with the common/combined layout are
|
|
351
|
+
recognised: fields after the user agent (such as `$http_x_forwarded_for`), virtual-host
|
|
352
|
+
prefixes and custom layouts get text-mode scanning only. `drop_field` is not supported (it
|
|
353
|
+
becomes `review`).
|
|
354
|
+
|
|
355
|
+
### syslog and CEF
|
|
356
|
+
|
|
357
|
+
`--format syslog` reads RFC 5424 and RFC 3164 (BSD) syslog lines, including files written by
|
|
358
|
+
rsyslog (no `<PRI>`, traditional or RFC 3339 timestamps), and CEF records, either bare or as the
|
|
359
|
+
message of a syslog line:
|
|
360
|
+
|
|
361
|
+
```text
|
|
362
|
+
<34>1 2026-01-05T10:00:00Z web01 billing 4242 ID47 [auth@32473 email="alice.test@example.com"] login failed
|
|
363
|
+
Jan 5 10:00:00 web01 sshd[4242]: Accepted publickey for USER_TEST_001
|
|
364
|
+
<134>Jan 05 10:00:00 fw01 CEF:0|Vendor|Product|1.0|100|Login ok|5|suser=alice.test src=192.0.2.10
|
|
365
|
+
```
|
|
366
|
+
|
|
367
|
+
It is never selected from the file extension.
|
|
368
|
+
|
|
369
|
+
- Every line is still scanned exactly like `text` (property-tested). Header fields become
|
|
370
|
+
`/timestamp`, `/hostname`, `/app_name`, `/procid`, `/msgid`; RFC 5424 structured data becomes
|
|
371
|
+
`/sd/<sd-id>/<name>`; the message is `/msg`.
|
|
372
|
+
- CEF header fields are `/cef/device_vendor`, `/cef/device_product`, `/cef/device_version`,
|
|
373
|
+
`/cef/signature_id`, `/cef/event_name` (CEF "Name") and `/cef/severity`; extensions are
|
|
374
|
+
`/cef/extension/<key>`. Common short keys also get a canonical name for the field-name
|
|
375
|
+
heuristics (`suser` is read as `source_user_name`, `src` as `source_ip`, `dhost` as
|
|
376
|
+
`destination_hostname`, ...), and `cs1`-style custom fields use their `cs1Label`. Findings keep
|
|
377
|
+
the raw key, so `field_names: [suser]` rules apply.
|
|
378
|
+
- Escapes are decoded for scanning and re-applied to replacements: `\"`, `\\`, `\]` in
|
|
379
|
+
structured data; `\|`, `\\` in CEF headers; `\=`, `\\`, `\n` in CEF extensions.
|
|
380
|
+
Header fields get `_` for whitespace (an RFC 3164 tag for anything outside
|
|
381
|
+
`[A-Za-z0-9_./-]`). Each rewritten line must still parse with all its fields, or the run fails.
|
|
382
|
+
- `-` (NILVALUE) fields get no field context. RFC 3164 lines need a `tag:` (or a CEF message) to
|
|
383
|
+
be recognised; other layouts, LEEF and multi-line messages get text-mode scanning only.
|
|
384
|
+
`drop_field` is not supported (it becomes `review`).
|
|
385
|
+
|
|
386
|
+
### Stack traces (Python, JVM, Node.js)
|
|
387
|
+
|
|
388
|
+
`--format stacktrace` reads a log like `text` and also recognises stack trace lines, so a policy
|
|
389
|
+
can decide what leaves with them. It is never selected from the file extension.
|
|
390
|
+
|
|
391
|
+
- Every line is still scanned exactly like `text` (property-tested); a credential or email in a
|
|
392
|
+
frame or source line is found and handled whatever the policy does with the frame.
|
|
393
|
+
- Frame lines (Python `File "...", line N, in f`; JVM `at a.B.c(B.java:12)`; Node
|
|
394
|
+
`at fn (x.js:1:2)` and `at x.js:1:2`) and the indented Python source/caret lines under a frame
|
|
395
|
+
get a `proprietary.source` finding from the `stack_trace` detector, at `/stack/frames/N` and
|
|
396
|
+
`/stack/frames/N/source`. Rules can select them by category or by `detectors: [stack_trace]`.
|
|
397
|
+
A policy without such a rule falls back to `unknown_sensitive_behavior` (review or block).
|
|
398
|
+
Starter policies: `dev_local` keeps frames; `developer_logs` keeps them for `local_dev`,
|
|
399
|
+
`approved_internal` and `production_monitoring` and redacts them for every other destination;
|
|
400
|
+
`external_ai` redacts them; the other starter policies have no rule for them, so they fall
|
|
401
|
+
back to review (`prd_external_ai`: block).
|
|
402
|
+
- Other findings on trace lines get the trace path too: the Python exception line is
|
|
403
|
+
`/stack/exception`, JVM `Caused by:`/`Suppressed:` lines are `/stack/caused_by` and
|
|
404
|
+
`/stack/suppressed`.
|
|
405
|
+
- Redacting frames keeps the exception type and message for debugging:
|
|
406
|
+
|
|
407
|
+
```yaml
|
|
408
|
+
- id: strip-stack-frames
|
|
409
|
+
priority: 200
|
|
410
|
+
match: {categories: [proprietary.source], destinations: [external_ai]}
|
|
411
|
+
action: redact
|
|
412
|
+
```
|
|
413
|
+
|
|
414
|
+
- There is no lookahead and nothing is buffered: each line is classified from the lines before
|
|
415
|
+
it, keeps the `max_record_bytes` limit, and a trace of any length is streamed. So a JVM or Node
|
|
416
|
+
header line (`Exception in thread ...`, `TypeError: ...`) is not marked as part of the trace,
|
|
417
|
+
and frames are recognised on their own (a truncated or interleaved trace still has every frame
|
|
418
|
+
flagged). An unrelated log line ends a trace; frame numbering restarts after it.
|
|
419
|
+
- Matching is structural, not semantic: any indented `at name (...)` line counts as a frame, and
|
|
420
|
+
Python source lines are only recognised after a traceback header or frame. Python exception
|
|
421
|
+
groups (`| ` prefixes), .NET, Go, Ruby and other runtimes get text-mode scanning only.
|
|
422
|
+
`drop_field` is not supported (it becomes `review`).
|
|
423
|
+
|
|
424
|
+
### Pipes (stdin/stdout)
|
|
425
|
+
|
|
426
|
+
Use `-` as INPUT to read stdin (then `--format` is required), and `sanitize --output -` to write
|
|
427
|
+
the sanitized data to stdout:
|
|
428
|
+
|
|
429
|
+
```bash
|
|
430
|
+
kubectl logs my-pod | ciphergate sanitize - --format text --policy policies/prd_external_ai.yml \
|
|
431
|
+
--environment prd --target-environment external --destination external_ai \
|
|
432
|
+
--output - --apply --report build/report.json > build/safe.log
|
|
433
|
+
```
|
|
434
|
+
|
|
435
|
+
- stdout receives data **only after every check passed**. A blocked, review, rescan-failed or
|
|
436
|
+
errored run writes nothing to stdout and exits nonzero, so always check the exit status (in
|
|
437
|
+
shells, `set -o pipefail`).
|
|
438
|
+
- With `--output -` the human summary goes to stderr. Use `--report PATH` for the JSON report;
|
|
439
|
+
`--report -` and `--report-format json` without `--report` are refused because stdout carries
|
|
440
|
+
the data.
|
|
441
|
+
- stdin is copied into a private temporary workspace (owner-only on POSIX), capped by
|
|
442
|
+
`--max-bytes`, and deleted when the run ends. Reports name the input `<stdin>` and the output
|
|
443
|
+
`<stdout>`.
|
|
444
|
+
- If the reader closes the pipe early, the run exits 5 (`stdout_broken_pipe`): what it received
|
|
445
|
+
is incomplete.
|
|
446
|
+
- A file actually named `-` must be passed as `./-`.
|
|
447
|
+
|
|
448
|
+
| Code | Meaning |
|
|
449
|
+
|---|---|
|
|
450
|
+
| 0 | Completed; no block/review; required context validated |
|
|
451
|
+
| 1 | Blocked, review required, missing/conflicting context |
|
|
452
|
+
| 2 | Command-line usage error |
|
|
453
|
+
| 3 | Policy syntax/schema/semantic error |
|
|
454
|
+
| 4 | Input parse/encoding/size/format error |
|
|
455
|
+
| 5 | Output/path error (exists without `--force`, alias, symlink, write failure) |
|
|
456
|
+
| 6 | Key or transform configuration missing/invalid, or a value a transform cannot handle |
|
|
457
|
+
| 7 | Strict rescan found gating residual findings (output withheld) |
|
|
458
|
+
| 8 | Unexpected internal error (safe message) |
|
|
459
|
+
| 130 | Interrupted (no output published) |
|
|
460
|
+
|
|
461
|
+
### Troubleshooting
|
|
462
|
+
|
|
463
|
+
- `destination_required_but_missing` — pass `--destination` (and `--environment`).
|
|
464
|
+
- `environment_context_conflict` — the policy pins that value; use a matching policy.
|
|
465
|
+
- `no_applicable_rule_requires_review` — add a rule for that category; inspect lists them.
|
|
466
|
+
- `key_missing` / exit 6 — set `CIPHERGATE_HMAC_KEY` (32+ bytes) or `CIPHERGATE_HMAC_KEY_FILE`.
|
|
467
|
+
- `key_file_invalid` / exit 6 — the key file is unreadable, too large, not a regular file,
|
|
468
|
+
accessible to group/others (POSIX), or both the variable and `_FILE` are set.
|
|
469
|
+
- `format_required` — unknown extension; pass `--format`.
|
|
470
|
+
|
|
471
|
+
## Python API
|
|
472
|
+
|
|
473
|
+
```python
|
|
474
|
+
from ciphergate import load_policy, sanitize_file, ContextInput, Environment, Destination
|
|
475
|
+
policy = load_policy("policies/developer_logs.yml")
|
|
476
|
+
report = sanitize_file("app.log", "app.clean.log", policy,
|
|
477
|
+
ContextInput(environment=Environment.DEV, destination=Destination.LOCAL_DEV))
|
|
478
|
+
print(report.status, report.exit_code)
|
|
479
|
+
```
|
|
480
|
+
|
|
481
|
+
Streams and directories use the same pipeline and the same safety rules as the CLI:
|
|
482
|
+
|
|
483
|
+
```python
|
|
484
|
+
import io
|
|
485
|
+
from ciphergate import InputFormat, SanitizeOptions, sanitize_stream, sanitize_directory
|
|
486
|
+
|
|
487
|
+
out = io.BytesIO()
|
|
488
|
+
report = sanitize_stream(open("app.log", "rb"), out, policy, ctx,
|
|
489
|
+
SanitizeOptions(format=InputFormat.TEXT))
|
|
490
|
+
# out is written only if report.ok; a blocked/review/failed run leaves it untouched.
|
|
491
|
+
|
|
492
|
+
batch = sanitize_directory("logs/", "logs.clean/", policy, ctx, recursive=True)
|
|
493
|
+
print(batch.status, batch.exit_code, batch.to_dict()["summary"])
|
|
494
|
+
```
|
|
495
|
+
|
|
496
|
+
- `*_stream` functions take binary streams (`"rb"`, `io.BytesIO`) and need an explicit format
|
|
497
|
+
(`fmt=` or `SanitizeOptions.format`). The input is spooled to a private temporary workspace,
|
|
498
|
+
capped at `max_input_bytes`, and removed afterwards. `sanitize_stream` copies to `output`
|
|
499
|
+
only after every check has passed. If that copy fails part-way, it raises `OutputWriteError`
|
|
500
|
+
(`stream_write_failed`), and `output` may hold an incomplete prefix of sanitized data.
|
|
501
|
+
- `*_directory` functions return a `BatchReport` (`status`, `exit_code`, `files`, `skipped`,
|
|
502
|
+
`to_dict()` in the `ciphergate.batch-report/v1` schema). `sanitize_directory` needs a new
|
|
503
|
+
output directory outside the input, publishes it only if every file was sanitized, and refuses
|
|
504
|
+
`SanitizeOptions(force=True)`.
|
|
505
|
+
- As with `sanitize_file`, `SanitizeOptions()` defaults to `apply=True`; pass `apply=False` for
|
|
506
|
+
a plan. Keys come from the environment unless `SanitizeOptions(keys=...)` is given.
|
|
507
|
+
|
|
508
|
+
Supported API: `load_policy`, `validate_policy`, `inspect_file`, `plan_file`, `sanitize_file`,
|
|
509
|
+
`rescan_file`, the `*_stream` and `*_directory` functions above, and the types exported from
|
|
510
|
+
`ciphergate` (including `BatchReport`). Other modules are internal.
|
|
511
|
+
|
|
512
|
+
## Documentation
|
|
513
|
+
|
|
514
|
+
[Architecture](docs/architecture.md) · [Policy schema](docs/policy-schema.md) ·
|
|
515
|
+
[Detector coverage](docs/detector-coverage.md) · [Threat model](docs/threat-model.md) ·
|
|
516
|
+
[Security & privacy](docs/security-and-privacy.md) · [Limitations](docs/limitations.md) ·
|
|
517
|
+
[Examples](docs/examples.md) · [Claude Code tooling](docs/claude-code-tooling.md) ·
|
|
518
|
+
[Security policy](SECURITY.md) · [Contributing](CONTRIBUTING.md) · [Changelog](CHANGELOG.md)
|
|
519
|
+
|
|
520
|
+
## Development
|
|
521
|
+
|
|
522
|
+
```bash
|
|
523
|
+
python -m pytest -q --cov=ciphergate
|
|
524
|
+
ruff check src tests scripts && ruff format --check src tests scripts
|
|
525
|
+
mypy
|
|
526
|
+
```
|
|
527
|
+
|
|
528
|
+
## License
|
|
529
|
+
|
|
530
|
+
Licensed under the [Apache License, Version 2.0](LICENSE); see also [NOTICE](NOTICE). The license
|
|
531
|
+
covers the software only: it is not legal advice and makes no compliance claim.
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
ciphergate/__init__.py,sha256=1xN3LaV2hJv7xNAPlM_cOCF_8PvSCDcKK5JBt3z8aYg,2844
|
|
2
|
+
ciphergate/__main__.py,sha256=wOnYViq_FCF3tHyKiX6J3YVjqOZv67Fq87IMpYhAuJI,110
|
|
3
|
+
ciphergate/cli.py,sha256=vymgmj7rIKFqaDdpgkY4uhIPHadfem8g7wg5pdos0zM,28502
|
|
4
|
+
ciphergate/config.py,sha256=m7MIFFue-cg9EIbUNw6oJNO5xTT-YkcMh8SSBRXplL0,1764
|
|
5
|
+
ciphergate/constants.py,sha256=pWnNutyJ622cYkNFkwNpzm6-gaMcAkD5ocR4-T9Rj3k,8360
|
|
6
|
+
ciphergate/errors.py,sha256=JFO5Ungzd_l7QlfHUs13AmeFBkYgCyEb9J6-Qur_ZzI,3315
|
|
7
|
+
ciphergate/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
8
|
+
ciphergate/detectors/__init__.py,sha256=OxjG9zduL0wA7xVl8XWxtFJcUyy_r3oblhIR1U2p8YI,219
|
|
9
|
+
ciphergate/detectors/base.py,sha256=Uwr3QoapU7vPcigjqC415oUTEjpNIURM7VfXl76iJSU,1672
|
|
10
|
+
ciphergate/detectors/custom_regex.py,sha256=yyWgZoDdz2BBOuScBXsTCN789LNsdgQYmr34EodAk6Y,1279
|
|
11
|
+
ciphergate/detectors/dates.py,sha256=VV2Yp2LPAKzeQeHj-1JHuJwBKI5ktBOar5uKMc5Z1R8,3441
|
|
12
|
+
ciphergate/detectors/email.py,sha256=mjPJEq_GwKiSFy9G9TlPuJpWngM995LHunlTfsd4K1M,1848
|
|
13
|
+
ciphergate/detectors/field_names.py,sha256=3sC1OiI2-RW9N9Pi_v1wdjD7KSCHrGpnpIOik7AritQ,7446
|
|
14
|
+
ciphergate/detectors/ip_address.py,sha256=o_Wg0C2HqU_wu6HumNv5sfzKWdNrTgZt8FRuWH03Ggc,2976
|
|
15
|
+
ciphergate/detectors/network.py,sha256=FuCOXKSjg0qx8vPlg0PHR4yePDWGiOgB5PDYC_XmSik,1385
|
|
16
|
+
ciphergate/detectors/payment_card.py,sha256=UUy2QwzeCtCINB9U6sI1ABp3Q0f43i1t0iZ1Vq96LMQ,2622
|
|
17
|
+
ciphergate/detectors/phone.py,sha256=1SZIM024gNk8jK_D6ci3drajvN75vtlWEs6aJ_gLK0E,2628
|
|
18
|
+
ciphergate/detectors/presidio_ner.py,sha256=Fz9I5m3u6nFX1NqbRyRNWFo0XBl7t0NyKOgaTIaWgr4,4745
|
|
19
|
+
ciphergate/detectors/regex_safety.py,sha256=ICxCd77SEvpmDUzzPgc6qOzYlyqSrQ5lLsrP_2ZGs1w,1655
|
|
20
|
+
ciphergate/detectors/registry.py,sha256=qt-IDZ1_gUYK0cyT0YV-UpxcvNEfjV68mdukcWhKRT8,7966
|
|
21
|
+
ciphergate/detectors/secrets.py,sha256=atg5jKqpIVouWYZbM_wMdt1Wqsl584O2V3wQk6o6N-o,4808
|
|
22
|
+
ciphergate/models/__init__.py,sha256=kS94nQBQL7fsR9rXgQPTHoFMeMWSCfR-Wv-U1o7npWM,68
|
|
23
|
+
ciphergate/models/context.py,sha256=X-2118oXDYSjNiF_K_-BmjKqATVUMwLVQvW4r-sxALE,2227
|
|
24
|
+
ciphergate/models/finding.py,sha256=co5VSbZ3qzYXCjP4XcrrCEBeStaP0nzhuXIDLNLYAHY,3103
|
|
25
|
+
ciphergate/models/policy.py,sha256=GmsNG9WZdacnZoD2sAUMKFbeZW0W4eQB-_FmT_vwNqA,14637
|
|
26
|
+
ciphergate/models/report.py,sha256=71aakSmUalBHJHkA_BsmEucHojgN4ZVUMXOw-WQdn4Y,3737
|
|
27
|
+
ciphergate/pipeline/__init__.py,sha256=5Q5K19ubA2aCwyux5zVZM002PQJ841kkJkpRl-0UPZM,84
|
|
28
|
+
ciphergate/pipeline/atomic_output.py,sha256=AHBhsbRA9RRsPoxTCJ1V12a6FUnzh4RpGXovUtLGwKs,5670
|
|
29
|
+
ciphergate/pipeline/batch.py,sha256=oIb7Wt0JXARN7uqCIPZ1xg-C8PH9KPqQ_7Adz4oWqss,19348
|
|
30
|
+
ciphergate/pipeline/inspector.py,sha256=mho9CvuEBsAG7fBjir7NfW2tINoDYAsTxU_FSDo-SLA,12185
|
|
31
|
+
ciphergate/pipeline/paths.py,sha256=z1p8LlMTFrAhVDHqkpFuP2WIJHIgjfq5qHXDVafuZZU,3523
|
|
32
|
+
ciphergate/pipeline/planner.py,sha256=QjhoP-v4PzwD2qopTT4NF7QTYi1vmp4fLDAQtjJn0GM,7680
|
|
33
|
+
ciphergate/pipeline/rescan.py,sha256=eTYITPkDdWBNiHOolLWs-k7XYid-LyQMxISEL_M3cDE,5438
|
|
34
|
+
ciphergate/pipeline/sanitizer.py,sha256=ThmTLAlqNbfPPlKMz4snkpsKQqN4HGPotjKRaVZmEsw,14884
|
|
35
|
+
ciphergate/pipeline/stdio.py,sha256=NnBihUn6zr2v1UFBji9BuSjF6ijnn0M_CIyqolJCo_I,6376
|
|
36
|
+
ciphergate/pipeline/streams.py,sha256=axAz5jeKhgO7hrO90hqMtXg6JVy2mcjuitjyHzwQxz4,5635
|
|
37
|
+
ciphergate/policy/__init__.py,sha256=uUbLirNpTweVcnTZoSV_VRgR3yI4BkX7GJiBVFAtmIs,617
|
|
38
|
+
ciphergate/policy/context.py,sha256=8tHgqc8eWRhD96JP2iDIeS1GSgbBjhKw2U7KYKHTmG8,7370
|
|
39
|
+
ciphergate/policy/engine.py,sha256=OuYXyyxbBAQFVY64whRJmP3x7F7hjWjIWp6jXcL123w,7657
|
|
40
|
+
ciphergate/policy/loader.py,sha256=FQh5L6uv1HxaKC-1f9wAk2r-3H6gmex7ipvkaLS_X6I,4555
|
|
41
|
+
ciphergate/policy/precedence.py,sha256=jGs5gXGcxSvyovHerGStdDZCqTZw4TT3xbDT6G2BwGI,5272
|
|
42
|
+
ciphergate/policy/validator.py,sha256=DpoReXH6wFc1B9LXJf4snLe8Kz-V9X73m2S9IDhB91A,25743
|
|
43
|
+
ciphergate/readers/__init__.py,sha256=JfcbPlaO7qUZOde9VPLdMyW8BV8luNZfQ6XCRn_iD6c,3981
|
|
44
|
+
ciphergate/readers/accesslog_reader.py,sha256=j8Ku5pdcu5Rs6nIjAFw78jdcg-MEA8Od-uaVGpF4LUE,9701
|
|
45
|
+
ciphergate/readers/base.py,sha256=mew-8ISMrwKgsbaiuS4odQqaMwVB5aD7hZ2i1l-qnPg,3942
|
|
46
|
+
ciphergate/readers/csv_reader.py,sha256=0xvwv1TbmZAfN0oZYje1OVezWQnRl6bm8YakUmN8Alw,9297
|
|
47
|
+
ciphergate/readers/json_reader.py,sha256=_f-mxC-KySygwv_uI4t2zIBDyb6CB8qkYyd1-VtK8ME,6190
|
|
48
|
+
ciphergate/readers/jsonl_reader.py,sha256=LPmOPKh0T04FZSc4ER3_QBdz80lvlwQmvlkyyAWycAU,3530
|
|
49
|
+
ciphergate/readers/jsonlog_reader.py,sha256=fxqN02VjHT6Y8nu5xksImPgF7CcYmJB1pDoA_4YJCN4,6442
|
|
50
|
+
ciphergate/readers/line_structure.py,sha256=ghTcx1hS6EhvdKVTXJSfme09kw4n_Lzyt7-M0HRs8cU,5411
|
|
51
|
+
ciphergate/readers/logfmt_reader.py,sha256=qvU768yslj_HnX5xvTew06q-12XzlPSCq7AtXMwLCGY,3693
|
|
52
|
+
ciphergate/readers/stacktrace_reader.py,sha256=ZPE_761YoqQaeGpJH8nAQD0RkgWyRywapr1-awOS6Bk,4383
|
|
53
|
+
ciphergate/readers/syslog_reader.py,sha256=3Jry_YxE6w0SxwHAZfgtrFVvcYAmsThr0hXjeXktM4s,11845
|
|
54
|
+
ciphergate/readers/text_reader.py,sha256=1Vc1MRljomHa5cfVPHZ0ot34VBDftS7y_bfI8EFncMI,1991
|
|
55
|
+
ciphergate/reporting/__init__.py,sha256=YP7P1DweiCe0pzmrlzhdS413k0Heuz4_ND5l-32AdpA,74
|
|
56
|
+
ciphergate/reporting/audit.py,sha256=saIsbTzjgE7kNS7jG2RoBCCzCGDk71JTF1fXSCMW6EU,2895
|
|
57
|
+
ciphergate/reporting/safe_text.py,sha256=TN8BkILdnN3N6DaevjKKALbGUlvX6V8ajBxsOwRU0rI,2417
|
|
58
|
+
ciphergate/reporting/serializer.py,sha256=Dliu2QXHYpYFvxZer6FuHpExWqgqs_CNZ-E0VsIlLYo,1443
|
|
59
|
+
ciphergate/reporting/terminal.py,sha256=C05-5kGFXiXKEncW-pcVlImVe4KUhmAOY7yFu-m3J9I,6226
|
|
60
|
+
ciphergate/templates/starter_policy.yml,sha256=VC-9ZvqgWq8U2SKTDT3sielVM4h2rcRHaTUu11rD06Y,2448
|
|
61
|
+
ciphergate/transforms/__init__.py,sha256=-WjKEtECFJkDFkW98AGhFCqaR16eIxvr8qoK_OjjnZ8,265
|
|
62
|
+
ciphergate/transforms/date_shift.py,sha256=se3RXh6aoe-diztdA9g4SvfcEKzr0865T9setM6oORs,3181
|
|
63
|
+
ciphergate/transforms/generalize.py,sha256=v6KPKrSEfqIc9GDFySfuY_PJOIdci-NRN_qoAxGryF8,3824
|
|
64
|
+
ciphergate/transforms/keys.py,sha256=suHEFv-sTHFD2fy5UcBVVgoYnwjei_X8dsXCNsv56nE,6697
|
|
65
|
+
ciphergate/transforms/pseudonymize.py,sha256=idn948Jw89jgZKH_q0JqRGX8ADJ4lRjALzo9GieKBRQ,4008
|
|
66
|
+
ciphergate/transforms/redact.py,sha256=Zq4cXuO5SvUq6vPTBBJTFlkJ9kPArOmy6tt7vAuftDk,297
|
|
67
|
+
ciphergate/transforms/registry.py,sha256=xL8mxaIkJDg_jmAnDqOmI0NSljrIjQ2tIPDRzLoYEt8,7902
|
|
68
|
+
ciphergate/transforms/replace.py,sha256=kzN5p1vTA36sNn9dqxXTwgmE7H1kc7AAO8wGHijZFWQ,590
|
|
69
|
+
ciphergate_cli-0.7.0.dist-info/METADATA,sha256=7AxESHLmdAsIlHgg_dO1GEaSTgDyFB8SpQswSoRu8wk,29268
|
|
70
|
+
ciphergate_cli-0.7.0.dist-info/WHEEL,sha256=2iZrJkDIkeDh0k9SikKMqFuRP-ua6mb4Ir1AsER4BEw,87
|
|
71
|
+
ciphergate_cli-0.7.0.dist-info/entry_points.txt,sha256=-VbFZjjWHKqw4YXsBtZh1bkvyFcAfNZiKIeALxFSFwo,51
|
|
72
|
+
ciphergate_cli-0.7.0.dist-info/licenses/LICENSE,sha256=yVuuHRzgI17MzTVgt3LsHvuX80innw--CmNPDCzO_iw,11358
|
|
73
|
+
ciphergate_cli-0.7.0.dist-info/licenses/NOTICE,sha256=UuXmeAv3VIfY3JgnqbyTz_9DbNL4w60eSgIEpQGE2bo,119
|
|
74
|
+
ciphergate_cli-0.7.0.dist-info/RECORD,,
|