# 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
