foundry-mcp 0.8.22__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.

Potentially problematic release.


This version of foundry-mcp might be problematic. Click here for more details.

Files changed (153) hide show
  1. foundry_mcp/__init__.py +13 -0
  2. foundry_mcp/cli/__init__.py +67 -0
  3. foundry_mcp/cli/__main__.py +9 -0
  4. foundry_mcp/cli/agent.py +96 -0
  5. foundry_mcp/cli/commands/__init__.py +37 -0
  6. foundry_mcp/cli/commands/cache.py +137 -0
  7. foundry_mcp/cli/commands/dashboard.py +148 -0
  8. foundry_mcp/cli/commands/dev.py +446 -0
  9. foundry_mcp/cli/commands/journal.py +377 -0
  10. foundry_mcp/cli/commands/lifecycle.py +274 -0
  11. foundry_mcp/cli/commands/modify.py +824 -0
  12. foundry_mcp/cli/commands/plan.py +640 -0
  13. foundry_mcp/cli/commands/pr.py +393 -0
  14. foundry_mcp/cli/commands/review.py +667 -0
  15. foundry_mcp/cli/commands/session.py +472 -0
  16. foundry_mcp/cli/commands/specs.py +686 -0
  17. foundry_mcp/cli/commands/tasks.py +807 -0
  18. foundry_mcp/cli/commands/testing.py +676 -0
  19. foundry_mcp/cli/commands/validate.py +982 -0
  20. foundry_mcp/cli/config.py +98 -0
  21. foundry_mcp/cli/context.py +298 -0
  22. foundry_mcp/cli/logging.py +212 -0
  23. foundry_mcp/cli/main.py +44 -0
  24. foundry_mcp/cli/output.py +122 -0
  25. foundry_mcp/cli/registry.py +110 -0
  26. foundry_mcp/cli/resilience.py +178 -0
  27. foundry_mcp/cli/transcript.py +217 -0
  28. foundry_mcp/config.py +1454 -0
  29. foundry_mcp/core/__init__.py +144 -0
  30. foundry_mcp/core/ai_consultation.py +1773 -0
  31. foundry_mcp/core/batch_operations.py +1202 -0
  32. foundry_mcp/core/cache.py +195 -0
  33. foundry_mcp/core/capabilities.py +446 -0
  34. foundry_mcp/core/concurrency.py +898 -0
  35. foundry_mcp/core/context.py +540 -0
  36. foundry_mcp/core/discovery.py +1603 -0
  37. foundry_mcp/core/error_collection.py +728 -0
  38. foundry_mcp/core/error_store.py +592 -0
  39. foundry_mcp/core/health.py +749 -0
  40. foundry_mcp/core/intake.py +933 -0
  41. foundry_mcp/core/journal.py +700 -0
  42. foundry_mcp/core/lifecycle.py +412 -0
  43. foundry_mcp/core/llm_config.py +1376 -0
  44. foundry_mcp/core/llm_patterns.py +510 -0
  45. foundry_mcp/core/llm_provider.py +1569 -0
  46. foundry_mcp/core/logging_config.py +374 -0
  47. foundry_mcp/core/metrics_persistence.py +584 -0
  48. foundry_mcp/core/metrics_registry.py +327 -0
  49. foundry_mcp/core/metrics_store.py +641 -0
  50. foundry_mcp/core/modifications.py +224 -0
  51. foundry_mcp/core/naming.py +146 -0
  52. foundry_mcp/core/observability.py +1216 -0
  53. foundry_mcp/core/otel.py +452 -0
  54. foundry_mcp/core/otel_stubs.py +264 -0
  55. foundry_mcp/core/pagination.py +255 -0
  56. foundry_mcp/core/progress.py +387 -0
  57. foundry_mcp/core/prometheus.py +564 -0
  58. foundry_mcp/core/prompts/__init__.py +464 -0
  59. foundry_mcp/core/prompts/fidelity_review.py +691 -0
  60. foundry_mcp/core/prompts/markdown_plan_review.py +515 -0
  61. foundry_mcp/core/prompts/plan_review.py +627 -0
  62. foundry_mcp/core/providers/__init__.py +237 -0
  63. foundry_mcp/core/providers/base.py +515 -0
  64. foundry_mcp/core/providers/claude.py +472 -0
  65. foundry_mcp/core/providers/codex.py +637 -0
  66. foundry_mcp/core/providers/cursor_agent.py +630 -0
  67. foundry_mcp/core/providers/detectors.py +515 -0
  68. foundry_mcp/core/providers/gemini.py +426 -0
  69. foundry_mcp/core/providers/opencode.py +718 -0
  70. foundry_mcp/core/providers/opencode_wrapper.js +308 -0
  71. foundry_mcp/core/providers/package-lock.json +24 -0
  72. foundry_mcp/core/providers/package.json +25 -0
  73. foundry_mcp/core/providers/registry.py +607 -0
  74. foundry_mcp/core/providers/test_provider.py +171 -0
  75. foundry_mcp/core/providers/validation.py +857 -0
  76. foundry_mcp/core/rate_limit.py +427 -0
  77. foundry_mcp/core/research/__init__.py +68 -0
  78. foundry_mcp/core/research/memory.py +528 -0
  79. foundry_mcp/core/research/models.py +1234 -0
  80. foundry_mcp/core/research/providers/__init__.py +40 -0
  81. foundry_mcp/core/research/providers/base.py +242 -0
  82. foundry_mcp/core/research/providers/google.py +507 -0
  83. foundry_mcp/core/research/providers/perplexity.py +442 -0
  84. foundry_mcp/core/research/providers/semantic_scholar.py +544 -0
  85. foundry_mcp/core/research/providers/tavily.py +383 -0
  86. foundry_mcp/core/research/workflows/__init__.py +25 -0
  87. foundry_mcp/core/research/workflows/base.py +298 -0
  88. foundry_mcp/core/research/workflows/chat.py +271 -0
  89. foundry_mcp/core/research/workflows/consensus.py +539 -0
  90. foundry_mcp/core/research/workflows/deep_research.py +4142 -0
  91. foundry_mcp/core/research/workflows/ideate.py +682 -0
  92. foundry_mcp/core/research/workflows/thinkdeep.py +405 -0
  93. foundry_mcp/core/resilience.py +600 -0
  94. foundry_mcp/core/responses.py +1624 -0
  95. foundry_mcp/core/review.py +366 -0
  96. foundry_mcp/core/security.py +438 -0
  97. foundry_mcp/core/spec.py +4119 -0
  98. foundry_mcp/core/task.py +2463 -0
  99. foundry_mcp/core/testing.py +839 -0
  100. foundry_mcp/core/validation.py +2357 -0
  101. foundry_mcp/dashboard/__init__.py +32 -0
  102. foundry_mcp/dashboard/app.py +119 -0
  103. foundry_mcp/dashboard/components/__init__.py +17 -0
  104. foundry_mcp/dashboard/components/cards.py +88 -0
  105. foundry_mcp/dashboard/components/charts.py +177 -0
  106. foundry_mcp/dashboard/components/filters.py +136 -0
  107. foundry_mcp/dashboard/components/tables.py +195 -0
  108. foundry_mcp/dashboard/data/__init__.py +11 -0
  109. foundry_mcp/dashboard/data/stores.py +433 -0
  110. foundry_mcp/dashboard/launcher.py +300 -0
  111. foundry_mcp/dashboard/views/__init__.py +12 -0
  112. foundry_mcp/dashboard/views/errors.py +217 -0
  113. foundry_mcp/dashboard/views/metrics.py +164 -0
  114. foundry_mcp/dashboard/views/overview.py +96 -0
  115. foundry_mcp/dashboard/views/providers.py +83 -0
  116. foundry_mcp/dashboard/views/sdd_workflow.py +255 -0
  117. foundry_mcp/dashboard/views/tool_usage.py +139 -0
  118. foundry_mcp/prompts/__init__.py +9 -0
  119. foundry_mcp/prompts/workflows.py +525 -0
  120. foundry_mcp/resources/__init__.py +9 -0
  121. foundry_mcp/resources/specs.py +591 -0
  122. foundry_mcp/schemas/__init__.py +38 -0
  123. foundry_mcp/schemas/intake-schema.json +89 -0
  124. foundry_mcp/schemas/sdd-spec-schema.json +414 -0
  125. foundry_mcp/server.py +150 -0
  126. foundry_mcp/tools/__init__.py +10 -0
  127. foundry_mcp/tools/unified/__init__.py +92 -0
  128. foundry_mcp/tools/unified/authoring.py +3620 -0
  129. foundry_mcp/tools/unified/context_helpers.py +98 -0
  130. foundry_mcp/tools/unified/documentation_helpers.py +268 -0
  131. foundry_mcp/tools/unified/environment.py +1341 -0
  132. foundry_mcp/tools/unified/error.py +479 -0
  133. foundry_mcp/tools/unified/health.py +225 -0
  134. foundry_mcp/tools/unified/journal.py +841 -0
  135. foundry_mcp/tools/unified/lifecycle.py +640 -0
  136. foundry_mcp/tools/unified/metrics.py +777 -0
  137. foundry_mcp/tools/unified/plan.py +876 -0
  138. foundry_mcp/tools/unified/pr.py +294 -0
  139. foundry_mcp/tools/unified/provider.py +589 -0
  140. foundry_mcp/tools/unified/research.py +1283 -0
  141. foundry_mcp/tools/unified/review.py +1042 -0
  142. foundry_mcp/tools/unified/review_helpers.py +314 -0
  143. foundry_mcp/tools/unified/router.py +102 -0
  144. foundry_mcp/tools/unified/server.py +565 -0
  145. foundry_mcp/tools/unified/spec.py +1283 -0
  146. foundry_mcp/tools/unified/task.py +3846 -0
  147. foundry_mcp/tools/unified/test.py +431 -0
  148. foundry_mcp/tools/unified/verification.py +520 -0
  149. foundry_mcp-0.8.22.dist-info/METADATA +344 -0
  150. foundry_mcp-0.8.22.dist-info/RECORD +153 -0
  151. foundry_mcp-0.8.22.dist-info/WHEEL +4 -0
  152. foundry_mcp-0.8.22.dist-info/entry_points.txt +3 -0
  153. foundry_mcp-0.8.22.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,438 @@
1
+ """
2
+ Security utilities for foundry-mcp.
3
+
4
+ Provides input validation, size limits, and prompt injection defense
5
+ for MCP tools. See docs/mcp_best_practices/04-validation-input-hygiene.md
6
+ and docs/mcp_best_practices/08-security-trust-boundaries.md for guidance.
7
+ """
8
+
9
+ import logging
10
+ import re
11
+ from dataclasses import dataclass
12
+ from typing import Any, Final, Optional, Tuple
13
+
14
+ logger = logging.getLogger(__name__)
15
+
16
+ # =============================================================================
17
+ # Input Size Limits
18
+ # =============================================================================
19
+ # These constants define maximum sizes for various input types to prevent
20
+ # resource exhaustion and denial-of-service attacks. Adjust based on your
21
+ # specific requirements, but be conservative.
22
+
23
+ MAX_INPUT_SIZE: Final[int] = 100_000
24
+ """Maximum total input payload size in bytes (100KB).
25
+
26
+ Use this to validate the overall size of request payloads before processing.
27
+ Prevents memory exhaustion from oversized requests.
28
+ """
29
+
30
+ MAX_ARRAY_LENGTH: Final[int] = 1_000
31
+ """Maximum number of items in array/list inputs.
32
+
33
+ Use this to limit iteration counts and prevent algorithmic complexity attacks.
34
+ Arrays larger than this should be paginated or streamed.
35
+ """
36
+
37
+ MAX_STRING_LENGTH: Final[int] = 10_000
38
+ """Maximum length for individual string fields (10K characters).
39
+
40
+ Use this for text fields like descriptions, content, etc.
41
+ Longer content should use dedicated file/blob handling.
42
+ """
43
+
44
+ MAX_NESTED_DEPTH: Final[int] = 10
45
+ """Maximum nesting depth for JSON structures.
46
+
47
+ Prevents stack overflow from deeply nested payloads.
48
+ Most legitimate use cases require < 5 levels of nesting.
49
+ """
50
+
51
+ MAX_FIELD_COUNT: Final[int] = 100
52
+ """Maximum number of fields in an object/dict.
53
+
54
+ Prevents resource exhaustion from objects with excessive properties.
55
+ """
56
+
57
+ # =============================================================================
58
+ # Prompt Injection Detection Patterns
59
+ # =============================================================================
60
+ # These regex patterns detect common prompt injection attempts in LLM-generated
61
+ # input. MCP tools receiving untrusted input should check against these patterns.
62
+ # See docs/mcp_best_practices/08-security-trust-boundaries.md for details.
63
+
64
+ INJECTION_PATTERNS: Final[list[str]] = [
65
+ # Instruction override attempts
66
+ r"ignore\s+(all\s+)?(previous|prior)\s+(instructions?|prompts?)",
67
+ r"disregard\s+(all\s+)?(previous|prior|above)",
68
+ r"forget\s+(everything|all)\s+(above|before)",
69
+ r"new\s+instructions?\s*:",
70
+
71
+ # System prompt injection
72
+ r"system\s*:\s*",
73
+ r"<\s*system\s*>",
74
+
75
+ # Special tokens (model-specific)
76
+ r"<\|.*?\|>", # OpenAI-style special tokens
77
+ r"\[INST\]|\[/INST\]", # Llama instruction markers
78
+ r"<\|im_start\|>|<\|im_end\|>", # ChatML markers
79
+ r"<<SYS>>|<</SYS>>", # Llama system markers
80
+
81
+ # Code block injection attempts
82
+ r"```system",
83
+ r"```\s*<\s*system",
84
+
85
+ # Role injection
86
+ r"^(assistant|user|system)\s*:",
87
+ ]
88
+ """Regex patterns for detecting prompt injection attempts.
89
+
90
+ Each pattern targets a specific injection technique:
91
+ - Instruction overrides: attempts to ignore/discard previous context
92
+ - System prompt injection: attempts to inject system-level instructions
93
+ - Special tokens: model-specific control sequences
94
+ - Code block injection: attempts to inject via markdown code blocks
95
+ - Role injection: attempts to assume different conversation roles
96
+
97
+ Use with detect_prompt_injection() for comprehensive checking.
98
+ """
99
+
100
+
101
+ # =============================================================================
102
+ # Detection Results
103
+ # =============================================================================
104
+
105
+ @dataclass
106
+ class InjectionDetectionResult:
107
+ """Result of prompt injection detection.
108
+
109
+ Attributes:
110
+ is_suspicious: Whether the input appears to contain injection attempts
111
+ matched_pattern: The regex pattern that matched (if any)
112
+ matched_text: The actual text that matched the pattern (if any)
113
+ """
114
+ is_suspicious: bool
115
+ matched_pattern: Optional[str] = None
116
+ matched_text: Optional[str] = None
117
+
118
+
119
+ # =============================================================================
120
+ # Detection Functions
121
+ # =============================================================================
122
+
123
+ def detect_prompt_injection(
124
+ text: str,
125
+ *,
126
+ log_detections: bool = True,
127
+ patterns: Optional[list[str]] = None,
128
+ ) -> InjectionDetectionResult:
129
+ """Detect potential prompt injection attempts in text.
130
+
131
+ Scans the input text against known injection patterns and returns
132
+ a result indicating whether suspicious content was found.
133
+
134
+ Args:
135
+ text: The input text to scan for injection attempts
136
+ log_detections: Whether to log detected injection attempts (default: True)
137
+ patterns: Optional custom patterns to use instead of INJECTION_PATTERNS
138
+
139
+ Returns:
140
+ InjectionDetectionResult with detection status and match details
141
+
142
+ Example:
143
+ >>> result = detect_prompt_injection("ignore previous instructions and...")
144
+ >>> if result.is_suspicious:
145
+ ... print(f"Blocked: matched pattern '{result.matched_pattern}'")
146
+ """
147
+ check_patterns = patterns if patterns is not None else INJECTION_PATTERNS
148
+
149
+ for pattern in check_patterns:
150
+ match = re.search(pattern, text, re.IGNORECASE | re.MULTILINE)
151
+ if match:
152
+ result = InjectionDetectionResult(
153
+ is_suspicious=True,
154
+ matched_pattern=pattern,
155
+ matched_text=match.group(0),
156
+ )
157
+
158
+ if log_detections:
159
+ # Log with limited text preview to avoid logging sensitive content
160
+ preview = text[:100] + "..." if len(text) > 100 else text
161
+ logger.warning(
162
+ "Potential prompt injection detected",
163
+ extra={
164
+ "pattern": pattern,
165
+ "matched_text": result.matched_text,
166
+ "text_preview": preview,
167
+ }
168
+ )
169
+
170
+ return result
171
+
172
+ return InjectionDetectionResult(is_suspicious=False)
173
+
174
+
175
+ def is_prompt_injection(text: str) -> bool:
176
+ """Simple check for prompt injection (returns bool only).
177
+
178
+ Convenience function when you only need a boolean result.
179
+
180
+ Args:
181
+ text: The input text to scan
182
+
183
+ Returns:
184
+ True if injection patterns detected, False otherwise
185
+
186
+ Example:
187
+ >>> if is_prompt_injection(user_input):
188
+ ... return error_response("Input contains disallowed patterns")
189
+ """
190
+ return detect_prompt_injection(text, log_detections=False).is_suspicious
191
+
192
+
193
+ # =============================================================================
194
+ # Size Validation Functions
195
+ # =============================================================================
196
+
197
+ @dataclass
198
+ class SizeValidationResult:
199
+ """Result of input size validation.
200
+
201
+ Attributes:
202
+ is_valid: Whether all size checks passed
203
+ violations: List of (field_name, violation_message) tuples
204
+ """
205
+ is_valid: bool
206
+ violations: list[Tuple[str, str]]
207
+
208
+
209
+ def validate_size(
210
+ value: Any,
211
+ field_name: str = "input",
212
+ *,
213
+ max_size: Optional[int] = None,
214
+ max_length: Optional[int] = None,
215
+ max_string_length: Optional[int] = None,
216
+ ) -> SizeValidationResult:
217
+ """Validate size constraints on a value.
218
+
219
+ Args:
220
+ value: The value to validate
221
+ field_name: Name of the field (for error messages)
222
+ max_size: Maximum byte size for serialized value (default: MAX_INPUT_SIZE)
223
+ max_length: Maximum length for arrays/lists (default: MAX_ARRAY_LENGTH)
224
+ max_string_length: Maximum length for strings (default: MAX_STRING_LENGTH)
225
+
226
+ Returns:
227
+ SizeValidationResult with validation status and any violations
228
+ """
229
+ import json
230
+
231
+ violations = []
232
+
233
+ # Check serialized size
234
+ effective_max_size = max_size if max_size is not None else MAX_INPUT_SIZE
235
+ try:
236
+ serialized = json.dumps(value) if not isinstance(value, str) else value
237
+ if len(serialized.encode('utf-8')) > effective_max_size:
238
+ violations.append((
239
+ field_name,
240
+ f"Exceeds maximum size ({effective_max_size} bytes)"
241
+ ))
242
+ except (TypeError, ValueError):
243
+ pass # Can't serialize, skip size check
244
+
245
+ # Check array length
246
+ effective_max_length = max_length if max_length is not None else MAX_ARRAY_LENGTH
247
+ if isinstance(value, (list, tuple)):
248
+ if len(value) > effective_max_length:
249
+ violations.append((
250
+ field_name,
251
+ f"Array exceeds maximum length ({effective_max_length} items)"
252
+ ))
253
+
254
+ # Check string length
255
+ effective_max_string = max_string_length if max_string_length is not None else MAX_STRING_LENGTH
256
+ if isinstance(value, str):
257
+ if len(value) > effective_max_string:
258
+ violations.append((
259
+ field_name,
260
+ f"String exceeds maximum length ({effective_max_string} characters)"
261
+ ))
262
+
263
+ return SizeValidationResult(
264
+ is_valid=len(violations) == 0,
265
+ violations=violations,
266
+ )
267
+
268
+
269
+ # =============================================================================
270
+ # Validation Decorators
271
+ # =============================================================================
272
+
273
+ def validate_input_size(
274
+ *,
275
+ max_size: Optional[int] = None,
276
+ max_array_length: Optional[int] = None,
277
+ max_string_length: Optional[int] = None,
278
+ check_injection: bool = False,
279
+ ):
280
+ """Decorator to validate input size limits on tool parameters.
281
+
282
+ Validates all string and collection parameters against size limits
283
+ before the function executes. Returns an error response if validation fails.
284
+
285
+ Args:
286
+ max_size: Maximum total input size in bytes (default: MAX_INPUT_SIZE)
287
+ max_array_length: Maximum array/list length (default: MAX_ARRAY_LENGTH)
288
+ max_string_length: Maximum string length (default: MAX_STRING_LENGTH)
289
+ check_injection: Also check for prompt injection patterns (default: False)
290
+
291
+ Returns:
292
+ Decorator function
293
+
294
+ Example:
295
+ @mcp.tool()
296
+ @validate_input_size(max_string_length=5000, check_injection=True)
297
+ def process_text(text: str, items: list) -> dict:
298
+ # Parameters are validated before this runs
299
+ return {"result": process(text)}
300
+
301
+ Note:
302
+ This decorator should be applied AFTER @mcp.tool() to ensure
303
+ validation runs before the tool handler.
304
+ """
305
+ import functools
306
+ from dataclasses import asdict
307
+
308
+ def decorator(func):
309
+ @functools.wraps(func)
310
+ def wrapper(*args, **kwargs):
311
+ all_violations = []
312
+
313
+ # Validate keyword arguments
314
+ for name, value in kwargs.items():
315
+ result = validate_size(
316
+ value,
317
+ field_name=name,
318
+ max_size=max_size,
319
+ max_length=max_array_length,
320
+ max_string_length=max_string_length,
321
+ )
322
+ all_violations.extend(result.violations)
323
+
324
+ # Check for injection if enabled and value is string
325
+ if check_injection and isinstance(value, str):
326
+ injection_result = detect_prompt_injection(value)
327
+ if injection_result.is_suspicious:
328
+ all_violations.append((
329
+ name,
330
+ f"Contains disallowed patterns: {injection_result.matched_text}"
331
+ ))
332
+
333
+ if all_violations:
334
+ # Import here to avoid circular dependency
335
+ try:
336
+ from foundry_mcp.core.responses import error_response
337
+ return asdict(error_response(
338
+ "Input validation failed",
339
+ error_code="VALIDATION_ERROR",
340
+ details={
341
+ "validation_errors": [
342
+ {"field": field, "message": msg}
343
+ for field, msg in all_violations
344
+ ]
345
+ }
346
+ ))
347
+ except ImportError:
348
+ # Fallback if responses module not available
349
+ return {
350
+ "success": False,
351
+ "error": "Input validation failed",
352
+ "data": {
353
+ "validation_errors": [
354
+ {"field": field, "message": msg}
355
+ for field, msg in all_violations
356
+ ]
357
+ }
358
+ }
359
+
360
+ return func(*args, **kwargs)
361
+
362
+ # Handle async functions
363
+ @functools.wraps(func)
364
+ async def async_wrapper(*args, **kwargs):
365
+ all_violations = []
366
+
367
+ for name, value in kwargs.items():
368
+ result = validate_size(
369
+ value,
370
+ field_name=name,
371
+ max_size=max_size,
372
+ max_length=max_array_length,
373
+ max_string_length=max_string_length,
374
+ )
375
+ all_violations.extend(result.violations)
376
+
377
+ if check_injection and isinstance(value, str):
378
+ injection_result = detect_prompt_injection(value)
379
+ if injection_result.is_suspicious:
380
+ all_violations.append((
381
+ name,
382
+ f"Contains disallowed patterns: {injection_result.matched_text}"
383
+ ))
384
+
385
+ if all_violations:
386
+ try:
387
+ from foundry_mcp.core.responses import error_response
388
+ return asdict(error_response(
389
+ "Input validation failed",
390
+ error_code="VALIDATION_ERROR",
391
+ details={
392
+ "validation_errors": [
393
+ {"field": field, "message": msg}
394
+ for field, msg in all_violations
395
+ ]
396
+ }
397
+ ))
398
+ except ImportError:
399
+ return {
400
+ "success": False,
401
+ "error": "Input validation failed",
402
+ "data": {
403
+ "validation_errors": [
404
+ {"field": field, "message": msg}
405
+ for field, msg in all_violations
406
+ ]
407
+ }
408
+ }
409
+
410
+ return await func(*args, **kwargs)
411
+
412
+ import asyncio
413
+ if asyncio.iscoroutinefunction(func):
414
+ return async_wrapper
415
+ return wrapper
416
+
417
+ return decorator
418
+
419
+
420
+ # Export all constants and functions
421
+ __all__ = [
422
+ # Constants
423
+ "MAX_INPUT_SIZE",
424
+ "MAX_ARRAY_LENGTH",
425
+ "MAX_STRING_LENGTH",
426
+ "MAX_NESTED_DEPTH",
427
+ "MAX_FIELD_COUNT",
428
+ "INJECTION_PATTERNS",
429
+ # Types
430
+ "InjectionDetectionResult",
431
+ "SizeValidationResult",
432
+ # Functions
433
+ "detect_prompt_injection",
434
+ "is_prompt_injection",
435
+ "validate_size",
436
+ # Decorators
437
+ "validate_input_size",
438
+ ]