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
276 lines
9.7 KiB
Markdown
276 lines
9.7 KiB
Markdown
# 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
|