docs(platform): add policy configs, runbooks, ops scripts and platform documentation
Config policies (16 files): alert_routing, architecture_pressure, backlog, cost_weights, data_governance, incident_escalation, incident_intelligence, network_allowlist, nodes_registry, observability_sources, rbac_tools_matrix, release_gate, risk_attribution, risk_policy, slo_policy, tool_limits, tools_rollout Ops (22 files): Caddyfile, calendar compose, grafana voice dashboard, deployments/incidents logs, runbooks for alerts/audit/backlog/incidents/sofiia/voice, cron jobs, scripts (alert_triage, audit_cleanup, migrate_*, governance, schedule), task_registry, voice alerts/ha/latency/policy Docs (30+ files): HUMANIZED_STEPAN v2.7-v3 changelogs and runbooks, NODA1/NODA2 status and setup, audit index and traces, backlog, incident, supervisor, tools, voice, opencode, release, risk, aistalk, spacebot Made-with: Cursor
This commit is contained in:
275
docs/tools/data_governance_tool.md
Normal file
275
docs/tools/data_governance_tool.md
Normal file
@@ -0,0 +1,275 @@
|
||||
# data_governance_tool — Data Governance & Privacy
|
||||
|
||||
**Категорія:** Security / Privacy / Compliance
|
||||
**RBAC:** `tools.data_gov.read` (scan_repo, scan_audit, retention_check, policy), `tools.data_gov.gate` (gate)
|
||||
**Ролі:** `agent_cto` (read + gate), `agent_oncall` (read)
|
||||
**Timeout:** 30 s
|
||||
**Rate limit:** 5 rpm
|
||||
|
||||
---
|
||||
|
||||
## Призначення
|
||||
|
||||
`data_governance_tool` — детермінований, read-only сканер для виявлення:
|
||||
|
||||
- **PII в коді/доках** (email, телефон, кредитні картки, паспорти)
|
||||
- **Хардкоджених секретів** (API keys, private keys, токени)
|
||||
- **Ризиків логування** (sensitive fields у logger calls, raw payload в audit records)
|
||||
- **Відсутності retention/TTL** при збереженні даних
|
||||
- **Аномалій в audit-стрімі** (PII у metadata, аномально великі outputs)
|
||||
- **Наявності cleanup-механізмів** (task_registry.yml, runbooks)
|
||||
|
||||
**Перший рівень — warning-only**: gate `privacy_watch` завжди `pass=True`, але генерує конкретні рекомендації.
|
||||
|
||||
---
|
||||
|
||||
## Actions
|
||||
|
||||
### `scan_repo` — статичний аналіз файлів
|
||||
|
||||
```json
|
||||
{
|
||||
"action": "scan_repo",
|
||||
"mode": "fast",
|
||||
"max_files": 200,
|
||||
"paths_include": ["services/", "config/", "ops/"],
|
||||
"paths_exclude": ["**/node_modules/**", "**/*.lock"],
|
||||
"focus": ["pii", "secrets", "logging", "retention"]
|
||||
}
|
||||
```
|
||||
|
||||
**Режими:**
|
||||
- `fast` (default): `.py`, `.yml`, `.yaml`, `.json`, `.sh` — оптимізовано для CI
|
||||
- `full`: всі розширення з `config/data_governance_policy.yml`
|
||||
|
||||
**Категорії перевірок:**
|
||||
|
||||
| ID | Категорія | Severity | Опис |
|
||||
|----|-----------|----------|------|
|
||||
| `DG-PII-001` | pii | warning | Email address |
|
||||
| `DG-PII-002` | pii | warning | Phone number |
|
||||
| `DG-PII-003` | pii | **error** | Credit card |
|
||||
| `DG-PII-004` | pii | warning | Passport-like ID |
|
||||
| `DG-SEC-000` | secrets | **error** | Secret value (inherited from governance) |
|
||||
| `DG-SEC-001` | secrets | **error** | Private key block |
|
||||
| `DG-LOG-001` | logging | warning | Sensitive field in logger call |
|
||||
| `DG-AUD-001` | logging | **error** | Raw payload near audit/log write |
|
||||
| `DG-RET-001` | retention | warning | Storage write без TTL/retention |
|
||||
|
||||
**Відповідь:**
|
||||
```json
|
||||
{
|
||||
"pass": true,
|
||||
"summary": "Scanned 87 files (fast mode). Found 0 errors, 3 warnings, 1 info.",
|
||||
"stats": { "errors": 0, "warnings": 3, "infos": 1, "files_scanned": 87 },
|
||||
"findings": [
|
||||
{
|
||||
"id": "DG-LOG-001",
|
||||
"category": "logging",
|
||||
"severity": "warning",
|
||||
"title": "Potential sensitive field logged in auth.py",
|
||||
"evidence": { "path": "services/router/auth.py", "lines": "L42-L46", "details": "token=***REDACTED***" },
|
||||
"recommended_fix": "Apply redact() before logging. Log hash+last4 for identifiers."
|
||||
}
|
||||
],
|
||||
"recommendations": ["Review logger calls for sensitive fields. Apply redact()..."]
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### `scan_audit` — аналіз audit-стріму
|
||||
|
||||
```json
|
||||
{
|
||||
"action": "scan_audit",
|
||||
"backend": "jsonl",
|
||||
"time_window_hours": 24,
|
||||
"max_events": 50000
|
||||
}
|
||||
```
|
||||
|
||||
**Перевірки:**
|
||||
|
||||
| ID | Опис |
|
||||
|----|------|
|
||||
| `DG-AUD-101` | PII-like pattern в полях метадата audit event (user_id, workspace_id) |
|
||||
| `DG-AUD-102` | Аномально великий `out_size` (>64KB за замовчуванням) |
|
||||
|
||||
---
|
||||
|
||||
### `retention_check` — перевірка cleanup-механізмів
|
||||
|
||||
```json
|
||||
{
|
||||
"action": "retention_check",
|
||||
"check_audit_cleanup_task": true,
|
||||
"check_jsonl_rotation": true,
|
||||
"check_memory_retention_docs": true,
|
||||
"check_logs_retention_docs": true
|
||||
}
|
||||
```
|
||||
|
||||
| ID | Severity | Опис |
|
||||
|----|----------|------|
|
||||
| `DG-RET-201` | warning | Не знайдено cleanup task або runbook для audit |
|
||||
| `DG-RET-202` | info | Cleanup/rotation задокументовано |
|
||||
| `DG-RET-203` | info | JSONL rotation реалізовано |
|
||||
| `DG-RET-204` | warning | JSONL rotation не підтверджено |
|
||||
| `DG-RET-205` | info | Memory retention policy не знайдено |
|
||||
| `DG-RET-206` | info | Log retention не задокументовано |
|
||||
|
||||
---
|
||||
|
||||
### `policy` — поточні політики
|
||||
|
||||
```json
|
||||
{ "action": "policy" }
|
||||
```
|
||||
|
||||
Повертає конфіг `config/data_governance_policy.yml`: retention, pii_patterns, logging_rules, severity_behavior.
|
||||
|
||||
---
|
||||
|
||||
## Evidence masking
|
||||
|
||||
**Всі evidence snippets маскуються** перед поверненням:
|
||||
1. Через `redact()` з `tool_governance` (успадковані `_SECRET_PATTERNS`)
|
||||
2. Truncate до 200 символів
|
||||
3. Ніяких raw значень у відповіді
|
||||
|
||||
---
|
||||
|
||||
## Інтеграція в release_check (privacy_watch gate)
|
||||
|
||||
`privacy_watch` — **warning-only gate**: завжди `pass=true`, додає рекомендації.
|
||||
|
||||
```yaml
|
||||
# ops/task_registry.yml (release_check inputs)
|
||||
run_privacy_watch: true # вмикає gate (default: true)
|
||||
privacy_watch_mode: "fast" # fast|full
|
||||
privacy_audit_window_hours: 24 # вікно для scan_audit
|
||||
```
|
||||
|
||||
**Gate output:**
|
||||
```json
|
||||
{
|
||||
"name": "privacy_watch",
|
||||
"status": "pass",
|
||||
"errors": 0,
|
||||
"warnings": 2,
|
||||
"infos": 1,
|
||||
"top_findings": [
|
||||
{ "id": "DG-LOG-001", "title": "...", "severity": "warning" }
|
||||
],
|
||||
"note": "3 finding(s): 0 error(s), 2 warning(s)",
|
||||
"recommendations": ["Review logger calls for sensitive fields."]
|
||||
}
|
||||
```
|
||||
|
||||
Якщо `data_governance_tool` недоступний → `skipped: true`, реліз не блокується.
|
||||
|
||||
---
|
||||
|
||||
## Конфігурація: `config/data_governance_policy.yml`
|
||||
|
||||
```yaml
|
||||
retention:
|
||||
audit_jsonl_days: 30
|
||||
audit_postgres_days: 90
|
||||
large_output_bytes: 65536 # threshold для DG-AUD-102
|
||||
|
||||
pii_patterns:
|
||||
email: { severity: "warning", ... }
|
||||
credit_card: { severity: "error", ... }
|
||||
|
||||
logging_rules:
|
||||
forbid_logging_fields: [password, token, secret, api_key, ...]
|
||||
raw_payload_indicators: [payload, prompt, messages, transcript, ...]
|
||||
redaction_calls: [redact, mask, sanitize, ...]
|
||||
|
||||
severity_behavior:
|
||||
gate_mode: "warning_only" # або "strict" (блокує на error)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## RBAC
|
||||
|
||||
```yaml
|
||||
data_governance_tool:
|
||||
actions:
|
||||
scan_repo: { entitlements: ["tools.data_gov.read"] }
|
||||
scan_audit: { entitlements: ["tools.data_gov.read"] }
|
||||
retention_check: { entitlements: ["tools.data_gov.read"] }
|
||||
policy: { entitlements: ["tools.data_gov.read"] }
|
||||
gate: { entitlements: ["tools.data_gov.gate"] }
|
||||
|
||||
role_entitlements:
|
||||
agent_cto: [..., tools.data_gov.read, tools.data_gov.gate]
|
||||
agent_oncall: [..., tools.data_gov.read]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Limits
|
||||
|
||||
```yaml
|
||||
data_governance_tool:
|
||||
timeout_ms: 30000 # 30s (file I/O + regex)
|
||||
max_chars_in: 3000 # params only
|
||||
max_bytes_out: 1MB
|
||||
rate_limit_rpm: 5
|
||||
concurrency: 1 # serial (filesystem-bound)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Security
|
||||
|
||||
- **Read-only**: ніяких записів, змін, видалень
|
||||
- **Path traversal protection**: всі шляхи перевіряються проти `repo_root`
|
||||
- **Evidence masking**: `redact()` + truncation — raw secrets ніколи не повертаються
|
||||
- **Never-scan list**: `.env`, `.pem`, `.key` файли не читаються
|
||||
- **Lock files excluded** (за замовчуванням): `*.lock` — запобігає false positives від hash-рядків у lock-файлах
|
||||
|
||||
---
|
||||
|
||||
## Тести
|
||||
|
||||
`tests/test_data_governance.py` (22 тести):
|
||||
|
||||
| Тест | Перевірка |
|
||||
|------|-----------|
|
||||
| `test_scan_repo_detects_pii_logging` | Email у logger call → DG-PII-001 |
|
||||
| `test_scan_repo_detects_logging_forbidden_field` | `token=` у logger → DG-LOG-001 |
|
||||
| `test_scan_repo_detects_secret` | Hardcoded API key → DG-SEC-000, masked |
|
||||
| `test_scan_repo_detects_private_key` | `-----BEGIN RSA PRIVATE KEY-----` → error |
|
||||
| `test_scan_repo_detects_credit_card` | 16-digit number → DG-PII-003 error |
|
||||
| `test_scan_repo_no_findings_clean` | Clean code → 0 error findings |
|
||||
| `test_scan_audit_detects_pii_in_meta` | Email у user_id → DG-AUD-101 |
|
||||
| `test_scan_audit_detects_large_output` | 200KB out_size → DG-AUD-102 |
|
||||
| `test_scan_audit_no_findings_for_clean_events` | Normal events → 0 findings |
|
||||
| `test_retention_check_missing_cleanup` | No runbook → DG-RET-201 |
|
||||
| `test_retention_check_with_cleanup` | Runbook mentions cleanup → DG-RET-202 |
|
||||
| `test_scan_repo_raw_payload_audit_write` | `payload` near logger → DG-AUD-001 |
|
||||
| `test_release_check_privacy_watch_integration` | Gate pass=True, adds recs |
|
||||
| `test_privacy_watch_skipped_on_tool_error` | Tool exception → skipped=True |
|
||||
| `test_rbac_deny` | alateya (agent_media) → denied |
|
||||
| `test_rbac_allow` | sofiia (agent_cto) → allowed |
|
||||
| `test_policy_action` | Returns structured policy |
|
||||
| `test_path_traversal_protection` | `../../etc/passwd` → None |
|
||||
| `test_scan_repo_excludes_lock_files` | `*.lock` excluded |
|
||||
| `test_mask_evidence_redacts_secrets` | key=value → masked |
|
||||
| `test_mask_evidence_truncates` | 500 chars → ≤120 |
|
||||
| `test_unknown_action_returns_error` | Invalid action → error dict |
|
||||
|
||||
---
|
||||
|
||||
## Наступні кроки
|
||||
|
||||
1. **`strict` mode** — увімкнути для `credit_card` + `private_key` (блокувати реліз)
|
||||
2. **AST-based analysis** — замість regex: точніший аналіз Python AST для logging calls
|
||||
3. **Git history scan** — перевіряти, чи не були secrets раніше в git history
|
||||
4. **GDPR retention report** — автоматичний звіт для DPO про час зберігання PII по системах
|
||||
5. **Integration з incident_triage** — DG findings у RCA якщо є privacy-related incident
|
||||
Reference in New Issue
Block a user