C# Implementation (#2)
C# implementation of SEP-1763 --------- Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
46d96779d6
commit
45f9e0ef27
3 changed files with 13862 additions and 21 deletions
|
|
@ -0,0 +1,19 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net10.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<LangVersion>latest</LangVersion>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting" Version="10.0.0-preview.*" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\src\ModelContextProtocol.Interceptors\ModelContextProtocol.Interceptors.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
|
@ -0,0 +1,195 @@
|
|||
using ModelContextProtocol.Interceptors;
|
||||
using ModelContextProtocol.Interceptors.Server;
|
||||
using ModelContextProtocol.Protocol;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Nodes;
|
||||
|
||||
/// <summary>
|
||||
/// A sample validation interceptor that validates tool call parameters for security issues.
|
||||
/// This interceptor can be deployed as a separate MCP service to validate requests
|
||||
/// before they reach the actual tool implementation.
|
||||
/// </summary>
|
||||
[McpServerInterceptorType]
|
||||
public class ParameterValidator
|
||||
{
|
||||
/// <summary>
|
||||
/// Validates tool call parameters for potential security issues.
|
||||
/// </summary>
|
||||
[McpServerInterceptor(
|
||||
Name = "parameter-validator",
|
||||
Description = "Validates tool call parameters for security issues",
|
||||
Events = new[] { InterceptorEvents.ToolsCall },
|
||||
Phase = InterceptorPhase.Request)]
|
||||
public ValidationInterceptorResult ValidateToolCall(JsonNode? payload)
|
||||
{
|
||||
if (payload is null)
|
||||
{
|
||||
return new ValidationInterceptorResult
|
||||
{
|
||||
Valid = false,
|
||||
Severity = ValidationSeverity.Error,
|
||||
Messages = [new() { Message = "Payload is required", Severity = ValidationSeverity.Error }]
|
||||
};
|
||||
}
|
||||
|
||||
// Parse the tool call request
|
||||
CallToolRequestParams? toolCall;
|
||||
try
|
||||
{
|
||||
toolCall = payload.Deserialize<CallToolRequestParams>();
|
||||
}
|
||||
catch (JsonException ex)
|
||||
{
|
||||
return new ValidationInterceptorResult
|
||||
{
|
||||
Valid = false,
|
||||
Severity = ValidationSeverity.Error,
|
||||
Messages = [new() { Message = $"Invalid payload format: {ex.Message}", Severity = ValidationSeverity.Error }]
|
||||
};
|
||||
}
|
||||
|
||||
if (toolCall is null || string.IsNullOrEmpty(toolCall.Name))
|
||||
{
|
||||
return new ValidationInterceptorResult
|
||||
{
|
||||
Valid = false,
|
||||
Severity = ValidationSeverity.Error,
|
||||
Messages = [new() { Message = "Tool name is required", Severity = ValidationSeverity.Error }]
|
||||
};
|
||||
}
|
||||
|
||||
// Validate tool arguments for security issues
|
||||
var messages = new List<ValidationMessage>();
|
||||
|
||||
if (toolCall.Arguments is not null)
|
||||
{
|
||||
foreach (var arg in toolCall.Arguments)
|
||||
{
|
||||
var value = arg.Value.ToString();
|
||||
if (string.IsNullOrEmpty(value))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check for SQL injection patterns
|
||||
if (ContainsSqlInjectionPattern(value))
|
||||
{
|
||||
messages.Add(new ValidationMessage
|
||||
{
|
||||
Path = $"arguments.{arg.Key}",
|
||||
Message = "Potentially malicious SQL content detected",
|
||||
Severity = ValidationSeverity.Error
|
||||
});
|
||||
}
|
||||
|
||||
// Check for command injection patterns
|
||||
if (ContainsCommandInjectionPattern(value))
|
||||
{
|
||||
messages.Add(new ValidationMessage
|
||||
{
|
||||
Path = $"arguments.{arg.Key}",
|
||||
Message = "Potentially malicious command content detected",
|
||||
Severity = ValidationSeverity.Error
|
||||
});
|
||||
}
|
||||
|
||||
// Check for path traversal patterns
|
||||
if (ContainsPathTraversalPattern(value))
|
||||
{
|
||||
messages.Add(new ValidationMessage
|
||||
{
|
||||
Path = $"arguments.{arg.Key}",
|
||||
Message = "Potentially malicious path traversal detected",
|
||||
Severity = ValidationSeverity.Warn
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (messages.Count > 0)
|
||||
{
|
||||
return new ValidationInterceptorResult
|
||||
{
|
||||
Valid = false,
|
||||
Severity = messages.Max(m => m.Severity),
|
||||
Messages = messages
|
||||
};
|
||||
}
|
||||
|
||||
return new ValidationInterceptorResult { Valid = true };
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Validates that required fields are present in tool calls.
|
||||
/// </summary>
|
||||
[McpServerInterceptor(
|
||||
Name = "required-fields-validator",
|
||||
Description = "Validates that required fields are present in tool calls",
|
||||
Events = new[] { InterceptorEvents.ToolsCall },
|
||||
Phase = InterceptorPhase.Request,
|
||||
PriorityHint = 10)]
|
||||
public ValidationInterceptorResult ValidateRequiredFields(JsonNode? payload, string @event)
|
||||
{
|
||||
if (payload is null)
|
||||
{
|
||||
return new ValidationInterceptorResult
|
||||
{
|
||||
Valid = false,
|
||||
Severity = ValidationSeverity.Error,
|
||||
Messages = [new() { Message = $"Payload is required for event '{@event}'", Severity = ValidationSeverity.Error }]
|
||||
};
|
||||
}
|
||||
|
||||
return new ValidationInterceptorResult { Valid = true };
|
||||
}
|
||||
|
||||
private static bool ContainsSqlInjectionPattern(string value)
|
||||
{
|
||||
var patterns = new[]
|
||||
{
|
||||
"'; DROP TABLE",
|
||||
"'; DELETE FROM",
|
||||
"' OR '1'='1",
|
||||
"' OR 1=1",
|
||||
"'; EXEC ",
|
||||
"'; INSERT INTO",
|
||||
"UNION SELECT",
|
||||
"-- "
|
||||
};
|
||||
|
||||
return patterns.Any(p => value.Contains(p, StringComparison.OrdinalIgnoreCase));
|
||||
}
|
||||
|
||||
private static bool ContainsCommandInjectionPattern(string value)
|
||||
{
|
||||
var patterns = new[]
|
||||
{
|
||||
"; rm -rf",
|
||||
"| rm -rf",
|
||||
"&& rm -rf",
|
||||
"; cat /etc/passwd",
|
||||
"| cat /etc/passwd",
|
||||
"`rm -rf`",
|
||||
"$(rm -rf",
|
||||
"; wget ",
|
||||
"| wget ",
|
||||
"; curl "
|
||||
};
|
||||
|
||||
return patterns.Any(p => value.Contains(p, StringComparison.OrdinalIgnoreCase));
|
||||
}
|
||||
|
||||
private static bool ContainsPathTraversalPattern(string value)
|
||||
{
|
||||
var patterns = new[]
|
||||
{
|
||||
"../",
|
||||
"..\\",
|
||||
"/etc/passwd",
|
||||
"/etc/shadow",
|
||||
"C:\\Windows\\System32"
|
||||
};
|
||||
|
||||
return patterns.Any(p => value.Contains(p, StringComparison.OrdinalIgnoreCase));
|
||||
}
|
||||
}
|
||||
21
csharp/sdk/samples/InterceptorServiceSample/Program.cs
Normal file
21
csharp/sdk/samples/InterceptorServiceSample/Program.cs
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using ModelContextProtocol.Interceptors;
|
||||
|
||||
// This sample demonstrates how to create an MCP server that exposes validation interceptors.
|
||||
// Interceptors can be deployed as separate services (sidecars, gateways) to validate,
|
||||
// mutate, or observe MCP messages without modifying the original server or client.
|
||||
|
||||
var builder = Host.CreateApplicationBuilder(args);
|
||||
|
||||
builder.Services.AddMcpServer()
|
||||
.WithStdioServerTransport()
|
||||
.WithInterceptors<ParameterValidator>();
|
||||
|
||||
builder.Logging.AddConsole(options =>
|
||||
{
|
||||
options.LogToStandardErrorThreshold = LogLevel.Trace;
|
||||
});
|
||||
|
||||
await builder.Build().RunAsync();
|
||||
Loading…
Reference in a new issue