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,361 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Tweek PCI-DSS Compliance Plugin
4
+
5
+ Detects payment card industry data:
6
+ - Credit/debit card numbers (with Luhn validation)
7
+ - CVV/CVC codes
8
+ - Bank account and routing numbers
9
+ - Payment tokens and references
10
+ - Cardholder data markers
11
+
12
+ Based on PCI-DSS requirements for protecting cardholder data.
13
+ """
14
+
15
+ from typing import Optional, List, Dict, Any
16
+ from tweek.plugins.base import (
17
+ CompliancePlugin,
18
+ ScanDirection,
19
+ ActionType,
20
+ Severity,
21
+ PatternDefinition,
22
+ Finding,
23
+ )
24
+
25
+
26
+ def luhn_checksum(card_number: str) -> bool:
27
+ """
28
+ Validate a card number using the Luhn algorithm.
29
+
30
+ Args:
31
+ card_number: Card number string (digits only)
32
+
33
+ Returns:
34
+ True if valid according to Luhn algorithm
35
+ """
36
+ # Remove non-digits
37
+ digits = ''.join(c for c in card_number if c.isdigit())
38
+
39
+ if len(digits) < 13 or len(digits) > 19:
40
+ return False
41
+
42
+ # Luhn algorithm
43
+ total = 0
44
+ reverse_digits = digits[::-1]
45
+
46
+ for i, digit in enumerate(reverse_digits):
47
+ n = int(digit)
48
+ if i % 2 == 1:
49
+ n *= 2
50
+ if n > 9:
51
+ n -= 9
52
+ total += n
53
+
54
+ return total % 10 == 0
55
+
56
+
57
+ class PCICompliancePlugin(CompliancePlugin):
58
+ """
59
+ PCI-DSS compliance plugin.
60
+
61
+ Detects payment card data and related financial information
62
+ that must be protected under PCI-DSS requirements.
63
+ """
64
+
65
+ VERSION = "1.0.0"
66
+ DESCRIPTION = "Detect PCI-DSS payment card and financial data"
67
+ AUTHOR = "Tweek"
68
+ REQUIRES_LICENSE = "enterprise"
69
+ TAGS = ["compliance", "pci", "financial", "payment"]
70
+
71
+ def __init__(self, config: Optional[Dict[str, Any]] = None):
72
+ super().__init__(config)
73
+ self._patterns: Optional[List[PatternDefinition]] = None
74
+ self._validate_luhn = config.get("validate_luhn", True) if config else True
75
+
76
+ @property
77
+ def name(self) -> str:
78
+ return "pci"
79
+
80
+ @property
81
+ def scan_direction(self) -> ScanDirection:
82
+ direction = self._config.get("scan_direction", "both")
83
+ return ScanDirection(direction)
84
+
85
+ def get_patterns(self) -> List[PatternDefinition]:
86
+ """Return PCI-DSS patterns."""
87
+ if self._patterns is not None:
88
+ return self._patterns
89
+
90
+ self._patterns = [
91
+ # =================================================================
92
+ # Credit Card Numbers
93
+ # =================================================================
94
+ PatternDefinition(
95
+ name="visa",
96
+ regex=r"\b4[0-9]{3}[\s-]?[0-9]{4}[\s-]?[0-9]{4}[\s-]?[0-9]{4}\b",
97
+ severity=Severity.CRITICAL,
98
+ description="Visa card number",
99
+ default_action=ActionType.REDACT,
100
+ tags=["pci", "card", "visa"],
101
+ ),
102
+ PatternDefinition(
103
+ name="mastercard",
104
+ regex=r"\b5[1-5][0-9]{2}[\s-]?[0-9]{4}[\s-]?[0-9]{4}[\s-]?[0-9]{4}\b",
105
+ severity=Severity.CRITICAL,
106
+ description="Mastercard number",
107
+ default_action=ActionType.REDACT,
108
+ tags=["pci", "card", "mastercard"],
109
+ ),
110
+ PatternDefinition(
111
+ name="mastercard_2_series",
112
+ regex=r"\b2[2-7][0-9]{2}[\s-]?[0-9]{4}[\s-]?[0-9]{4}[\s-]?[0-9]{4}\b",
113
+ severity=Severity.CRITICAL,
114
+ description="Mastercard 2-series number",
115
+ default_action=ActionType.REDACT,
116
+ tags=["pci", "card", "mastercard"],
117
+ ),
118
+ PatternDefinition(
119
+ name="amex",
120
+ regex=r"\b3[47][0-9]{2}[\s-]?[0-9]{6}[\s-]?[0-9]{5}\b",
121
+ severity=Severity.CRITICAL,
122
+ description="American Express card number",
123
+ default_action=ActionType.REDACT,
124
+ tags=["pci", "card", "amex"],
125
+ ),
126
+ PatternDefinition(
127
+ name="discover",
128
+ regex=r"\b6(?:011|5[0-9]{2})[\s-]?[0-9]{4}[\s-]?[0-9]{4}[\s-]?[0-9]{4}\b",
129
+ severity=Severity.CRITICAL,
130
+ description="Discover card number",
131
+ default_action=ActionType.REDACT,
132
+ tags=["pci", "card", "discover"],
133
+ ),
134
+ PatternDefinition(
135
+ name="diners",
136
+ regex=r"\b3(?:0[0-5]|[68][0-9])[0-9][\s-]?[0-9]{6}[\s-]?[0-9]{4}\b",
137
+ severity=Severity.CRITICAL,
138
+ description="Diners Club card number",
139
+ default_action=ActionType.REDACT,
140
+ tags=["pci", "card", "diners"],
141
+ ),
142
+ PatternDefinition(
143
+ name="jcb",
144
+ regex=r"\b(?:2131|1800|35[0-9]{2})[\s-]?[0-9]{4}[\s-]?[0-9]{4}[\s-]?[0-9]{4}\b",
145
+ severity=Severity.CRITICAL,
146
+ description="JCB card number",
147
+ default_action=ActionType.REDACT,
148
+ tags=["pci", "card", "jcb"],
149
+ ),
150
+ PatternDefinition(
151
+ name="generic_card",
152
+ regex=r"\b(?:[0-9]{4}[\s-]?){3}[0-9]{4}\b",
153
+ severity=Severity.HIGH,
154
+ description="Potential card number (16 digits)",
155
+ default_action=ActionType.WARN,
156
+ tags=["pci", "card", "generic"],
157
+ ),
158
+
159
+ # =================================================================
160
+ # CVV/CVC/Security Codes
161
+ # =================================================================
162
+ PatternDefinition(
163
+ name="cvv_labeled",
164
+ regex=r"(?i)(?:CVV|CVC|CVV2|CVC2|CSC|CID)[:\s#]*[0-9]{3,4}",
165
+ severity=Severity.CRITICAL,
166
+ description="Card security code (CVV/CVC)",
167
+ default_action=ActionType.REDACT,
168
+ tags=["pci", "cvv"],
169
+ ),
170
+ PatternDefinition(
171
+ name="security_code",
172
+ regex=r"(?i)(?:SECURITY\s*CODE|CARD\s*CODE|VERIFICATION\s*(?:CODE|VALUE))[:\s#]*[0-9]{3,4}",
173
+ severity=Severity.CRITICAL,
174
+ description="Card security/verification code",
175
+ default_action=ActionType.REDACT,
176
+ tags=["pci", "cvv"],
177
+ ),
178
+
179
+ # =================================================================
180
+ # Expiration Dates
181
+ # =================================================================
182
+ PatternDefinition(
183
+ name="expiry_labeled",
184
+ regex=r"(?i)(?:EXP(?:IRY|IRATION)?|VALID\s*(?:THRU|UNTIL))[:\s]*(?:0[1-9]|1[0-2])[\s/\-]?(?:[0-9]{2}|20[2-9][0-9])",
185
+ severity=Severity.HIGH,
186
+ description="Card expiration date",
187
+ default_action=ActionType.WARN,
188
+ tags=["pci", "expiry"],
189
+ ),
190
+
191
+ # =================================================================
192
+ # Bank Account Information
193
+ # =================================================================
194
+ PatternDefinition(
195
+ name="bank_account",
196
+ regex=r"(?i)(?:BANK\s*)?(?:ACCOUNT|ACCT)[\s#:]*[0-9]{8,17}",
197
+ severity=Severity.HIGH,
198
+ description="Bank account number",
199
+ default_action=ActionType.WARN,
200
+ tags=["pci", "bank", "account"],
201
+ ),
202
+ PatternDefinition(
203
+ name="routing_number",
204
+ regex=r"(?i)(?:ROUTING|ABA|RTN)[\s#:]*[0-9]{9}",
205
+ severity=Severity.HIGH,
206
+ description="Bank routing number",
207
+ default_action=ActionType.WARN,
208
+ tags=["pci", "bank", "routing"],
209
+ ),
210
+ PatternDefinition(
211
+ name="iban",
212
+ regex=r"\b[A-Z]{2}[0-9]{2}[A-Z0-9]{4}[0-9]{7}(?:[A-Z0-9]?){0,16}\b",
213
+ severity=Severity.HIGH,
214
+ description="International Bank Account Number (IBAN)",
215
+ default_action=ActionType.WARN,
216
+ tags=["pci", "bank", "iban"],
217
+ ),
218
+ PatternDefinition(
219
+ name="swift_bic",
220
+ regex=r"(?i)(?:SWIFT|BIC)[:\s]*[A-Z]{6}[A-Z0-9]{2}(?:[A-Z0-9]{3})?",
221
+ severity=Severity.MEDIUM,
222
+ description="SWIFT/BIC code",
223
+ default_action=ActionType.WARN,
224
+ tags=["pci", "bank", "swift"],
225
+ ),
226
+
227
+ # =================================================================
228
+ # Payment Tokens and References
229
+ # =================================================================
230
+ PatternDefinition(
231
+ name="stripe_token",
232
+ regex=r"(?i)(?:tok|pm|pi|ch|cus|sub|inv|in)_[a-zA-Z0-9]{14,}",
233
+ severity=Severity.MEDIUM,
234
+ description="Stripe payment token/ID",
235
+ default_action=ActionType.WARN,
236
+ tags=["pci", "token", "stripe"],
237
+ ),
238
+ PatternDefinition(
239
+ name="paypal_transaction",
240
+ regex=r"(?i)(?:PAYPAL|PP)\s*(?:TRANSACTION|TXN|ID)[:\s#]*[A-Z0-9]{17}",
241
+ severity=Severity.MEDIUM,
242
+ description="PayPal transaction ID",
243
+ default_action=ActionType.WARN,
244
+ tags=["pci", "token", "paypal"],
245
+ ),
246
+
247
+ # =================================================================
248
+ # Cardholder Data
249
+ # =================================================================
250
+ PatternDefinition(
251
+ name="cardholder_name",
252
+ regex=r"(?i)(?:CARDHOLDER|CARD\s*HOLDER|NAME\s*ON\s*CARD)[:\s]+[A-Z][a-z]+(?:\s+[A-Z][a-z]+)+",
253
+ severity=Severity.MEDIUM,
254
+ description="Cardholder name",
255
+ default_action=ActionType.WARN,
256
+ tags=["pci", "cardholder"],
257
+ ),
258
+ PatternDefinition(
259
+ name="billing_address",
260
+ regex=r"(?i)(?:BILLING\s*ADDRESS|CARD\s*ADDRESS)[:\s]+[\w\s,.-]+",
261
+ severity=Severity.LOW,
262
+ description="Billing address",
263
+ default_action=ActionType.WARN,
264
+ tags=["pci", "cardholder", "address"],
265
+ ),
266
+
267
+ # =================================================================
268
+ # PCI Context Markers
269
+ # =================================================================
270
+ PatternDefinition(
271
+ name="pan_marker",
272
+ regex=r"(?i)\bPAN\b[:\s]+|PRIMARY\s*ACCOUNT\s*NUMBER",
273
+ severity=Severity.HIGH,
274
+ description="Primary Account Number reference",
275
+ default_action=ActionType.WARN,
276
+ tags=["pci", "marker"],
277
+ ),
278
+ PatternDefinition(
279
+ name="magnetic_stripe",
280
+ regex=r"(?i)(?:TRACK\s*[12]|MAGNETIC\s*STRIPE|MAG\s*STRIPE)\s*DATA",
281
+ severity=Severity.CRITICAL,
282
+ description="Magnetic stripe data reference",
283
+ default_action=ActionType.BLOCK,
284
+ tags=["pci", "magnetic", "sensitive"],
285
+ ),
286
+ PatternDefinition(
287
+ name="pin_block",
288
+ regex=r"(?i)PIN\s*BLOCK|ENCRYPTED\s*PIN",
289
+ severity=Severity.CRITICAL,
290
+ description="PIN block reference",
291
+ default_action=ActionType.BLOCK,
292
+ tags=["pci", "pin", "sensitive"],
293
+ ),
294
+ ]
295
+
296
+ return self._patterns
297
+
298
+ def scan(self, content: str, direction: ScanDirection) -> "ScanResult":
299
+ """
300
+ Scan content for PCI data with Luhn validation for card numbers.
301
+
302
+ Overrides base scan to add Luhn validation for card numbers.
303
+ """
304
+ from tweek.plugins.base import ScanResult
305
+
306
+ result = super().scan(content, direction)
307
+
308
+ if not self._validate_luhn:
309
+ return result
310
+
311
+ # Filter card findings through Luhn validation
312
+ validated_findings = []
313
+ for finding in result.findings:
314
+ if finding.pattern_name in (
315
+ "visa", "mastercard", "mastercard_2_series", "amex",
316
+ "discover", "diners", "jcb", "generic_card"
317
+ ):
318
+ # Validate with Luhn
319
+ if luhn_checksum(finding.matched_text):
320
+ validated_findings.append(finding)
321
+ # else: skip false positive
322
+ else:
323
+ # Non-card patterns pass through
324
+ validated_findings.append(finding)
325
+
326
+ return ScanResult(
327
+ passed=len(validated_findings) == 0,
328
+ findings=validated_findings,
329
+ action=self._determine_action(validated_findings),
330
+ message=self._format_message(validated_findings, direction),
331
+ scan_direction=direction,
332
+ plugin_name=self.name
333
+ )
334
+
335
+ def _format_message(
336
+ self,
337
+ findings: List,
338
+ direction: ScanDirection
339
+ ) -> Optional[str]:
340
+ """Format a PCI-specific message."""
341
+ if not findings:
342
+ return None
343
+
344
+ card_findings = [f for f in findings if "card" in f.metadata.get("pattern_tags", [])]
345
+ cvv_findings = [f for f in findings if "cvv" in f.metadata.get("pattern_tags", [])]
346
+
347
+ msg_parts = []
348
+
349
+ if direction == ScanDirection.OUTPUT:
350
+ msg_parts.append(f"WARNING: LLM output contains {len(findings)} potential PCI data element(s).")
351
+ else:
352
+ msg_parts.append(f"ALERT: Input contains {len(findings)} potential PCI data element(s).")
353
+
354
+ if card_findings:
355
+ msg_parts.append(f" {len(card_findings)} card number(s) detected (Luhn validated)")
356
+ if cvv_findings:
357
+ msg_parts.append(f" {len(cvv_findings)} CVV/security code(s) detected")
358
+
359
+ msg_parts.append("This data must be protected per PCI-DSS requirements.")
360
+
361
+ return "\n".join(msg_parts)
@@ -0,0 +1,275 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Tweek SOC2 Compliance Plugin
4
+
5
+ Detects patterns indicating SOC2-relevant security and compliance concerns:
6
+ - Access control indicators (passwords, API keys in logs)
7
+ - Audit log patterns (security events, access logs)
8
+ - Change management markers
9
+ - Incident response indicators
10
+ - Risk assessment markers
11
+ - Security policy references
12
+
13
+ SOC2 Trust Services Criteria:
14
+ - Security
15
+ - Availability
16
+ - Processing Integrity
17
+ - Confidentiality
18
+ - Privacy
19
+
20
+ Supports bidirectional scanning:
21
+ - OUTPUT: Detect LLM generating content that could violate SOC2 controls
22
+ - INPUT: Detect sensitive SOC2-relevant data in incoming content
23
+ """
24
+
25
+ from typing import Optional, List, Dict, Any
26
+ from tweek.plugins.base import (
27
+ CompliancePlugin,
28
+ ScanDirection,
29
+ ActionType,
30
+ Severity,
31
+ PatternDefinition,
32
+ )
33
+
34
+
35
+ class SOC2CompliancePlugin(CompliancePlugin):
36
+ """
37
+ SOC2 compliance plugin.
38
+
39
+ Detects patterns relevant to SOC2 Trust Services Criteria,
40
+ helping prevent exposure of security-sensitive information.
41
+ """
42
+
43
+ VERSION = "1.0.0"
44
+ DESCRIPTION = "Detect SOC2-relevant security and compliance patterns"
45
+ AUTHOR = "Tweek"
46
+ REQUIRES_LICENSE = "enterprise"
47
+ TAGS = ["compliance", "soc2", "security", "audit"]
48
+
49
+ def __init__(self, config: Optional[Dict[str, Any]] = None):
50
+ super().__init__(config)
51
+ self._patterns: Optional[List[PatternDefinition]] = None
52
+
53
+ @property
54
+ def name(self) -> str:
55
+ return "soc2"
56
+
57
+ @property
58
+ def scan_direction(self) -> ScanDirection:
59
+ direction = self._config.get("scan_direction", "both")
60
+ return ScanDirection(direction)
61
+
62
+ def get_patterns(self) -> List[PatternDefinition]:
63
+ """Return SOC2 compliance patterns."""
64
+ if self._patterns is not None:
65
+ return self._patterns
66
+
67
+ self._patterns = [
68
+ # =================================================================
69
+ # Access Control (Security Criteria)
70
+ # =================================================================
71
+ PatternDefinition(
72
+ name="access_credentials",
73
+ regex=r"(?i)(?:admin|root|service)\s*(?:password|pwd|pass|credential)[:\s=]+\S+",
74
+ severity=Severity.CRITICAL,
75
+ description="Administrative credentials exposure",
76
+ default_action=ActionType.BLOCK,
77
+ tags=["soc2", "security", "access-control"],
78
+ ),
79
+ PatternDefinition(
80
+ name="api_key_exposure",
81
+ regex=r"(?i)(?:api[_-]?key|auth[_-]?token|bearer[_-]?token|access[_-]?token)[:\s=]+['\"]?[\w-]{20,}['\"]?",
82
+ severity=Severity.CRITICAL,
83
+ description="API key or token exposure",
84
+ default_action=ActionType.REDACT,
85
+ tags=["soc2", "security", "access-control"],
86
+ ),
87
+ PatternDefinition(
88
+ name="connection_string",
89
+ regex=r"(?i)(?:connection[_-]?string|conn[_-]?str)[:\s=]+['\"]?[^'\"]{20,}['\"]?",
90
+ severity=Severity.HIGH,
91
+ description="Database connection string exposure",
92
+ default_action=ActionType.REDACT,
93
+ tags=["soc2", "security", "access-control"],
94
+ ),
95
+ PatternDefinition(
96
+ name="private_key_header",
97
+ regex=r"-----BEGIN\s+(?:RSA\s+)?PRIVATE\s+KEY-----",
98
+ severity=Severity.CRITICAL,
99
+ description="Private key header detected",
100
+ default_action=ActionType.BLOCK,
101
+ tags=["soc2", "security", "cryptography"],
102
+ ),
103
+
104
+ # =================================================================
105
+ # Audit Logging (Security/Processing Integrity)
106
+ # =================================================================
107
+ PatternDefinition(
108
+ name="audit_log_tampering",
109
+ regex=r"(?i)(?:delete|truncate|drop)\s+(?:from\s+)?(?:audit|security|access)[_-]?logs?",
110
+ severity=Severity.CRITICAL,
111
+ description="Audit log tampering attempt",
112
+ default_action=ActionType.BLOCK,
113
+ tags=["soc2", "audit", "integrity"],
114
+ ),
115
+ PatternDefinition(
116
+ name="log_manipulation",
117
+ regex=r"(?i)(?:modify|alter|update)\s+(?:audit|security)[_-]?(?:log|trail|record)",
118
+ severity=Severity.HIGH,
119
+ description="Security log manipulation",
120
+ default_action=ActionType.WARN,
121
+ tags=["soc2", "audit", "integrity"],
122
+ ),
123
+ PatternDefinition(
124
+ name="disable_logging",
125
+ regex=r"(?i)(?:disable|stop|pause|turn\s*off)\s+(?:audit|security|access)[_-]?log",
126
+ severity=Severity.HIGH,
127
+ description="Attempt to disable security logging",
128
+ default_action=ActionType.WARN,
129
+ tags=["soc2", "audit"],
130
+ ),
131
+
132
+ # =================================================================
133
+ # Change Management (Processing Integrity)
134
+ # =================================================================
135
+ PatternDefinition(
136
+ name="unauthorized_change",
137
+ regex=r"(?i)(?:bypass|skip|disable)\s+(?:change[_-]?management|approval|review)",
138
+ severity=Severity.HIGH,
139
+ description="Change management bypass attempt",
140
+ default_action=ActionType.WARN,
141
+ tags=["soc2", "change-management"],
142
+ ),
143
+ PatternDefinition(
144
+ name="production_direct_access",
145
+ regex=r"(?i)direct\s+(?:production|prod)\s+(?:access|modification|change)",
146
+ severity=Severity.MEDIUM,
147
+ description="Direct production access indication",
148
+ default_action=ActionType.WARN,
149
+ tags=["soc2", "change-management"],
150
+ ),
151
+
152
+ # =================================================================
153
+ # Incident Response (Security/Availability)
154
+ # =================================================================
155
+ PatternDefinition(
156
+ name="security_incident",
157
+ regex=r"(?i)(?:security\s+)?(?:incident|breach|compromise|intrusion)\s+(?:report|detected|confirmed)",
158
+ severity=Severity.HIGH,
159
+ description="Security incident indicator",
160
+ default_action=ActionType.WARN,
161
+ tags=["soc2", "incident-response"],
162
+ ),
163
+ PatternDefinition(
164
+ name="data_breach",
165
+ regex=r"(?i)data\s+(?:breach|leak|exposure|exfiltration)",
166
+ severity=Severity.CRITICAL,
167
+ description="Data breach indicator",
168
+ default_action=ActionType.WARN,
169
+ tags=["soc2", "incident-response", "confidentiality"],
170
+ ),
171
+ PatternDefinition(
172
+ name="unauthorized_access",
173
+ regex=r"(?i)unauthorized\s+(?:access|login|entry|attempt)",
174
+ severity=Severity.HIGH,
175
+ description="Unauthorized access indicator",
176
+ default_action=ActionType.WARN,
177
+ tags=["soc2", "incident-response", "security"],
178
+ ),
179
+
180
+ # =================================================================
181
+ # Confidentiality Controls
182
+ # =================================================================
183
+ PatternDefinition(
184
+ name="confidential_marker",
185
+ regex=r"(?i)\[?\s*(?:CONFIDENTIAL|INTERNAL\s+ONLY|RESTRICTED)\s*\]?",
186
+ severity=Severity.MEDIUM,
187
+ description="Confidentiality marking",
188
+ default_action=ActionType.WARN,
189
+ tags=["soc2", "confidentiality"],
190
+ ),
191
+ PatternDefinition(
192
+ name="customer_data_exposure",
193
+ regex=r"(?i)(?:customer|client|user)\s+(?:pii|personal\s+data|sensitive\s+data)",
194
+ severity=Severity.HIGH,
195
+ description="Customer sensitive data reference",
196
+ default_action=ActionType.WARN,
197
+ tags=["soc2", "confidentiality", "privacy"],
198
+ ),
199
+
200
+ # =================================================================
201
+ # Risk Assessment
202
+ # =================================================================
203
+ PatternDefinition(
204
+ name="vulnerability_disclosure",
205
+ regex=r"(?i)(?:vulnerability|cve-\d{4}-\d+|security\s+flaw)\s+(?:in|affecting|found)",
206
+ severity=Severity.MEDIUM,
207
+ description="Vulnerability disclosure",
208
+ default_action=ActionType.WARN,
209
+ tags=["soc2", "risk", "security"],
210
+ ),
211
+ PatternDefinition(
212
+ name="risk_assessment",
213
+ regex=r"(?i)(?:high|critical)\s+risk\s+(?:identified|assessment|finding)",
214
+ severity=Severity.MEDIUM,
215
+ description="High risk assessment finding",
216
+ default_action=ActionType.WARN,
217
+ tags=["soc2", "risk"],
218
+ ),
219
+
220
+ # =================================================================
221
+ # Security Policy
222
+ # =================================================================
223
+ PatternDefinition(
224
+ name="policy_exception",
225
+ regex=r"(?i)(?:security\s+)?policy\s+exception\s+(?:granted|approved|requested)",
226
+ severity=Severity.MEDIUM,
227
+ description="Security policy exception",
228
+ default_action=ActionType.WARN,
229
+ tags=["soc2", "policy"],
230
+ ),
231
+ PatternDefinition(
232
+ name="compliance_violation",
233
+ regex=r"(?i)(?:compliance|regulatory|policy)\s+violation",
234
+ severity=Severity.HIGH,
235
+ description="Compliance violation indicator",
236
+ default_action=ActionType.WARN,
237
+ tags=["soc2", "compliance"],
238
+ ),
239
+ ]
240
+
241
+ return self._patterns
242
+
243
+ def _format_message(
244
+ self,
245
+ findings: List,
246
+ direction: ScanDirection
247
+ ) -> Optional[str]:
248
+ """Format a SOC2-specific message."""
249
+ if not findings:
250
+ return None
251
+
252
+ # Group findings by SOC2 criteria
253
+ security = [f for f in findings if "security" in f.metadata.get("pattern_tags", [])]
254
+ confidentiality = [f for f in findings if "confidentiality" in f.metadata.get("pattern_tags", [])]
255
+ audit = [f for f in findings if "audit" in f.metadata.get("pattern_tags", [])]
256
+
257
+ if direction == ScanDirection.OUTPUT:
258
+ lines = [
259
+ f"WARNING: LLM output contains {len(findings)} SOC2-relevant finding(s).",
260
+ "Review for potential security/compliance concerns:"
261
+ ]
262
+ else:
263
+ lines = [
264
+ f"ALERT: Input contains {len(findings)} SOC2-relevant finding(s).",
265
+ "Verify proper handling according to security policies:"
266
+ ]
267
+
268
+ if security:
269
+ lines.append(f" Security: {len(security)} finding(s)")
270
+ if confidentiality:
271
+ lines.append(f" Confidentiality: {len(confidentiality)} finding(s)")
272
+ if audit:
273
+ lines.append(f" Audit: {len(audit)} finding(s)")
274
+
275
+ return "\n".join(lines)
@@ -0,0 +1,30 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Tweek Tool Detector Plugins
4
+
5
+ Detector plugins identify installed LLM tools and IDEs:
6
+ - Moltbot: AI coding assistant
7
+ - Cursor: AI-powered IDE
8
+ - Continue.dev: VS Code AI extension
9
+ - Copilot: GitHub Copilot
10
+ - Windsurf: Codeium AI IDE
11
+
12
+ Detection helps:
13
+ - Identify proxy conflicts
14
+ - Configure appropriate protection
15
+ - Suggest integration options
16
+ """
17
+
18
+ from tweek.plugins.detectors.moltbot import MoltbotDetector
19
+ from tweek.plugins.detectors.cursor import CursorDetector
20
+ from tweek.plugins.detectors.continue_dev import ContinueDetector
21
+ from tweek.plugins.detectors.copilot import CopilotDetector
22
+ from tweek.plugins.detectors.windsurf import WindsurfDetector
23
+
24
+ __all__ = [
25
+ "MoltbotDetector",
26
+ "CursorDetector",
27
+ "ContinueDetector",
28
+ "CopilotDetector",
29
+ "WindsurfDetector",
30
+ ]