tweek 0.1.0__py3-none-any.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.
Files changed (85) hide show
  1. tweek/__init__.py +16 -0
  2. tweek/cli.py +3390 -0
  3. tweek/cli_helpers.py +193 -0
  4. tweek/config/__init__.py +13 -0
  5. tweek/config/allowed_dirs.yaml +23 -0
  6. tweek/config/manager.py +1064 -0
  7. tweek/config/patterns.yaml +751 -0
  8. tweek/config/tiers.yaml +129 -0
  9. tweek/diagnostics.py +589 -0
  10. tweek/hooks/__init__.py +1 -0
  11. tweek/hooks/pre_tool_use.py +861 -0
  12. tweek/integrations/__init__.py +3 -0
  13. tweek/integrations/moltbot.py +243 -0
  14. tweek/licensing.py +398 -0
  15. tweek/logging/__init__.py +9 -0
  16. tweek/logging/bundle.py +350 -0
  17. tweek/logging/json_logger.py +150 -0
  18. tweek/logging/security_log.py +745 -0
  19. tweek/mcp/__init__.py +24 -0
  20. tweek/mcp/approval.py +456 -0
  21. tweek/mcp/approval_cli.py +356 -0
  22. tweek/mcp/clients/__init__.py +37 -0
  23. tweek/mcp/clients/chatgpt.py +112 -0
  24. tweek/mcp/clients/claude_desktop.py +203 -0
  25. tweek/mcp/clients/gemini.py +178 -0
  26. tweek/mcp/proxy.py +667 -0
  27. tweek/mcp/screening.py +175 -0
  28. tweek/mcp/server.py +317 -0
  29. tweek/platform/__init__.py +131 -0
  30. tweek/plugins/__init__.py +835 -0
  31. tweek/plugins/base.py +1080 -0
  32. tweek/plugins/compliance/__init__.py +30 -0
  33. tweek/plugins/compliance/gdpr.py +333 -0
  34. tweek/plugins/compliance/gov.py +324 -0
  35. tweek/plugins/compliance/hipaa.py +285 -0
  36. tweek/plugins/compliance/legal.py +322 -0
  37. tweek/plugins/compliance/pci.py +361 -0
  38. tweek/plugins/compliance/soc2.py +275 -0
  39. tweek/plugins/detectors/__init__.py +30 -0
  40. tweek/plugins/detectors/continue_dev.py +206 -0
  41. tweek/plugins/detectors/copilot.py +254 -0
  42. tweek/plugins/detectors/cursor.py +192 -0
  43. tweek/plugins/detectors/moltbot.py +205 -0
  44. tweek/plugins/detectors/windsurf.py +214 -0
  45. tweek/plugins/git_discovery.py +395 -0
  46. tweek/plugins/git_installer.py +491 -0
  47. tweek/plugins/git_lockfile.py +338 -0
  48. tweek/plugins/git_registry.py +503 -0
  49. tweek/plugins/git_security.py +482 -0
  50. tweek/plugins/providers/__init__.py +30 -0
  51. tweek/plugins/providers/anthropic.py +181 -0
  52. tweek/plugins/providers/azure_openai.py +289 -0
  53. tweek/plugins/providers/bedrock.py +248 -0
  54. tweek/plugins/providers/google.py +197 -0
  55. tweek/plugins/providers/openai.py +230 -0
  56. tweek/plugins/scope.py +130 -0
  57. tweek/plugins/screening/__init__.py +26 -0
  58. tweek/plugins/screening/llm_reviewer.py +149 -0
  59. tweek/plugins/screening/pattern_matcher.py +273 -0
  60. tweek/plugins/screening/rate_limiter.py +174 -0
  61. tweek/plugins/screening/session_analyzer.py +159 -0
  62. tweek/proxy/__init__.py +302 -0
  63. tweek/proxy/addon.py +223 -0
  64. tweek/proxy/interceptor.py +313 -0
  65. tweek/proxy/server.py +315 -0
  66. tweek/sandbox/__init__.py +71 -0
  67. tweek/sandbox/executor.py +382 -0
  68. tweek/sandbox/linux.py +278 -0
  69. tweek/sandbox/profile_generator.py +323 -0
  70. tweek/screening/__init__.py +13 -0
  71. tweek/screening/context.py +81 -0
  72. tweek/security/__init__.py +22 -0
  73. tweek/security/llm_reviewer.py +348 -0
  74. tweek/security/rate_limiter.py +682 -0
  75. tweek/security/secret_scanner.py +506 -0
  76. tweek/security/session_analyzer.py +600 -0
  77. tweek/vault/__init__.py +40 -0
  78. tweek/vault/cross_platform.py +251 -0
  79. tweek/vault/keychain.py +288 -0
  80. tweek-0.1.0.dist-info/METADATA +335 -0
  81. tweek-0.1.0.dist-info/RECORD +85 -0
  82. tweek-0.1.0.dist-info/WHEEL +5 -0
  83. tweek-0.1.0.dist-info/entry_points.txt +25 -0
  84. tweek-0.1.0.dist-info/licenses/LICENSE +190 -0
  85. tweek-0.1.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,285 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Tweek HIPAA Compliance Plugin
4
+
5
+ Detects Protected Health Information (PHI) identifiers:
6
+ - Patient identifiers (MRN, patient ID)
7
+ - Medical record references
8
+ - Diagnosis codes (ICD-10)
9
+ - Prescription information
10
+ - Healthcare facility identifiers
11
+ - Insurance information
12
+
13
+ Based on the 18 HIPAA identifiers that constitute PHI.
14
+ """
15
+
16
+ from typing import Optional, List, Dict, Any
17
+ from tweek.plugins.base import (
18
+ CompliancePlugin,
19
+ ScanDirection,
20
+ ActionType,
21
+ Severity,
22
+ PatternDefinition,
23
+ )
24
+
25
+
26
+ class HIPAACompliancePlugin(CompliancePlugin):
27
+ """
28
+ HIPAA/PHI compliance plugin.
29
+
30
+ Detects patterns that may indicate Protected Health Information (PHI)
31
+ as defined by HIPAA regulations.
32
+ """
33
+
34
+ VERSION = "1.0.0"
35
+ DESCRIPTION = "Detect HIPAA Protected Health Information (PHI) patterns"
36
+ AUTHOR = "Tweek"
37
+ REQUIRES_LICENSE = "enterprise"
38
+ TAGS = ["compliance", "hipaa", "healthcare", "phi"]
39
+
40
+ def __init__(self, config: Optional[Dict[str, Any]] = None):
41
+ super().__init__(config)
42
+ self._patterns: Optional[List[PatternDefinition]] = None
43
+
44
+ @property
45
+ def name(self) -> str:
46
+ return "hipaa"
47
+
48
+ @property
49
+ def scan_direction(self) -> ScanDirection:
50
+ direction = self._config.get("scan_direction", "both")
51
+ return ScanDirection(direction)
52
+
53
+ def get_patterns(self) -> List[PatternDefinition]:
54
+ """Return HIPAA PHI patterns."""
55
+ if self._patterns is not None:
56
+ return self._patterns
57
+
58
+ self._patterns = [
59
+ # =================================================================
60
+ # Patient Identifiers
61
+ # =================================================================
62
+ PatternDefinition(
63
+ name="mrn",
64
+ regex=r"(?i)(?:MRN|MEDICAL\s*RECORD\s*(?:NUMBER|#|NO\.?))[:\s#]*[A-Z]?\d{5,12}",
65
+ severity=Severity.HIGH,
66
+ description="Medical Record Number",
67
+ default_action=ActionType.WARN,
68
+ tags=["phi", "identifier", "mrn"],
69
+ ),
70
+ PatternDefinition(
71
+ name="patient_id",
72
+ regex=r"(?i)PATIENT\s*(?:ID|#|NUMBER|NO\.?)[:\s#]*[\w-]{4,20}",
73
+ severity=Severity.HIGH,
74
+ description="Patient identifier",
75
+ default_action=ActionType.WARN,
76
+ tags=["phi", "identifier"],
77
+ ),
78
+ PatternDefinition(
79
+ name="account_number",
80
+ regex=r"(?i)(?:HOSPITAL|MEDICAL|PATIENT)\s*(?:ACCOUNT|ACCT)\s*(?:#|NUMBER|NO\.?)[:\s#]*\d{6,15}",
81
+ severity=Severity.HIGH,
82
+ description="Medical account number",
83
+ default_action=ActionType.WARN,
84
+ tags=["phi", "identifier", "account"],
85
+ ),
86
+
87
+ # =================================================================
88
+ # Diagnosis Codes
89
+ # =================================================================
90
+ PatternDefinition(
91
+ name="icd10_code",
92
+ regex=r"\b[A-TV-Z]\d{2}(?:\.\d{1,4})?\b",
93
+ severity=Severity.MEDIUM,
94
+ description="ICD-10 diagnosis code",
95
+ default_action=ActionType.WARN,
96
+ tags=["phi", "diagnosis", "icd10"],
97
+ ),
98
+ PatternDefinition(
99
+ name="diagnosis_context",
100
+ regex=r"(?i)(?:DIAGNOSED?\s+WITH|DX|DIAGNOSIS)[:\s]+[\w\s]{3,50}",
101
+ severity=Severity.MEDIUM,
102
+ description="Diagnosis context",
103
+ default_action=ActionType.WARN,
104
+ tags=["phi", "diagnosis"],
105
+ ),
106
+ PatternDefinition(
107
+ name="cpt_code",
108
+ regex=r"(?i)CPT[:\s#]*\d{5}",
109
+ severity=Severity.MEDIUM,
110
+ description="CPT procedure code",
111
+ default_action=ActionType.WARN,
112
+ tags=["phi", "procedure", "cpt"],
113
+ ),
114
+
115
+ # =================================================================
116
+ # Prescription Information
117
+ # =================================================================
118
+ PatternDefinition(
119
+ name="prescription",
120
+ regex=r"(?i)(?:PRESCRIBED?|RX|MEDICATION)[:\s]+[\w\s-]+(?:\d+\s*(?:mg|ml|mcg|g|units?))",
121
+ severity=Severity.MEDIUM,
122
+ description="Prescription or medication with dosage",
123
+ default_action=ActionType.WARN,
124
+ tags=["phi", "prescription"],
125
+ ),
126
+ PatternDefinition(
127
+ name="dea_number",
128
+ regex=r"(?i)DEA\s*(?:#|NUMBER|NO\.?)?[:\s]*[A-Z]{2}\d{7}",
129
+ severity=Severity.HIGH,
130
+ description="DEA number (prescriber identifier)",
131
+ default_action=ActionType.WARN,
132
+ tags=["phi", "identifier", "dea"],
133
+ ),
134
+ PatternDefinition(
135
+ name="ndc_code",
136
+ regex=r"(?i)NDC[:\s#]*\d{4,5}-\d{3,4}-\d{1,2}",
137
+ severity=Severity.MEDIUM,
138
+ description="National Drug Code",
139
+ default_action=ActionType.WARN,
140
+ tags=["phi", "medication", "ndc"],
141
+ ),
142
+
143
+ # =================================================================
144
+ # Insurance Information
145
+ # =================================================================
146
+ PatternDefinition(
147
+ name="health_plan_id",
148
+ regex=r"(?i)(?:HEALTH\s*PLAN|INSURANCE|POLICY)\s*(?:ID|#|NUMBER|NO\.?)[:\s#]*[\w-]{6,20}",
149
+ severity=Severity.HIGH,
150
+ description="Health plan/insurance identifier",
151
+ default_action=ActionType.WARN,
152
+ tags=["phi", "insurance"],
153
+ ),
154
+ PatternDefinition(
155
+ name="medicare_id",
156
+ regex=r"(?i)MEDICARE\s*(?:ID|#|NUMBER|NO\.?|BENEFICIARY)[:\s#]*\d[A-Z]\d[A-Z]-?[A-Z]{2}\d-?[A-Z]{2}\d{2}",
157
+ severity=Severity.HIGH,
158
+ description="Medicare Beneficiary Identifier",
159
+ default_action=ActionType.WARN,
160
+ tags=["phi", "medicare", "identifier"],
161
+ ),
162
+ PatternDefinition(
163
+ name="medicaid_id",
164
+ regex=r"(?i)MEDICAID\s*(?:ID|#|NUMBER|NO\.?)[:\s#]*[\w-]{8,15}",
165
+ severity=Severity.HIGH,
166
+ description="Medicaid identifier",
167
+ default_action=ActionType.WARN,
168
+ tags=["phi", "medicaid", "identifier"],
169
+ ),
170
+
171
+ # =================================================================
172
+ # Provider Identifiers
173
+ # =================================================================
174
+ PatternDefinition(
175
+ name="npi",
176
+ regex=r"(?i)NPI[:\s#]*\d{10}",
177
+ severity=Severity.MEDIUM,
178
+ description="National Provider Identifier",
179
+ default_action=ActionType.WARN,
180
+ tags=["phi", "provider", "npi"],
181
+ ),
182
+ PatternDefinition(
183
+ name="physician_context",
184
+ regex=r"(?i)(?:ATTENDING|TREATING|PRIMARY)\s*(?:PHYSICIAN|DOCTOR|DR\.?)[:\s]+[\w\s]{3,40}",
185
+ severity=Severity.LOW,
186
+ description="Physician reference with context",
187
+ default_action=ActionType.WARN,
188
+ tags=["phi", "provider"],
189
+ ),
190
+
191
+ # =================================================================
192
+ # Facility Information
193
+ # =================================================================
194
+ PatternDefinition(
195
+ name="facility_id",
196
+ regex=r"(?i)(?:FACILITY|HOSPITAL|CLINIC)\s*(?:ID|#|NUMBER|NO\.?)[:\s#]*[\w-]{4,20}",
197
+ severity=Severity.MEDIUM,
198
+ description="Healthcare facility identifier",
199
+ default_action=ActionType.WARN,
200
+ tags=["phi", "facility"],
201
+ ),
202
+ PatternDefinition(
203
+ name="bed_location",
204
+ regex=r"(?i)(?:ROOM|BED|UNIT)[:\s#]*[\w-]{1,10}",
205
+ severity=Severity.LOW,
206
+ description="Patient room/bed location",
207
+ default_action=ActionType.WARN,
208
+ tags=["phi", "location"],
209
+ ),
210
+
211
+ # =================================================================
212
+ # Medical Conditions (Sensitive)
213
+ # =================================================================
214
+ PatternDefinition(
215
+ name="hiv_status",
216
+ regex=r"(?i)HIV\s*(?:\+|\-|POSITIVE|NEGATIVE|STATUS|TEST)",
217
+ severity=Severity.CRITICAL,
218
+ description="HIV status information",
219
+ default_action=ActionType.BLOCK,
220
+ tags=["phi", "sensitive", "hiv"],
221
+ ),
222
+ PatternDefinition(
223
+ name="mental_health",
224
+ regex=r"(?i)(?:PSYCH(?:IATRIC)?|MENTAL\s*HEALTH)\s*(?:DIAGNOSIS|HISTORY|TREATMENT|EVAL)",
225
+ severity=Severity.HIGH,
226
+ description="Mental health information",
227
+ default_action=ActionType.WARN,
228
+ tags=["phi", "sensitive", "mental-health"],
229
+ ),
230
+ PatternDefinition(
231
+ name="substance_abuse",
232
+ regex=r"(?i)(?:SUBSTANCE\s*ABUSE|DRUG\s*(?:ABUSE|ADDICTION)|ALCOHOL(?:ISM)?)\s*(?:HISTORY|TREATMENT|PROGRAM)",
233
+ severity=Severity.HIGH,
234
+ description="Substance abuse information",
235
+ default_action=ActionType.WARN,
236
+ tags=["phi", "sensitive", "substance-abuse"],
237
+ ),
238
+
239
+ # =================================================================
240
+ # Dates (PHI when combined with health info)
241
+ # =================================================================
242
+ PatternDefinition(
243
+ name="date_of_service",
244
+ regex=r"(?i)(?:DATE\s*OF\s*(?:SERVICE|VISIT|ADMISSION|DISCHARGE)|DOS)[:\s]*\d{1,2}[-/]\d{1,2}[-/]\d{2,4}",
245
+ severity=Severity.LOW,
246
+ description="Date of healthcare service",
247
+ default_action=ActionType.WARN,
248
+ tags=["phi", "date"],
249
+ ),
250
+ PatternDefinition(
251
+ name="dob_context",
252
+ regex=r"(?i)(?:DOB|DATE\s*OF\s*BIRTH|BIRTH\s*DATE)[:\s]*\d{1,2}[-/]\d{1,2}[-/]\d{2,4}",
253
+ severity=Severity.HIGH,
254
+ description="Date of birth in healthcare context",
255
+ default_action=ActionType.WARN,
256
+ tags=["phi", "date", "dob"],
257
+ ),
258
+ ]
259
+
260
+ return self._patterns
261
+
262
+ def _format_message(
263
+ self,
264
+ findings: List,
265
+ direction: ScanDirection
266
+ ) -> Optional[str]:
267
+ """Format a HIPAA-specific message."""
268
+ if not findings:
269
+ return None
270
+
271
+ # Categorize findings
272
+ high_severity = [f for f in findings if f.severity in (Severity.HIGH, Severity.CRITICAL)]
273
+
274
+ if direction == ScanDirection.OUTPUT:
275
+ msg = f"WARNING: LLM output may contain {len(findings)} PHI indicator(s).\n"
276
+ if high_severity:
277
+ msg += f" {len(high_severity)} HIGH/CRITICAL severity pattern(s) detected.\n"
278
+ msg += "Review output before sharing to ensure HIPAA compliance."
279
+ return msg
280
+ else:
281
+ msg = f"ALERT: Input may contain {len(findings)} PHI indicator(s).\n"
282
+ if high_severity:
283
+ msg += f" {len(high_severity)} HIGH/CRITICAL severity pattern(s) detected.\n"
284
+ msg += "Ensure this data is handled in accordance with HIPAA requirements."
285
+ return msg
@@ -0,0 +1,322 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Tweek Legal Compliance Plugin
4
+
5
+ Detects legal privilege and confidentiality markers:
6
+ - Attorney-client privilege
7
+ - Work product doctrine
8
+ - Confidential communications
9
+ - Settlement discussions
10
+ - Trade secrets
11
+ - NDA-protected information
12
+
13
+ Helps prevent inadvertent waiver of legal privileges through LLM processing.
14
+ """
15
+
16
+ from typing import Optional, List, Dict, Any
17
+ from tweek.plugins.base import (
18
+ CompliancePlugin,
19
+ ScanDirection,
20
+ ActionType,
21
+ Severity,
22
+ PatternDefinition,
23
+ )
24
+
25
+
26
+ class LegalCompliancePlugin(CompliancePlugin):
27
+ """
28
+ Legal privilege and confidentiality compliance plugin.
29
+
30
+ Detects markers indicating legally protected communications
31
+ that should not be processed by external AI systems.
32
+ """
33
+
34
+ VERSION = "1.0.0"
35
+ DESCRIPTION = "Detect legal privilege and confidentiality markers"
36
+ AUTHOR = "Tweek"
37
+ REQUIRES_LICENSE = "enterprise"
38
+ TAGS = ["compliance", "legal", "privilege", "confidential"]
39
+
40
+ def __init__(self, config: Optional[Dict[str, Any]] = None):
41
+ super().__init__(config)
42
+ self._patterns: Optional[List[PatternDefinition]] = None
43
+
44
+ @property
45
+ def name(self) -> str:
46
+ return "legal"
47
+
48
+ @property
49
+ def scan_direction(self) -> ScanDirection:
50
+ direction = self._config.get("scan_direction", "both")
51
+ return ScanDirection(direction)
52
+
53
+ def get_patterns(self) -> List[PatternDefinition]:
54
+ """Return legal privilege patterns."""
55
+ if self._patterns is not None:
56
+ return self._patterns
57
+
58
+ self._patterns = [
59
+ # =================================================================
60
+ # Attorney-Client Privilege
61
+ # =================================================================
62
+ PatternDefinition(
63
+ name="attorney_client_privilege",
64
+ regex=r"(?i)ATTORNEY[\s-]*CLIENT\s+PRIVILEG(?:E|ED)",
65
+ severity=Severity.HIGH,
66
+ description="Attorney-client privilege marker",
67
+ default_action=ActionType.WARN,
68
+ tags=["privilege", "attorney-client"],
69
+ ),
70
+ PatternDefinition(
71
+ name="privileged_confidential",
72
+ regex=r"(?i)PRIVILEGED\s+(?:AND\s+)?CONFIDENTIAL",
73
+ severity=Severity.HIGH,
74
+ description="Privileged and confidential marker",
75
+ default_action=ActionType.WARN,
76
+ tags=["privilege", "confidential"],
77
+ ),
78
+ PatternDefinition(
79
+ name="legal_privilege",
80
+ regex=r"(?i)(?:SUBJECT\s+TO|PROTECTED\s+BY)\s+(?:LEGAL\s+)?PRIVILEG(?:E|ED)",
81
+ severity=Severity.HIGH,
82
+ description="Legal privilege protection marker",
83
+ default_action=ActionType.WARN,
84
+ tags=["privilege"],
85
+ ),
86
+ PatternDefinition(
87
+ name="solicitor_privilege",
88
+ regex=r"(?i)SOLICITOR[\s-]*CLIENT\s+PRIVILEG(?:E|ED)|LEGAL\s+PROFESSIONAL\s+PRIVILEGE",
89
+ severity=Severity.HIGH,
90
+ description="Solicitor-client privilege (UK/Commonwealth)",
91
+ default_action=ActionType.WARN,
92
+ tags=["privilege", "solicitor-client"],
93
+ ),
94
+
95
+ # =================================================================
96
+ # Work Product Doctrine
97
+ # =================================================================
98
+ PatternDefinition(
99
+ name="work_product",
100
+ regex=r"(?i)(?:ATTORNEY|LEGAL|LAWYER)\s+WORK\s+PRODUCT",
101
+ severity=Severity.HIGH,
102
+ description="Attorney work product doctrine",
103
+ default_action=ActionType.WARN,
104
+ tags=["privilege", "work-product"],
105
+ ),
106
+ PatternDefinition(
107
+ name="trial_preparation",
108
+ regex=r"(?i)TRIAL\s+PREPARATION\s+MATERIALS?",
109
+ severity=Severity.HIGH,
110
+ description="Trial preparation materials",
111
+ default_action=ActionType.WARN,
112
+ tags=["privilege", "work-product"],
113
+ ),
114
+ PatternDefinition(
115
+ name="litigation_hold",
116
+ regex=r"(?i)LITIGATION\s+HOLD|LEGAL\s+HOLD|PRESERVATION\s+NOTICE",
117
+ severity=Severity.MEDIUM,
118
+ description="Litigation hold notice",
119
+ default_action=ActionType.WARN,
120
+ tags=["litigation"],
121
+ ),
122
+
123
+ # =================================================================
124
+ # Confidential Communications
125
+ # =================================================================
126
+ PatternDefinition(
127
+ name="confidential_header",
128
+ regex=r"(?i)^[-=]*\s*CONFIDENTIAL\s*[-=]*$",
129
+ severity=Severity.MEDIUM,
130
+ description="Confidential header/footer",
131
+ default_action=ActionType.WARN,
132
+ tags=["confidential"],
133
+ ),
134
+ PatternDefinition(
135
+ name="strictly_confidential",
136
+ regex=r"(?i)STRICTLY\s+CONFIDENTIAL",
137
+ severity=Severity.HIGH,
138
+ description="Strictly confidential marker",
139
+ default_action=ActionType.WARN,
140
+ tags=["confidential"],
141
+ ),
142
+ PatternDefinition(
143
+ name="confidential_info",
144
+ regex=r"(?i)THIS\s+(?:DOCUMENT|COMMUNICATION|EMAIL|MESSAGE)\s+(?:IS|CONTAINS?)\s+CONFIDENTIAL",
145
+ severity=Severity.MEDIUM,
146
+ description="Confidential communication notice",
147
+ default_action=ActionType.WARN,
148
+ tags=["confidential"],
149
+ ),
150
+
151
+ # =================================================================
152
+ # Settlement and Mediation
153
+ # =================================================================
154
+ PatternDefinition(
155
+ name="settlement_privilege",
156
+ regex=r"(?i)SETTLEMENT\s+(?:PRIVILEGE|NEGOTIATIONS?|DISCUSSION)",
157
+ severity=Severity.HIGH,
158
+ description="Settlement privilege/negotiations",
159
+ default_action=ActionType.WARN,
160
+ tags=["privilege", "settlement"],
161
+ ),
162
+ PatternDefinition(
163
+ name="mediation_confidential",
164
+ regex=r"(?i)(?:MEDIATION|ARBITRATION)\s+(?:PRIVILEGE|CONFIDENTIAL)",
165
+ severity=Severity.HIGH,
166
+ description="Mediation/arbitration confidentiality",
167
+ default_action=ActionType.WARN,
168
+ tags=["privilege", "adr"],
169
+ ),
170
+ PatternDefinition(
171
+ name="without_prejudice",
172
+ regex=r"(?i)WITHOUT\s+PREJUDICE",
173
+ severity=Severity.MEDIUM,
174
+ description="Without prejudice marker (settlement protection)",
175
+ default_action=ActionType.WARN,
176
+ tags=["privilege", "settlement"],
177
+ ),
178
+ PatternDefinition(
179
+ name="rule_408",
180
+ regex=r"(?i)(?:FRE|FEDERAL\s+RULES?\s+OF\s+EVIDENCE)\s+(?:RULE\s+)?408|SETTLEMENT\s+COMMUNICATIONS?",
181
+ severity=Severity.MEDIUM,
182
+ description="FRE 408 settlement communication",
183
+ default_action=ActionType.WARN,
184
+ tags=["privilege", "settlement"],
185
+ ),
186
+
187
+ # =================================================================
188
+ # Trade Secrets
189
+ # =================================================================
190
+ PatternDefinition(
191
+ name="trade_secret",
192
+ regex=r"(?i)TRADE\s+SECRET|PROPRIETARY\s+(?:AND\s+)?CONFIDENTIAL",
193
+ severity=Severity.HIGH,
194
+ description="Trade secret marker",
195
+ default_action=ActionType.WARN,
196
+ tags=["trade-secret", "confidential"],
197
+ ),
198
+ PatternDefinition(
199
+ name="dtsa_protected",
200
+ regex=r"(?i)(?:DTSA|DEFEND\s+TRADE\s+SECRETS\s+ACT)\s+PROTECTED",
201
+ severity=Severity.HIGH,
202
+ description="DTSA protected information",
203
+ default_action=ActionType.WARN,
204
+ tags=["trade-secret"],
205
+ ),
206
+
207
+ # =================================================================
208
+ # Non-Disclosure Agreements
209
+ # =================================================================
210
+ PatternDefinition(
211
+ name="nda_protected",
212
+ regex=r"(?i)(?:NDA|NON[\s-]?DISCLOSURE\s+AGREEMENT)\s+PROTECTED",
213
+ severity=Severity.MEDIUM,
214
+ description="NDA-protected information",
215
+ default_action=ActionType.WARN,
216
+ tags=["nda", "confidential"],
217
+ ),
218
+ PatternDefinition(
219
+ name="confidentiality_agreement",
220
+ regex=r"(?i)(?:SUBJECT\s+TO|PROTECTED\s+BY|COVERED\s+BY)\s+(?:A\s+)?(?:CONFIDENTIALITY|NON[\s-]?DISCLOSURE)\s+AGREEMENT",
221
+ severity=Severity.MEDIUM,
222
+ description="Confidentiality agreement reference",
223
+ default_action=ActionType.WARN,
224
+ tags=["nda", "confidential"],
225
+ ),
226
+
227
+ # =================================================================
228
+ # Regulatory and Compliance
229
+ # =================================================================
230
+ PatternDefinition(
231
+ name="export_controlled",
232
+ regex=r"(?i)(?:EXPORT\s+CONTROLLED?|ITAR|EAR)\s+(?:INFORMATION|DATA|MATERIAL)",
233
+ severity=Severity.HIGH,
234
+ description="Export controlled information (ITAR/EAR)",
235
+ default_action=ActionType.WARN,
236
+ tags=["export-control", "regulatory"],
237
+ ),
238
+ PatternDefinition(
239
+ name="material_non_public",
240
+ regex=r"(?i)MATERIAL\s+NON[\s-]?PUBLIC\s+INFORMATION|MNPI",
241
+ severity=Severity.CRITICAL,
242
+ description="Material non-public information (insider trading)",
243
+ default_action=ActionType.BLOCK,
244
+ tags=["mnpi", "securities"],
245
+ ),
246
+
247
+ # =================================================================
248
+ # Legal Disclaimers and Warnings
249
+ # =================================================================
250
+ PatternDefinition(
251
+ name="unauthorized_disclosure",
252
+ regex=r"(?i)UNAUTHORIZED\s+(?:DISCLOSURE|DISTRIBUTION|USE|ACCESS)\s+(?:IS\s+)?(?:STRICTLY\s+)?PROHIBITED",
253
+ severity=Severity.MEDIUM,
254
+ description="Unauthorized disclosure warning",
255
+ default_action=ActionType.WARN,
256
+ tags=["disclaimer"],
257
+ ),
258
+ PatternDefinition(
259
+ name="intended_recipient",
260
+ regex=r"(?i)(?:INTENDED\s+(?:ONLY\s+)?FOR|SOLELY\s+FOR)\s+(?:THE\s+)?(?:NAMED\s+)?RECIPIENT",
261
+ severity=Severity.LOW,
262
+ description="Intended recipient notice",
263
+ default_action=ActionType.WARN,
264
+ tags=["disclaimer"],
265
+ ),
266
+ PatternDefinition(
267
+ name="delete_if_received",
268
+ regex=r"(?i)(?:IF\s+YOU\s+(?:HAVE\s+)?RECEIVED\s+THIS\s+(?:IN\s+)?ERROR|PLEASE\s+DELETE|NOTIFY\s+THE\s+SENDER)",
269
+ severity=Severity.LOW,
270
+ description="Error receipt notice",
271
+ default_action=ActionType.WARN,
272
+ tags=["disclaimer"],
273
+ ),
274
+
275
+ # =================================================================
276
+ # Legal Document Types
277
+ # =================================================================
278
+ PatternDefinition(
279
+ name="draft_document",
280
+ regex=r"(?i)(?:DRAFT|PRELIMINARY)\s+[-–]\s+(?:PRIVILEGED|CONFIDENTIAL|NOT\s+FOR\s+DISTRIBUTION)",
281
+ severity=Severity.MEDIUM,
282
+ description="Draft/preliminary document marker",
283
+ default_action=ActionType.WARN,
284
+ tags=["draft", "confidential"],
285
+ ),
286
+ PatternDefinition(
287
+ name="legal_memo",
288
+ regex=r"(?i)(?:ATTORNEY|LEGAL)\s+(?:MEMORANDUM|MEMO)\s+[-–]\s+(?:PRIVILEGED|CONFIDENTIAL)",
289
+ severity=Severity.HIGH,
290
+ description="Legal memorandum marker",
291
+ default_action=ActionType.WARN,
292
+ tags=["memo", "privilege"],
293
+ ),
294
+ ]
295
+
296
+ return self._patterns
297
+
298
+ def _format_message(
299
+ self,
300
+ findings: List,
301
+ direction: ScanDirection
302
+ ) -> Optional[str]:
303
+ """Format a legal-specific message."""
304
+ if not findings:
305
+ return None
306
+
307
+ privilege_findings = [f for f in findings if "privilege" in f.metadata.get("pattern_tags", [])]
308
+ trade_secret_findings = [f for f in findings if "trade-secret" in f.metadata.get("pattern_tags", [])]
309
+
310
+ if direction == ScanDirection.OUTPUT:
311
+ msg = f"WARNING: LLM output contains {len(findings)} legal/privilege marker(s).\n"
312
+ msg += "These may be hallucinated and do not confer actual privilege.\n"
313
+ msg += "Do NOT rely on these markers for legal protection."
314
+ else:
315
+ msg = f"ALERT: Input contains {len(findings)} legal/privilege marker(s).\n"
316
+ if privilege_findings:
317
+ msg += f" {len(privilege_findings)} privilege marker(s) - may constitute waiver if shared\n"
318
+ if trade_secret_findings:
319
+ msg += f" {len(trade_secret_findings)} trade secret marker(s)\n"
320
+ msg += "Processing privileged content through external AI may waive protections."
321
+
322
+ return msg