-
Notifications
You must be signed in to change notification settings - Fork 264
Description
Title: Implement input validation for non-admin API endpoints
Depends on: #339
Description:
Following the implementation of input validation for admin endpoints (#339), this ticket extends the same validation framework to all remaining API endpoints. This ensures consistent data integrity and proper handling of user input across the entire API surface.
API Endpoints Requiring Validation:
1. Server Management APIs (/servers/*)
# app/main.py - Server APIs
- [ ] POST /servers
- [ ] PUT /servers/{server_id}
- [ ] POST /servers/{server_id}/message
Fields requiring validation:
name
: Server names displayed in UIdescription
: Server descriptionsicon
: Image URLsassociatedTools
,associatedResources
,associatedPrompts
: Reference lists
2. Tool Management APIs (/tools/*)
# app/main.py - Tool APIs
- [ ] POST /tools
- [ ] PUT /tools/{tool_id}
Fields requiring validation:
- Same as admin endpoints - reuse
SecureToolCreate
andSecureToolUpdate
models
3. Resource Management APIs (/resources/*)
# app/main.py - Resource APIs
- [ ] POST /resources
- [ ] PUT /resources/{uri:path}
Fields requiring validation:
- Same as admin endpoints - reuse
SecureResourceCreate
andSecureResourceUpdate
models
4. Prompt Management APIs (/prompts/*)
# app/main.py - Prompt APIs
- [ ] POST /prompts
- [ ] POST /prompts/{name} (with args)
- [ ] PUT /prompts/{name}
Fields requiring validation:
- Same as admin endpoints - reuse
SecurePromptCreate
andSecurePromptUpdate
models - Additional validation for
args
parameter in POST /prompts/{name}
5. Gateway Management APIs (/gateways/*)
# app/main.py - Gateway APIs
- [ ] POST /gateways
- [ ] PUT /gateways/{gateway_id}
Fields requiring validation:
- Same as admin endpoints - reuse
SecureGatewayCreate
andSecureGatewayUpdate
models
6. Root Management APIs (/roots/*)
# app/main.py - Root APIs
- [ ] POST /roots
Fields requiring validation:
uri
: Root URI identifiername
: Root name for display
7. RPC Endpoint
# app/main.py - RPC endpoint
- [ ] POST /rpc
Fields requiring validation:
- Reuse
SecureRPCRequest
model from Issue 1 - Additional validation for method-specific parameters
8. Protocol APIs (/protocol/*)
# app/main.py - Protocol APIs
- [ ] POST /protocol/initialize
- [ ] POST /protocol/notifications
- [ ] POST /protocol/completion/complete
- [ ] POST /protocol/sampling/createMessage
Fields requiring validation:
- Protocol-specific request bodies
- Message content that may be displayed
9. Utility Endpoints
# app/main.py - Utility routes
- [ ] POST /message (SSE messages)
- [ ] POST /logging/setLevel
Fields requiring validation:
- Message content for SSE
- Log level enumeration
Implementation Tasks:
A. Apply Existing Validators (from Issue 1):
# Reuse validators from app/core/validators.py
from app.core.validators import SecurityValidator
# Apply to non-admin schemas
from app.schemas import (
ToolCreate, ToolUpdate,
ResourceCreate, ResourceUpdate,
PromptCreate, PromptUpdate,
GatewayCreate, GatewayUpdate,
ServerCreate, ServerUpdate
)
B. Create Additional Models:
# app/schemas/roots.py
class SecureRootCreate(BaseModel):
model_config = ConfigDict(str_strip_whitespace=True)
uri: str
name: str
@field_validator('uri')
@classmethod
def validate_uri(cls, v: str) -> str:
"""Validate root URI format"""
return SecurityValidator.validate_identifier(v, "Root URI")
@field_validator('name')
@classmethod
def validate_name(cls, v: str) -> str:
"""Validate root name"""
return SecurityValidator.validate_name(v, "Root name")
# app/schemas/protocol.py
class SecureInitializeRequest(BaseModel):
model_config = ConfigDict(str_strip_whitespace=True)
protocol_version: str
capabilities: Dict[str, Any] = {}
client_info: Dict[str, Any] = {}
@field_validator('protocol_version')
@classmethod
def validate_version(cls, v: str) -> str:
"""Validate protocol version format"""
if not re.match(r'^\d+\.\d+\.\d+$', v):
raise ValueError("Invalid protocol version format")
return v
@field_validator('capabilities', 'client_info')
@classmethod
def validate_json_fields(cls, v: Dict[str, Any]) -> Dict[str, Any]:
"""Validate JSON structure depth"""
SecurityValidator.validate_json_depth(v)
return v
class SecurePromptArgs(BaseModel):
"""Validation for prompt arguments in POST /prompts/{name}"""
model_config = ConfigDict(str_strip_whitespace=True)
args: Dict[str, str] = {}
@field_validator('args')
@classmethod
def validate_args(cls, v: Dict[str, str]) -> Dict[str, str]:
"""Validate prompt arguments"""
for key, value in v.items():
# Validate keys
if not re.match(r'^[a-zA-Z0-9_]+$', key):
raise ValueError(f"Invalid argument key: {key}")
# Sanitize values that might be displayed
v[key] = SecurityValidator.sanitize_display_text(value, f"Argument {key}")
return v
C. Update Route Handlers:
# app/main.py - Apply secure models
# Server routes
@server_router.post("", response_model=ServerRead)
async def create_server(
server: SecureServerCreate, # Changed from ServerCreate
db: Session = Depends(get_db),
user: str = Depends(require_auth),
) -> ServerRead:
# Input is now validated
pass
# Tool routes
@tool_router.post("", response_model=ToolRead)
async def create_tool(
tool: SecureToolCreate, # Changed from ToolCreate
db: Session = Depends(get_db),
user: str = Depends(require_auth)
) -> ToolRead:
# Input is now validated
pass
# Similar changes for all other endpoints...
D. Special Handling for Dynamic Routes:
# Validate path parameters
@resource_router.get("/{uri:path}")
async def read_resource(
uri: str,
db: Session = Depends(get_db),
user: str = Depends(require_auth)
) -> ResourceContent:
# Validate URI parameter
try:
validated_uri = SecurityValidator.validate_identifier(uri, "Resource URI")
except ValueError as e:
raise HTTPException(status_code=400, detail=str(e))
# Continue with validated URI
pass
# RPC endpoint with dynamic method validation
@utility_router.post("/rpc")
async def handle_rpc(
request: Request,
db: Session = Depends(get_db),
user: str = Depends(require_auth)
):
body = await request.json()
# Validate as SecureRPCRequest first
try:
rpc_request = SecureRPCRequest(**body)
except ValidationError as e:
raise HTTPException(status_code=422, detail=e.errors())
# Method-specific validation
if rpc_request.method == "prompts/get":
# Additional validation for prompt-specific params
pass
Testing Requirements:
- Test each endpoint with the same payloads as admin endpoints
- Test protocol-specific endpoints with malformed requests
- Test RPC endpoint with various method calls
- Test WebSocket endpoints with invalid JSON
- Verify path parameter validation
- Test with MCP protocol compliance
Acceptance Criteria:
- All non-admin endpoints validate input using the same framework as admin endpoints
- Reuse validation models from Issue 1 where applicable
- Path parameters and query parameters are validated
- Protocol compliance is maintained (MCP spec)
- WebSocket and SSE endpoints handle invalid input gracefully
- Error messages are consistent with admin endpoints
- Performance impact remains < 10ms per request
Note: This implementation leverages the validation framework established in Issue 1 (#339) to ensure consistent security practices across all API endpoints. The same SecurityValidator
class and configuration settings are reused to maintain uniformity in validation rules and limits.