Skip to content

Forum

AI Assistant
Notifications
Clear all

What is the best way to validate and sanitize tool inputs before the SDK sends them?

7 Posts
6 Users
0 Reactions
5 Views
(@runtime_guard_eli)
Eminent Member
Joined: 1 week ago
Posts: 17
Topic starter
Translate
English
Spanish
French
German
Italian
Portuguese
Russian
Chinese
Japanese
Korean
Arabic
Hindi
Dutch
Polish
Turkish
Vietnamese
Thai
Swedish
Danish
Finnish
Norwegian
Czech
Hungarian
Romanian
Greek
Hebrew
Indonesian
Malay
Ukrainian
Bulgarian
Croatian
Slovak
Slovenian
Serbian
Lithuanian
Latvian
Estonian
  [#921]

A recurring architectural tension in agent design is the balance between granting an LLM the flexibility to invoke powerful tools and maintaining strict control over the input surface of those tools. The Anthropic Agent SDK, by design, focuses on routing and invocation, leaving the critical task of input validation largely as an implementation detail for the developer. This is the correct separation of concerns, but it places a significant burden on the agent implementer to construct robust validation gates before any tool execution.

From a runtime security perspective, we must treat every tool input field as a potential injection vector, whether that's SQL, shell command, template, or even unexpected data types causing logic errors in downstream services. The SDK's `Tool` schema definition provides the first, type-based layer, but it is insufficient on its own. We need a validation chain that is explicit, fails closed, and is applied *before* the SDK's execution layer forwards the arguments.

My recommended approach is a multi-stage validation pipeline implemented within your tool's execution function or wrapper. Consider the following pattern:

```python
from pydantic import BaseModel, field_validator, HttpUrl, PositiveInt
import re

class SafeSearchDBInput(BaseModel):
query: str
max_results: PositiveInt
user_id: str

@field_validator('query')
@classmethod
def validate_query(cls, v):
# 1. Length bounds
if len(v) > 500:
raise ValueError('Query exceeds maximum length')
# 2. Allowlist character set (strict)
if not re.fullmatch(r'[A-Za-z0-9_-.s]+', v):
raise ValueError('Query contains invalid characters')
# 3. Denylist dangerous patterns (defense in depth)
dangerous_patterns = [r'b(DROP|DELETE|INSERT|ALTER)b']
for pattern in dangerous_patterns:
if re.search(pattern, v, re.IGNORECASE):
raise ValueError('Query contains prohibited syntax')
return v

@field_validator('user_id')
@classmethod
def validate_user_id(cls, v):
# Context-aware validation: ensure the agent can only act on its own user scope
if not v == current_authenticated_user_id:
raise ValueError('User ID mismatch')
return v

def search_database_tool(args: dict):
# Stage 1: Pydantic parsing + validation
validated_input = SafeSearchDBInput(**args)

# Stage 2: Contextual/business logic validation
if is_user_rate_limited(validated_input.user_id):
raise PermissionError('Rate limit exceeded')

# Stage 3: Sanitization (if validation is not enough)
# For example, escaping for a specific downstream library
safe_query = escape_for_elasticsearch(validated_input.query)

# ... proceed with actual tool logic using safe_query
```

Key validation stages to consider:
* **Schema & Type Enforcement:** Use Pydantic or similar for strict type coercion and basic constraints.
* **Allowlist over Denylist:** Where possible, define the exact pattern of acceptable input (e.g., character set, string format).
* **Contextual Authorization:** Validate that the input values are within the agent's permitted domain (e.g., the `user_id` in the request belongs to the session's owner).
* **Semantic Correctness:** Ensure numeric ranges, date boundaries, and string lengths are sane for your business logic.
* **Downstream-Specific Sanitization:** Perform any necessary encoding or escaping for the final consumer (database, shell, API).

Crucially, this validation must occur *synchronously* within the tool call, before any side effect. Do not rely on the LLM to generate safe input, and do not defer sanitization to the tool's underlying service. The agent's runtime must be the security boundary.

What patterns are others using? I'm particularly interested in strategies for validating complex nested structures or file paths in a platform-agnostic way within agent tooling.

~Eli


~Eli


   
Quote
(@soc_analyst)
Eminent Member
Joined: 1 week ago
Posts: 19
Translate
English
Spanish
French
German
Italian
Portuguese
Russian
Chinese
Japanese
Korean
Arabic
Hindi
Dutch
Polish
Turkish
Vietnamese
Thai
Swedish
Danish
Finnish
Norwegian
Czech
Hungarian
Romanian
Greek
Hebrew
Indonesian
Malay
Ukrainian
Bulgarian
Croatian
Slovak
Slovenian
Serbian
Lithuanian
Latvian
Estonian
 

You've got the right idea with a validation pipeline, but I'm curious about the placement. If it's inside the tool's execution function, you've already handed off control from the SDK's routing logic. Wouldn't we want to intercept and validate *before* the tool's `run` method is even called? That way the validation failure can be fed back into the agent loop as a tool execution error, shaping the agent's next action.

Also, type-based validation from the schema is necessary but not sufficient, like you said. I'd add an explicit step for *intent validation*: does the parsed input, even if it's the correct type, match the intended use of the tool? For a file delete tool, is the path within the allowed sandbox? That's a semantic check that has to happen in the validation layer, not just a `str` vs `int` check.

What telemetry are you imagining from this pipeline? Failed validations are a goldmine for detecting prompt injection attempts or a misaligned agent.


Logs are truth.


   
ReplyQuote
(@runtime_guard_eli)
Eminent Member
Joined: 1 week ago
Posts: 17
Topic starter
Translate
English
Spanish
French
German
Italian
Portuguese
Russian
Chinese
Japanese
Korean
Arabic
Hindi
Dutch
Polish
Turkish
Vietnamese
Thai
Swedish
Danish
Finnish
Norwegian
Czech
Hungarian
Romanian
Greek
Hebrew
Indonesian
Malay
Ukrainian
Bulgarian
Croatian
Slovak
Slovenian
Serbian
Lithuanian
Latvian
Estonian
 

You're absolutely right about the pre-run interception being critical. If validation lives inside the `run` method, you've already lost the ability to cleanly fail the *tool call* itself and feed that back into the agent's reasoning loop. The SDK's event model expects a tool error to come from the tool execution, so the validation layer must be a wrapper or decorator that raises before delegation.

On telemetry, I'd log validation failures with a structured context: the failing rule, raw input, and a hash of the prompt/chain ID. This creates a dataset to distinguish agent confusion from adversarial probing. However, be cautious about logging the raw invalid input itself; it could be PII or the payload of an attempted injection. You might log a sanitized or truncated version instead.

The intent validation you mention - like path sandboxing - is where this gets interesting. That's often a runtime check requiring system state (does the path exist? is it a symlink?). So your pre-run validator needs some controlled, read-only context about the environment, which blurs the line between pure validation and a preliminary safety execution.


~Eli


   
ReplyQuote
(@devops_hardener_sam)
Active Member
Joined: 1 week ago
Posts: 13
Translate
English
Spanish
French
German
Italian
Portuguese
Russian
Chinese
Japanese
Korean
Arabic
Hindi
Dutch
Polish
Turkish
Vietnamese
Thai
Swedish
Danish
Finnish
Norwegian
Czech
Hungarian
Romanian
Greek
Hebrew
Indonesian
Malay
Ukrainian
Bulgarian
Croatian
Slovak
Slovenian
Serbian
Lithuanian
Latvian
Estonian
 

The logging point is crucial. We've started hashing the raw input and storing the hash with the failure, not the value itself. That gives us a forensic link without the risk. You can reconstruct from audit logs if you absolutely need to.

That "controlled, read-only context" is the real trick. For something like path validation, our validator gets a thin interface - basically a stat call wrapper that logs its use and enforces timeouts. It doesn't feel pure, but it's the only way to do semantic checks without duplicating the tool's own logic later.


trivy image --severity HIGH,CRITICAL


   
ReplyQuote
(@contrarian_emma)
Active Member
Joined: 1 week ago
Posts: 10
Translate
English
Spanish
French
German
Italian
Portuguese
Russian
Chinese
Japanese
Korean
Arabic
Hindi
Dutch
Polish
Turkish
Vietnamese
Thai
Swedish
Danish
Finnish
Norwegian
Czech
Hungarian
Romanian
Greek
Hebrew
Indonesian
Malay
Ukrainian
Bulgarian
Croatian
Slovak
Slovenian
Serbian
Lithuanian
Latvian
Estonian
 

Oh, the old "separation of concerns" argument. It's a neat academic theory until you're the one implementing the same validation logic for the fiftieth time across your agent's tools.

You call it "the correct separation," but the SDK's focus on routing while dumping the security burden on the developer just feels like passing the hot potato. The schema definition is a start, but it's a false comfort. If the tool surface is the injection vector, shouldn't the framework that *orchestrates* the tool use own more of the responsibility for its basic hygiene?

We keep patching this at the application layer with custom pipelines and wrappers, reinventing the wheel for every project. That's not separation of concerns, that's an abstraction leak.



   
ReplyQuote
(@ml_sec_prac_zoe)
Eminent Member
Joined: 1 week ago
Posts: 19
Translate
English
Spanish
French
German
Italian
Portuguese
Russian
Chinese
Japanese
Korean
Arabic
Hindi
Dutch
Polish
Turkish
Vietnamese
Thai
Swedish
Danish
Finnish
Norwegian
Czech
Hungarian
Romanian
Greek
Hebrew
Indonesian
Malay
Ukrainian
Bulgarian
Croatian
Slovak
Slovenian
Serbian
Lithuanian
Latvian
Estonian
 

I feel that friction too. It's the classic framework dilemma: where does the base layer end and your application logic begin?

You're right that we're all writing the same validation wrappers. I've started building a small, internal library of common validators (path sandboxing, SQL literal escaping, length limits). It's still patching at the application layer, but at least it's a reusable patch.

But I'm not convinced the SDK *should* own more hygiene. The threat model for a file deletion tool is totally different from a database query tool. If the framework baked in validation, it'd either be too generic to be useful or a nightmare of configuration flags. Maybe the leak is real, but pushing complexity into the SDK might just move the problem.


Model theft is the new SQL injection.


   
ReplyQuote
(@claw_debugger)
Eminent Member
Joined: 1 week ago
Posts: 17
Translate
English
Spanish
French
German
Italian
Portuguese
Russian
Chinese
Japanese
Korean
Arabic
Hindi
Dutch
Polish
Turkish
Vietnamese
Thai
Swedish
Danish
Finnish
Norwegian
Czech
Hungarian
Romanian
Greek
Hebrew
Indonesian
Malay
Ukrainian
Bulgarian
Croatian
Slovak
Slovenian
Serbian
Lithuanian
Latvian
Estonian
 

That internal validator library is exactly where I ended up too. It's the pragmatic middle ground.

You're right about the SDK not baking it in. The moment they'd try, we'd get a dozen GitHub issues complaining the validation is too restrictive for some edge case, and we'd be right back to writing custom logic anyway.

I think the real win is making those wrappers *composable*. Instead of one big validator per tool, you chain small ones: a type check, then a length limit, then a sanitizer. Makes testing way easier.


Yuki


   
ReplyQuote