MCPcopy Create free account
hub / github.com/Emmimal/control-layer / InputGuard

Class InputGuard

control_layer.py:235–290  ·  view source on GitHub ↗

Validates and sanitizes user input before it reaches the prompt builder. Three checks, in order: 1. Empty / whitespace-only input. 2. Character length exceeds budget. 3. Known injection pattern match (20 patterns, tested). Returns a ValidationResult. Never raises.

Source from the content-addressed store, hash-verified

233
234
235class InputGuard:
236 """
237 Validates and sanitizes user input before it reaches the prompt builder.
238
239 Three checks, in order:
240 1. Empty / whitespace-only input.
241 2. Character length exceeds budget.
242 3. Known injection pattern match (20 patterns, tested).
243
244 Returns a ValidationResult. Never raises.
245 """
246
247 def __init__(self, max_input_chars: int = 2000):
248 self.max_input_chars = max_input_chars
249 self._patterns = [
250 re.compile(p, re.IGNORECASE | re.DOTALL)
251 for p in INJECTION_PATTERNS
252 ]
253
254 def validate(self, user_input: str) -> ValidationResult:
255 if not user_input or not user_input.strip():
256 return ValidationResult(
257 passed=False,
258 failure_mode=FailureMode.CONSTRAINT_VIOLATION,
259 message="Input is empty.",
260 score=0.0,
261 )
262
263 if len(user_input) > self.max_input_chars:
264 return ValidationResult(
265 passed=False,
266 failure_mode=FailureMode.TOKEN_OVERFLOW,
267 message=(
268 f"Input exceeds {self.max_input_chars} chars "
269 f"({len(user_input)} received)."
270 ),
271 score=0.0,
272 )
273
274 for pattern in self._patterns:
275 if pattern.search(user_input):
276 return ValidationResult(
277 passed=False,
278 failure_mode=FailureMode.PROMPT_INJECTION,
279 message=f"Injection pattern detected: '{pattern.pattern[:60]}'",
280 score=0.0,
281 )
282
283 return ValidationResult(passed=True, score=1.0)
284
285 def sanitize(self, user_input: str) -> str:
286 text = user_input.strip()
287 text = re.sub(r"\s+", " ", text)
288 # Strip null bytes and non-printable control chars (except newline/tab)
289 text = re.sub(r"[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]", "", text)
290 return text
291
292

Callers 2

setup_methodMethod · 0.90
__init__Method · 0.85

Calls

no outgoing calls

Tested by 1

setup_methodMethod · 0.72