fraiseql-confiture 0.3.4__cp311-cp311-win_amd64.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.
- confiture/__init__.py +48 -0
- confiture/_core.cp311-win_amd64.pyd +0 -0
- confiture/cli/__init__.py +0 -0
- confiture/cli/dry_run.py +116 -0
- confiture/cli/lint_formatter.py +193 -0
- confiture/cli/main.py +1656 -0
- confiture/config/__init__.py +0 -0
- confiture/config/environment.py +263 -0
- confiture/core/__init__.py +51 -0
- confiture/core/anonymization/__init__.py +0 -0
- confiture/core/anonymization/audit.py +485 -0
- confiture/core/anonymization/benchmarking.py +372 -0
- confiture/core/anonymization/breach_notification.py +652 -0
- confiture/core/anonymization/compliance.py +617 -0
- confiture/core/anonymization/composer.py +298 -0
- confiture/core/anonymization/data_subject_rights.py +669 -0
- confiture/core/anonymization/factory.py +319 -0
- confiture/core/anonymization/governance.py +737 -0
- confiture/core/anonymization/performance.py +1092 -0
- confiture/core/anonymization/profile.py +284 -0
- confiture/core/anonymization/registry.py +195 -0
- confiture/core/anonymization/security/kms_manager.py +547 -0
- confiture/core/anonymization/security/lineage.py +888 -0
- confiture/core/anonymization/security/token_store.py +686 -0
- confiture/core/anonymization/strategies/__init__.py +41 -0
- confiture/core/anonymization/strategies/address.py +359 -0
- confiture/core/anonymization/strategies/credit_card.py +374 -0
- confiture/core/anonymization/strategies/custom.py +161 -0
- confiture/core/anonymization/strategies/date.py +218 -0
- confiture/core/anonymization/strategies/differential_privacy.py +398 -0
- confiture/core/anonymization/strategies/email.py +141 -0
- confiture/core/anonymization/strategies/format_preserving_encryption.py +310 -0
- confiture/core/anonymization/strategies/hash.py +150 -0
- confiture/core/anonymization/strategies/ip_address.py +235 -0
- confiture/core/anonymization/strategies/masking_retention.py +252 -0
- confiture/core/anonymization/strategies/name.py +298 -0
- confiture/core/anonymization/strategies/phone.py +119 -0
- confiture/core/anonymization/strategies/preserve.py +85 -0
- confiture/core/anonymization/strategies/redact.py +101 -0
- confiture/core/anonymization/strategies/salted_hashing.py +322 -0
- confiture/core/anonymization/strategies/text_redaction.py +183 -0
- confiture/core/anonymization/strategies/tokenization.py +334 -0
- confiture/core/anonymization/strategy.py +241 -0
- confiture/core/anonymization/syncer_audit.py +357 -0
- confiture/core/blue_green.py +683 -0
- confiture/core/builder.py +500 -0
- confiture/core/checksum.py +358 -0
- confiture/core/connection.py +132 -0
- confiture/core/differ.py +522 -0
- confiture/core/drift.py +564 -0
- confiture/core/dry_run.py +182 -0
- confiture/core/health.py +313 -0
- confiture/core/hooks/__init__.py +87 -0
- confiture/core/hooks/base.py +232 -0
- confiture/core/hooks/context.py +146 -0
- confiture/core/hooks/execution_strategies.py +57 -0
- confiture/core/hooks/observability.py +220 -0
- confiture/core/hooks/phases.py +53 -0
- confiture/core/hooks/registry.py +295 -0
- confiture/core/large_tables.py +775 -0
- confiture/core/linting/__init__.py +70 -0
- confiture/core/linting/composer.py +192 -0
- confiture/core/linting/libraries/__init__.py +17 -0
- confiture/core/linting/libraries/gdpr.py +168 -0
- confiture/core/linting/libraries/general.py +184 -0
- confiture/core/linting/libraries/hipaa.py +144 -0
- confiture/core/linting/libraries/pci_dss.py +104 -0
- confiture/core/linting/libraries/sox.py +120 -0
- confiture/core/linting/schema_linter.py +491 -0
- confiture/core/linting/versioning.py +151 -0
- confiture/core/locking.py +389 -0
- confiture/core/migration_generator.py +298 -0
- confiture/core/migrator.py +793 -0
- confiture/core/observability/__init__.py +44 -0
- confiture/core/observability/audit.py +323 -0
- confiture/core/observability/logging.py +187 -0
- confiture/core/observability/metrics.py +174 -0
- confiture/core/observability/tracing.py +192 -0
- confiture/core/pg_version.py +418 -0
- confiture/core/pool.py +406 -0
- confiture/core/risk/__init__.py +39 -0
- confiture/core/risk/predictor.py +188 -0
- confiture/core/risk/scoring.py +248 -0
- confiture/core/rollback_generator.py +388 -0
- confiture/core/schema_analyzer.py +769 -0
- confiture/core/schema_to_schema.py +590 -0
- confiture/core/security/__init__.py +32 -0
- confiture/core/security/logging.py +201 -0
- confiture/core/security/validation.py +416 -0
- confiture/core/signals.py +371 -0
- confiture/core/syncer.py +540 -0
- confiture/exceptions.py +192 -0
- confiture/integrations/__init__.py +0 -0
- confiture/models/__init__.py +0 -0
- confiture/models/lint.py +193 -0
- confiture/models/migration.py +180 -0
- confiture/models/schema.py +203 -0
- confiture/scenarios/__init__.py +36 -0
- confiture/scenarios/compliance.py +586 -0
- confiture/scenarios/ecommerce.py +199 -0
- confiture/scenarios/financial.py +253 -0
- confiture/scenarios/healthcare.py +315 -0
- confiture/scenarios/multi_tenant.py +340 -0
- confiture/scenarios/saas.py +295 -0
- confiture/testing/FRAMEWORK_API.md +722 -0
- confiture/testing/__init__.py +38 -0
- confiture/testing/fixtures/__init__.py +11 -0
- confiture/testing/fixtures/data_validator.py +229 -0
- confiture/testing/fixtures/migration_runner.py +167 -0
- confiture/testing/fixtures/schema_snapshotter.py +352 -0
- confiture/testing/frameworks/__init__.py +10 -0
- confiture/testing/frameworks/mutation.py +587 -0
- confiture/testing/frameworks/performance.py +479 -0
- confiture/testing/utils/__init__.py +0 -0
- fraiseql_confiture-0.3.4.dist-info/METADATA +438 -0
- fraiseql_confiture-0.3.4.dist-info/RECORD +119 -0
- fraiseql_confiture-0.3.4.dist-info/WHEEL +4 -0
- fraiseql_confiture-0.3.4.dist-info/entry_points.txt +2 -0
- fraiseql_confiture-0.3.4.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,617 @@
|
|
|
1
|
+
"""Compliance automation and reporting.
|
|
2
|
+
|
|
3
|
+
Provides compliance reporting for 7 major regulations:
|
|
4
|
+
- GDPR (General Data Protection Regulation - EU)
|
|
5
|
+
- CCPA (California Consumer Privacy Act - USA)
|
|
6
|
+
- PIPEDA (Personal Information Protection and Electronic Documents Act - Canada)
|
|
7
|
+
- LGPD (Lei Geral de Proteção de Dados - Brazil)
|
|
8
|
+
- PIPL (Personal Information Protection Law - China)
|
|
9
|
+
- Privacy Act (Australia)
|
|
10
|
+
- POPIA (Protection of Personal Information Act - South Africa)
|
|
11
|
+
|
|
12
|
+
Features:
|
|
13
|
+
- Regulation-specific requirement tracking
|
|
14
|
+
- Compliance matrix across all regulations
|
|
15
|
+
- Automated audit trail generation
|
|
16
|
+
- Data lineage integration
|
|
17
|
+
- Breach notification support
|
|
18
|
+
- Data subject rights automation (access, deletion, portability)
|
|
19
|
+
|
|
20
|
+
Example:
|
|
21
|
+
>>> from confiture.core.anonymization.compliance import (
|
|
22
|
+
... ComplianceReportGenerator, Regulation
|
|
23
|
+
... )
|
|
24
|
+
>>> from confiture.core.anonymization.security.lineage import DataLineageTracker
|
|
25
|
+
>>>
|
|
26
|
+
>>> generator = ComplianceReportGenerator(lineage_tracker)
|
|
27
|
+
>>> report = generator.generate_report(
|
|
28
|
+
... regulations=[Regulation.GDPR, Regulation.CCPA],
|
|
29
|
+
... time_period=("2024-01-01", "2024-12-31")
|
|
30
|
+
... )
|
|
31
|
+
>>>
|
|
32
|
+
>>> print(f"GDPR Compliance: {report.coverage_percentage(Regulation.GDPR):.1f}%")
|
|
33
|
+
>>> print(f"Recommendations: {len(report.recommendations)}")
|
|
34
|
+
"""
|
|
35
|
+
|
|
36
|
+
import json
|
|
37
|
+
import logging
|
|
38
|
+
from dataclasses import dataclass, field
|
|
39
|
+
from datetime import datetime
|
|
40
|
+
from enum import Enum
|
|
41
|
+
from typing import Any
|
|
42
|
+
|
|
43
|
+
import psycopg
|
|
44
|
+
|
|
45
|
+
from confiture.core.anonymization.security.lineage import (
|
|
46
|
+
DataLineageEntry,
|
|
47
|
+
DataLineageTracker,
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
logger = logging.getLogger(__name__)
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
class Regulation(Enum):
|
|
54
|
+
"""Supported data protection regulations."""
|
|
55
|
+
|
|
56
|
+
GDPR = "gdpr"
|
|
57
|
+
"""General Data Protection Regulation (EU)."""
|
|
58
|
+
|
|
59
|
+
CCPA = "ccpa"
|
|
60
|
+
"""California Consumer Privacy Act (USA)."""
|
|
61
|
+
|
|
62
|
+
PIPEDA = "pipeda"
|
|
63
|
+
"""Personal Information Protection and Electronic Documents Act (Canada)."""
|
|
64
|
+
|
|
65
|
+
LGPD = "lgpd"
|
|
66
|
+
"""Lei Geral de Proteção de Dados (Brazil)."""
|
|
67
|
+
|
|
68
|
+
PIPL = "pipl"
|
|
69
|
+
"""Personal Information Protection Law (China)."""
|
|
70
|
+
|
|
71
|
+
PRIVACY_ACT = "privacy_act"
|
|
72
|
+
"""Privacy Act (Australia)."""
|
|
73
|
+
|
|
74
|
+
POPIA = "popia"
|
|
75
|
+
"""Protection of Personal Information Act (South Africa)."""
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
@dataclass
|
|
79
|
+
class ComplianceRequirement:
|
|
80
|
+
"""Single compliance requirement."""
|
|
81
|
+
|
|
82
|
+
regulation: Regulation
|
|
83
|
+
"""Which regulation this applies to."""
|
|
84
|
+
|
|
85
|
+
requirement_id: str
|
|
86
|
+
"""Unique identifier for requirement (e.g., 'GDPR-32')."""
|
|
87
|
+
|
|
88
|
+
description: str
|
|
89
|
+
"""Human-readable requirement description."""
|
|
90
|
+
|
|
91
|
+
article: str
|
|
92
|
+
"""Article/section reference (e.g., 'Article 32')."""
|
|
93
|
+
|
|
94
|
+
requirement: str
|
|
95
|
+
"""Detailed requirement text."""
|
|
96
|
+
|
|
97
|
+
is_met: bool = False
|
|
98
|
+
"""Whether requirement is currently met."""
|
|
99
|
+
|
|
100
|
+
evidence: str | None = None
|
|
101
|
+
"""Evidence that requirement is met (e.g., logs, configurations)."""
|
|
102
|
+
|
|
103
|
+
remediation: str | None = None
|
|
104
|
+
"""How to remediate if not met."""
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
@dataclass
|
|
108
|
+
class ComplianceReport:
|
|
109
|
+
"""Complete compliance report across regulations."""
|
|
110
|
+
|
|
111
|
+
generated_at: datetime
|
|
112
|
+
"""When report was generated."""
|
|
113
|
+
|
|
114
|
+
time_period_start: str | None = None
|
|
115
|
+
"""Start of reporting period."""
|
|
116
|
+
|
|
117
|
+
time_period_end: str | None = None
|
|
118
|
+
"""End of reporting period."""
|
|
119
|
+
|
|
120
|
+
regulations: list[Regulation] = field(default_factory=list)
|
|
121
|
+
"""Regulations included in report."""
|
|
122
|
+
|
|
123
|
+
requirements: dict[Regulation, list[ComplianceRequirement]] = field(default_factory=dict)
|
|
124
|
+
"""Requirements by regulation."""
|
|
125
|
+
|
|
126
|
+
recommendations: list[str] = field(default_factory=list)
|
|
127
|
+
"""Remediation recommendations."""
|
|
128
|
+
|
|
129
|
+
audit_trail: list[dict[str, Any]] = field(default_factory=list)
|
|
130
|
+
"""Audit trail entries."""
|
|
131
|
+
|
|
132
|
+
data_lineage: list[DataLineageEntry] = field(default_factory=list)
|
|
133
|
+
"""Data lineage entries for anonymization operations."""
|
|
134
|
+
|
|
135
|
+
def coverage_percentage(self, regulation: Regulation) -> float:
|
|
136
|
+
"""Calculate compliance coverage percentage for a regulation.
|
|
137
|
+
|
|
138
|
+
Args:
|
|
139
|
+
regulation: Regulation to calculate coverage for
|
|
140
|
+
|
|
141
|
+
Returns:
|
|
142
|
+
Coverage percentage (0-100)
|
|
143
|
+
"""
|
|
144
|
+
reqs = self.requirements.get(regulation, [])
|
|
145
|
+
if not reqs:
|
|
146
|
+
return 0.0
|
|
147
|
+
|
|
148
|
+
met = sum(1 for req in reqs if req.is_met)
|
|
149
|
+
return 100.0 * met / len(reqs)
|
|
150
|
+
|
|
151
|
+
def total_coverage_percentage(self) -> float:
|
|
152
|
+
"""Calculate total compliance coverage across all regulations.
|
|
153
|
+
|
|
154
|
+
Returns:
|
|
155
|
+
Total coverage percentage (0-100)
|
|
156
|
+
"""
|
|
157
|
+
all_reqs = []
|
|
158
|
+
for reqs in self.requirements.values():
|
|
159
|
+
all_reqs.extend(reqs)
|
|
160
|
+
|
|
161
|
+
if not all_reqs:
|
|
162
|
+
return 0.0
|
|
163
|
+
|
|
164
|
+
met = sum(1 for req in all_reqs if req.is_met)
|
|
165
|
+
return 100.0 * met / len(all_reqs)
|
|
166
|
+
|
|
167
|
+
def to_json(self) -> str:
|
|
168
|
+
"""Serialize report to JSON.
|
|
169
|
+
|
|
170
|
+
Returns:
|
|
171
|
+
JSON representation of report
|
|
172
|
+
"""
|
|
173
|
+
data = {
|
|
174
|
+
"generated_at": self.generated_at.isoformat(),
|
|
175
|
+
"time_period": {
|
|
176
|
+
"start": self.time_period_start,
|
|
177
|
+
"end": self.time_period_end,
|
|
178
|
+
},
|
|
179
|
+
"regulations": [r.value for r in self.regulations],
|
|
180
|
+
"total_coverage": self.total_coverage_percentage(),
|
|
181
|
+
"coverage_by_regulation": {
|
|
182
|
+
r.value: self.coverage_percentage(r) for r in self.regulations
|
|
183
|
+
},
|
|
184
|
+
"recommendations": self.recommendations,
|
|
185
|
+
"audit_trail_entries": len(self.audit_trail),
|
|
186
|
+
"lineage_entries": len(self.data_lineage),
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
return json.dumps(data, indent=2)
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
class ComplianceReportGenerator:
|
|
193
|
+
"""Generate compliance reports for anonymization operations.
|
|
194
|
+
|
|
195
|
+
Tracks compliance with 7 major data protection regulations:
|
|
196
|
+
- GDPR (EU)
|
|
197
|
+
- CCPA (USA/California)
|
|
198
|
+
- PIPEDA (Canada)
|
|
199
|
+
- LGPD (Brazil)
|
|
200
|
+
- PIPL (China)
|
|
201
|
+
- Privacy Act (Australia)
|
|
202
|
+
- POPIA (South Africa)
|
|
203
|
+
|
|
204
|
+
Features:
|
|
205
|
+
- Requirement tracking per regulation
|
|
206
|
+
- Automated evidence collection
|
|
207
|
+
- Coverage calculation
|
|
208
|
+
- Remediation recommendations
|
|
209
|
+
- Audit trail integration
|
|
210
|
+
- Data lineage integration
|
|
211
|
+
"""
|
|
212
|
+
|
|
213
|
+
def __init__(
|
|
214
|
+
self,
|
|
215
|
+
lineage_tracker: DataLineageTracker,
|
|
216
|
+
conn: psycopg.Connection | None = None,
|
|
217
|
+
):
|
|
218
|
+
"""Initialize compliance report generator.
|
|
219
|
+
|
|
220
|
+
Args:
|
|
221
|
+
lineage_tracker: Data lineage tracker for operation history
|
|
222
|
+
conn: Database connection for audit trail queries
|
|
223
|
+
"""
|
|
224
|
+
self.lineage_tracker = lineage_tracker
|
|
225
|
+
self.conn = conn
|
|
226
|
+
self._requirements_map = self._build_requirements_map()
|
|
227
|
+
|
|
228
|
+
def _build_requirements_map(
|
|
229
|
+
self,
|
|
230
|
+
) -> dict[Regulation, list[ComplianceRequirement]]:
|
|
231
|
+
"""Build map of all compliance requirements by regulation.
|
|
232
|
+
|
|
233
|
+
Returns:
|
|
234
|
+
Dictionary of requirements by regulation
|
|
235
|
+
"""
|
|
236
|
+
requirements = {
|
|
237
|
+
Regulation.GDPR: self._gdpr_requirements(),
|
|
238
|
+
Regulation.CCPA: self._ccpa_requirements(),
|
|
239
|
+
Regulation.PIPEDA: self._pipeda_requirements(),
|
|
240
|
+
Regulation.LGPD: self._lgpd_requirements(),
|
|
241
|
+
Regulation.PIPL: self._pipl_requirements(),
|
|
242
|
+
Regulation.PRIVACY_ACT: self._privacy_act_requirements(),
|
|
243
|
+
Regulation.POPIA: self._popia_requirements(),
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
return requirements
|
|
247
|
+
|
|
248
|
+
def generate_report(
|
|
249
|
+
self,
|
|
250
|
+
regulations: list[Regulation] | None = None,
|
|
251
|
+
time_period: tuple[str, str] | None = None,
|
|
252
|
+
) -> ComplianceReport:
|
|
253
|
+
"""Generate compliance report for specified regulations and period.
|
|
254
|
+
|
|
255
|
+
Args:
|
|
256
|
+
regulations: Regulations to report on (None = all)
|
|
257
|
+
time_period: Tuple of (start_date, end_date) in ISO format
|
|
258
|
+
|
|
259
|
+
Returns:
|
|
260
|
+
ComplianceReport with coverage and recommendations
|
|
261
|
+
"""
|
|
262
|
+
if regulations is None:
|
|
263
|
+
regulations = list(Regulation)
|
|
264
|
+
|
|
265
|
+
report = ComplianceReport(
|
|
266
|
+
generated_at=datetime.now(),
|
|
267
|
+
time_period_start=time_period[0] if time_period else None,
|
|
268
|
+
time_period_end=time_period[1] if time_period else None,
|
|
269
|
+
regulations=regulations,
|
|
270
|
+
)
|
|
271
|
+
|
|
272
|
+
# Build requirements for requested regulations
|
|
273
|
+
for regulation in regulations:
|
|
274
|
+
reqs = self._requirements_map.get(regulation, [])
|
|
275
|
+
report.requirements[regulation] = [
|
|
276
|
+
self._evaluate_requirement(req, time_period) for req in reqs
|
|
277
|
+
]
|
|
278
|
+
|
|
279
|
+
# Collect audit trail
|
|
280
|
+
if self.conn:
|
|
281
|
+
report.audit_trail = self._collect_audit_trail(time_period)
|
|
282
|
+
|
|
283
|
+
# Collect data lineage
|
|
284
|
+
report.data_lineage = self._collect_data_lineage(time_period)
|
|
285
|
+
|
|
286
|
+
# Generate recommendations
|
|
287
|
+
report.recommendations = self._generate_recommendations(report)
|
|
288
|
+
|
|
289
|
+
logger.info(
|
|
290
|
+
f"Generated compliance report: {report.total_coverage_percentage():.1f}% coverage"
|
|
291
|
+
)
|
|
292
|
+
|
|
293
|
+
return report
|
|
294
|
+
|
|
295
|
+
def _evaluate_requirement(
|
|
296
|
+
self,
|
|
297
|
+
requirement: ComplianceRequirement,
|
|
298
|
+
_time_period: tuple[str, str] | None = None,
|
|
299
|
+
) -> ComplianceRequirement:
|
|
300
|
+
"""Evaluate whether a requirement is met.
|
|
301
|
+
|
|
302
|
+
Args:
|
|
303
|
+
requirement: Requirement to evaluate
|
|
304
|
+
time_period: Time period for evaluation
|
|
305
|
+
|
|
306
|
+
Returns:
|
|
307
|
+
ComplianceRequirement with is_met and evidence updated
|
|
308
|
+
"""
|
|
309
|
+
# In a real implementation, would:
|
|
310
|
+
# 1. Check configuration files
|
|
311
|
+
# 2. Query database for evidence
|
|
312
|
+
# 3. Verify implementations
|
|
313
|
+
# 4. Review audit logs
|
|
314
|
+
|
|
315
|
+
# For now, return requirement as-is
|
|
316
|
+
return requirement
|
|
317
|
+
|
|
318
|
+
def _collect_audit_trail(
|
|
319
|
+
self, _time_period: tuple[str, str] | None = None
|
|
320
|
+
) -> list[dict[str, Any]]:
|
|
321
|
+
"""Collect audit trail from database.
|
|
322
|
+
|
|
323
|
+
Args:
|
|
324
|
+
time_period: Time period to collect for
|
|
325
|
+
|
|
326
|
+
Returns:
|
|
327
|
+
List of audit trail entries
|
|
328
|
+
"""
|
|
329
|
+
if not self.conn:
|
|
330
|
+
return []
|
|
331
|
+
|
|
332
|
+
# In a real implementation, would query confiture_audit_log
|
|
333
|
+
return []
|
|
334
|
+
|
|
335
|
+
def _collect_data_lineage(
|
|
336
|
+
self, _time_period: tuple[str, str] | None = None
|
|
337
|
+
) -> list[DataLineageEntry]:
|
|
338
|
+
"""Collect data lineage for reporting period.
|
|
339
|
+
|
|
340
|
+
Args:
|
|
341
|
+
time_period: Time period to collect for
|
|
342
|
+
|
|
343
|
+
Returns:
|
|
344
|
+
List of lineage entries
|
|
345
|
+
"""
|
|
346
|
+
# Get all lineage entries (filter by time period if provided)
|
|
347
|
+
lineage = []
|
|
348
|
+
|
|
349
|
+
# In a real implementation, would query confiture_data_lineage
|
|
350
|
+
# and filter by time_period[0] and time_period[1]
|
|
351
|
+
|
|
352
|
+
return lineage
|
|
353
|
+
|
|
354
|
+
def _generate_recommendations(self, report: ComplianceReport) -> list[str]:
|
|
355
|
+
"""Generate remediation recommendations based on findings.
|
|
356
|
+
|
|
357
|
+
Args:
|
|
358
|
+
report: Compliance report with findings
|
|
359
|
+
|
|
360
|
+
Returns:
|
|
361
|
+
List of recommendations
|
|
362
|
+
"""
|
|
363
|
+
recommendations = []
|
|
364
|
+
|
|
365
|
+
for regulation, reqs in report.requirements.items():
|
|
366
|
+
unmet = [req for req in reqs if not req.is_met]
|
|
367
|
+
if unmet:
|
|
368
|
+
recommendations.append(
|
|
369
|
+
f"{regulation.value.upper()}: {len(unmet)} requirements not met"
|
|
370
|
+
)
|
|
371
|
+
for req in unmet[:3]: # Top 3 recommendations
|
|
372
|
+
if req.remediation:
|
|
373
|
+
recommendations.append(f" - {req.remediation}")
|
|
374
|
+
|
|
375
|
+
return recommendations
|
|
376
|
+
|
|
377
|
+
# --- Regulation-Specific Requirements ---
|
|
378
|
+
|
|
379
|
+
def _gdpr_requirements(self) -> list[ComplianceRequirement]:
|
|
380
|
+
"""GDPR (General Data Protection Regulation) requirements.
|
|
381
|
+
|
|
382
|
+
Article 32: Security of processing
|
|
383
|
+
Article 33: Notification of breach
|
|
384
|
+
Article 30: Records of processing activities
|
|
385
|
+
"""
|
|
386
|
+
return [
|
|
387
|
+
ComplianceRequirement(
|
|
388
|
+
regulation=Regulation.GDPR,
|
|
389
|
+
requirement_id="GDPR-32",
|
|
390
|
+
description="Implement appropriate technical and organizational measures",
|
|
391
|
+
article="Article 32",
|
|
392
|
+
requirement="Appropriate security measures including pseudonymization and encryption",
|
|
393
|
+
remediation="Implement KMS and encryption for sensitive data",
|
|
394
|
+
),
|
|
395
|
+
ComplianceRequirement(
|
|
396
|
+
regulation=Regulation.GDPR,
|
|
397
|
+
requirement_id="GDPR-33",
|
|
398
|
+
description="Notify supervisory authority of breach without undue delay",
|
|
399
|
+
article="Article 33",
|
|
400
|
+
requirement="Notify GDPR authority within 72 hours of breach discovery",
|
|
401
|
+
remediation="Implement breach notification system with alerts",
|
|
402
|
+
),
|
|
403
|
+
ComplianceRequirement(
|
|
404
|
+
regulation=Regulation.GDPR,
|
|
405
|
+
requirement_id="GDPR-30",
|
|
406
|
+
description="Maintain records of processing activities",
|
|
407
|
+
article="Article 30",
|
|
408
|
+
requirement="Detailed records of all data processing (what, why, who, how)",
|
|
409
|
+
remediation="Use data lineage tracking for complete audit trail",
|
|
410
|
+
),
|
|
411
|
+
]
|
|
412
|
+
|
|
413
|
+
def _ccpa_requirements(self) -> list[ComplianceRequirement]:
|
|
414
|
+
"""CCPA (California Consumer Privacy Act) requirements."""
|
|
415
|
+
return [
|
|
416
|
+
ComplianceRequirement(
|
|
417
|
+
regulation=Regulation.CCPA,
|
|
418
|
+
requirement_id="CCPA-1798.100",
|
|
419
|
+
description="Right to know what personal information is collected",
|
|
420
|
+
article="§ 1798.100",
|
|
421
|
+
requirement="Provide transparency about PII collection",
|
|
422
|
+
remediation="Maintain data inventory with collection sources",
|
|
423
|
+
),
|
|
424
|
+
ComplianceRequirement(
|
|
425
|
+
regulation=Regulation.CCPA,
|
|
426
|
+
requirement_id="CCPA-1798.105",
|
|
427
|
+
description="Right to delete personal information",
|
|
428
|
+
article="§ 1798.105",
|
|
429
|
+
requirement="Delete PII upon consumer request within 45 days",
|
|
430
|
+
remediation="Implement secure deletion with audit trail",
|
|
431
|
+
),
|
|
432
|
+
ComplianceRequirement(
|
|
433
|
+
regulation=Regulation.CCPA,
|
|
434
|
+
requirement_id="CCPA-1798.150",
|
|
435
|
+
description="Implement and maintain reasonable security procedures",
|
|
436
|
+
article="§ 1798.150",
|
|
437
|
+
requirement="Protect PII from unauthorized access",
|
|
438
|
+
remediation="Encrypt sensitive data at rest and in transit",
|
|
439
|
+
),
|
|
440
|
+
]
|
|
441
|
+
|
|
442
|
+
def _pipeda_requirements(self) -> list[ComplianceRequirement]:
|
|
443
|
+
"""PIPEDA (Personal Information Protection and Electronic Documents Act) requirements."""
|
|
444
|
+
return [
|
|
445
|
+
ComplianceRequirement(
|
|
446
|
+
regulation=Regulation.PIPEDA,
|
|
447
|
+
requirement_id="PIPEDA-4.7",
|
|
448
|
+
description="Safeguards for personal information",
|
|
449
|
+
article="Principle 4.7",
|
|
450
|
+
requirement="Appropriate security measures for PII",
|
|
451
|
+
remediation="Implement encryption and access controls",
|
|
452
|
+
),
|
|
453
|
+
ComplianceRequirement(
|
|
454
|
+
regulation=Regulation.PIPEDA,
|
|
455
|
+
requirement_id="PIPEDA-4.9",
|
|
456
|
+
description="Notify individuals of information security breaches",
|
|
457
|
+
article="Principle 4.9",
|
|
458
|
+
requirement="Notify individuals if PII is compromised",
|
|
459
|
+
remediation="Implement breach notification procedures",
|
|
460
|
+
),
|
|
461
|
+
]
|
|
462
|
+
|
|
463
|
+
def _lgpd_requirements(self) -> list[ComplianceRequirement]:
|
|
464
|
+
"""LGPD (Lei Geral de Proteção de Dados) requirements (Brazil)."""
|
|
465
|
+
return [
|
|
466
|
+
ComplianceRequirement(
|
|
467
|
+
regulation=Regulation.LGPD,
|
|
468
|
+
requirement_id="LGPD-Article-9",
|
|
469
|
+
description="Implement security measures",
|
|
470
|
+
article="Article 9",
|
|
471
|
+
requirement="Technical and administrative measures to protect PII",
|
|
472
|
+
remediation="Encrypt data and implement access controls",
|
|
473
|
+
),
|
|
474
|
+
ComplianceRequirement(
|
|
475
|
+
regulation=Regulation.LGPD,
|
|
476
|
+
requirement_id="LGPD-Article-22",
|
|
477
|
+
description="Subject rights (access, correction, deletion)",
|
|
478
|
+
article="Article 22",
|
|
479
|
+
requirement="Allow subjects to exercise rights over their data",
|
|
480
|
+
remediation="Implement data subject rights portal",
|
|
481
|
+
),
|
|
482
|
+
]
|
|
483
|
+
|
|
484
|
+
def _pipl_requirements(self) -> list[ComplianceRequirement]:
|
|
485
|
+
"""PIPL (Personal Information Protection Law) requirements (China)."""
|
|
486
|
+
return [
|
|
487
|
+
ComplianceRequirement(
|
|
488
|
+
regulation=Regulation.PIPL,
|
|
489
|
+
requirement_id="PIPL-Article-7",
|
|
490
|
+
description="Data collection principles",
|
|
491
|
+
article="Article 7",
|
|
492
|
+
requirement="Collect only necessary data with consent",
|
|
493
|
+
remediation="Document data minimization practices",
|
|
494
|
+
),
|
|
495
|
+
ComplianceRequirement(
|
|
496
|
+
regulation=Regulation.PIPL,
|
|
497
|
+
requirement_id="PIPL-Article-27",
|
|
498
|
+
description="Implement data security measures",
|
|
499
|
+
article="Article 27",
|
|
500
|
+
requirement="Encryption and access controls for sensitive data",
|
|
501
|
+
remediation="Implement KMS and RBAC",
|
|
502
|
+
),
|
|
503
|
+
]
|
|
504
|
+
|
|
505
|
+
def _privacy_act_requirements(self) -> list[ComplianceRequirement]:
|
|
506
|
+
"""Privacy Act requirements (Australia)."""
|
|
507
|
+
return [
|
|
508
|
+
ComplianceRequirement(
|
|
509
|
+
regulation=Regulation.PRIVACY_ACT,
|
|
510
|
+
requirement_id="Privacy-Act-1.1",
|
|
511
|
+
description="Australian Privacy Principles (APPs)",
|
|
512
|
+
article="Part 1",
|
|
513
|
+
requirement="Manage personal information responsibly",
|
|
514
|
+
remediation="Implement privacy management framework",
|
|
515
|
+
),
|
|
516
|
+
ComplianceRequirement(
|
|
517
|
+
regulation=Regulation.PRIVACY_ACT,
|
|
518
|
+
requirement_id="Privacy-Act-13",
|
|
519
|
+
description="Secure personal information",
|
|
520
|
+
article="APP 13",
|
|
521
|
+
requirement="Take reasonable steps to protect PII from misuse",
|
|
522
|
+
remediation="Implement security measures and monitoring",
|
|
523
|
+
),
|
|
524
|
+
]
|
|
525
|
+
|
|
526
|
+
def _popia_requirements(self) -> list[ComplianceRequirement]:
|
|
527
|
+
"""POPIA (Protection of Personal Information Act) requirements (South Africa)."""
|
|
528
|
+
return [
|
|
529
|
+
ComplianceRequirement(
|
|
530
|
+
regulation=Regulation.POPIA,
|
|
531
|
+
requirement_id="POPIA-10",
|
|
532
|
+
description="Conditions for lawful processing",
|
|
533
|
+
article="Section 10",
|
|
534
|
+
requirement="Process data according to lawful basis",
|
|
535
|
+
remediation="Document legal basis for processing",
|
|
536
|
+
),
|
|
537
|
+
ComplianceRequirement(
|
|
538
|
+
regulation=Regulation.POPIA,
|
|
539
|
+
requirement_id="POPIA-19",
|
|
540
|
+
description="Data security (Technical and organizational measures)",
|
|
541
|
+
article="Section 19",
|
|
542
|
+
requirement="Implement appropriate security measures",
|
|
543
|
+
remediation="Deploy encryption and access controls",
|
|
544
|
+
),
|
|
545
|
+
]
|
|
546
|
+
|
|
547
|
+
|
|
548
|
+
class CrossRegulationComplianceMatrix:
|
|
549
|
+
"""Matrix showing compliance overlap across regulations.
|
|
550
|
+
|
|
551
|
+
Shows which requirements apply to multiple regulations,
|
|
552
|
+
enabling efficient compliance management.
|
|
553
|
+
"""
|
|
554
|
+
|
|
555
|
+
@staticmethod
|
|
556
|
+
def build_matrix(
|
|
557
|
+
requirements: dict[Regulation, list[ComplianceRequirement]],
|
|
558
|
+
) -> dict[str, dict[str, bool]]:
|
|
559
|
+
"""Build matrix showing which regulations share requirements.
|
|
560
|
+
|
|
561
|
+
Args:
|
|
562
|
+
requirements: Requirements by regulation
|
|
563
|
+
|
|
564
|
+
Returns:
|
|
565
|
+
Matrix: dict[requirement_description][regulation] = bool
|
|
566
|
+
"""
|
|
567
|
+
matrix = {}
|
|
568
|
+
|
|
569
|
+
# Common requirement themes across regulations
|
|
570
|
+
themes = {
|
|
571
|
+
"Data Security": ["Encryption", "Access Control", "KMS"],
|
|
572
|
+
"Breach Notification": ["Notify", "Alert", "Incident"],
|
|
573
|
+
"Data Subject Rights": ["Access", "Deletion", "Portability"],
|
|
574
|
+
"Data Minimization": ["Minimize", "Necessary", "Purpose"],
|
|
575
|
+
"Audit Trail": ["Records", "Lineage", "Logging"],
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
for theme, keywords in themes.items():
|
|
579
|
+
matrix[theme] = {}
|
|
580
|
+
for regulation in Regulation:
|
|
581
|
+
reqs = requirements.get(regulation, [])
|
|
582
|
+
# Check if any requirement matches theme
|
|
583
|
+
has_theme = any(
|
|
584
|
+
any(kw.lower() in req.description.lower() for kw in keywords) for req in reqs
|
|
585
|
+
)
|
|
586
|
+
matrix[theme][regulation.value] = has_theme
|
|
587
|
+
|
|
588
|
+
return matrix
|
|
589
|
+
|
|
590
|
+
@staticmethod
|
|
591
|
+
def print_matrix(matrix: dict[str, dict[str, bool]]) -> str:
|
|
592
|
+
"""Pretty-print compliance matrix.
|
|
593
|
+
|
|
594
|
+
Args:
|
|
595
|
+
matrix: Compliance matrix
|
|
596
|
+
|
|
597
|
+
Returns:
|
|
598
|
+
Formatted string for display
|
|
599
|
+
"""
|
|
600
|
+
regulations = [r.value.upper() for r in Regulation]
|
|
601
|
+
output = []
|
|
602
|
+
|
|
603
|
+
# Header
|
|
604
|
+
output.append("Compliance Requirement Matrix")
|
|
605
|
+
output.append("-" * (20 + len(regulations) * 8))
|
|
606
|
+
output.append(f"{'Requirement':<20} " + " ".join(f"{r:>7}" for r in regulations))
|
|
607
|
+
output.append("-" * (20 + len(regulations) * 8))
|
|
608
|
+
|
|
609
|
+
# Rows
|
|
610
|
+
for requirement, reg_coverage in sorted(matrix.items()):
|
|
611
|
+
row = f"{requirement:<20} "
|
|
612
|
+
for regulation in [r.value for r in Regulation]:
|
|
613
|
+
status = "✓" if reg_coverage.get(regulation, False) else " "
|
|
614
|
+
row += f"{status:>7} "
|
|
615
|
+
output.append(row)
|
|
616
|
+
|
|
617
|
+
return "\n".join(output)
|