feat(B-SENS): Sensitive Data Boundary + AI/Data Exposure Policy + AIProvider Compliance
Check Cross-Plugin Imports / check (push) Has been cancelled
Check Cross-Plugin Imports / check (push) Has been cancelled
B-SENS: app/core/sensitive_data.py (NEU) — zentrale Sensitive-Field-Verwaltung - SENSITIVE_FIELDS dict für contact/user/mail_account/system_settings - is_sensitive(), sanitize_dict(), register_sensitive_fields() - Integration: errors.py (Log-Redaction), audit.py (Audit-Masking), export_service.py (Export-Filter), embedding.py (Index-Filter) B-DATA-POL: AI/Data Exposure Policy - DATA_EXPOSURE_POLICY: pro Entity+Field welche Systeme erlaubt (llm_context/search/embeddings/rag/agent_memory/export) - filter_for_llm_context/search/embeddings/export/rag/agent_memory() B-AIPROV-COMP: AIProvider Compliance Metadata - Migration 0119: 7 neue Spalten an ai_providers (region, hosting_type, dpa_status, retention_policy, training_on_customer_data, transfer_notice, allowed_data_classes) - llm_client.py: get_provider_compliance() + check_data_class_allowed() B-PRIV-TEST: 76 Tests in test_sensitive_data.py — alle grün - Sensitive Fields, Exposure Policy, Provider Compliance, Secrets-always-blocked - Keine Regression: 39 LLM-Client Tests grün
This commit is contained in:
+27
-2
@@ -31,7 +31,12 @@ _SENSITIVE_PATTERNS = re.compile(
|
||||
|
||||
|
||||
def _sanitize_context(context: Any, max_depth: int = 3, _depth: int = 0) -> Any:
|
||||
"""Recursively remove sensitive keys and limit depth/size of context data."""
|
||||
"""Recursively remove sensitive keys and limit depth/size of context data.
|
||||
|
||||
Combines regex-based pattern matching with the central
|
||||
:mod:`app.core.sensitive_data` module to ensure entity-specific
|
||||
sensitive fields are also redacted.
|
||||
"""
|
||||
if _depth > max_depth:
|
||||
return "[truncated]"
|
||||
if isinstance(context, dict):
|
||||
@@ -49,6 +54,23 @@ def _sanitize_context(context: Any, max_depth: int = 3, _depth: int = 0) -> Any:
|
||||
return context
|
||||
|
||||
|
||||
def _sanitize_entity_context(context: dict[str, Any]) -> dict[str, Any]:
|
||||
"""Sanitize context dict using both pattern matching and entity-aware redaction.
|
||||
|
||||
If the context contains an ``entity_type`` key, uses :func:`sanitize_dict`
|
||||
from :mod:`app.core.sensitive_data` to redact entity-specific sensitive
|
||||
fields. Falls back to pattern-based sanitization otherwise.
|
||||
"""
|
||||
from app.core.sensitive_data import sanitize_dict
|
||||
|
||||
entity_type = context.get("entity_type") or context.get("type")
|
||||
if entity_type:
|
||||
sanitized = sanitize_dict(context, str(entity_type))
|
||||
else:
|
||||
sanitized = dict(context)
|
||||
return _sanitize_context(sanitized)
|
||||
|
||||
|
||||
# -- Request schema --
|
||||
|
||||
class ErrorReport(BaseModel):
|
||||
@@ -77,7 +99,10 @@ async def report_error(error: ErrorReport, request: Request) -> Response:
|
||||
return Response(status_code=status.HTTP_429_TOO_MANY_REQUESTS)
|
||||
|
||||
# Sanitize context to prevent leaking sensitive data
|
||||
sanitized_context = _sanitize_context(error.context) if error.context else None
|
||||
if error.context:
|
||||
sanitized_context = _sanitize_entity_context(error.context)
|
||||
else:
|
||||
sanitized_context = None
|
||||
|
||||
# Log with structured info
|
||||
logger.error(
|
||||
|
||||
Reference in New Issue
Block a user