Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions changelog.d/unreleased/3306-3308.fixed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
---
category: fixed
issues:
- 3306
- 3307
- 3308
affected:
- tests/CodeIndex.Tests/McpAuditLogTests.cs
---

## English

- **MCP audit request-id truncation coverage now uses a protocol-valid escaped id (#3306, #3307, #3308)**: the regression test now keeps the JSON-RPC id within request validation limits while still making the serialized audit request id exceed the audit display cap.

## 日本語

- **MCP audit request-id truncation のカバレッジが protocol-valid な escaped id を使うようになりました (#3306, #3307, #3308)**: 回帰テストは JSON-RPC id を request validation の上限内に保ちつつ、serialized audit request id が audit 表示上限を超える入力で検証するようになりました。
39 changes: 37 additions & 2 deletions tests/CodeIndex.Tests/McpAuditLogTests.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Globalization;
using System.Text;
using System.Text.Json;
using System.Text.Json.Nodes;
using CodeIndex.Cli;
Expand Down Expand Up @@ -126,7 +127,7 @@ public void ToolsCall_MissingToolName_StillEmitsAuditRecord()
}

[Fact]
public void ToolsCall_OversizedRequestId_IsRejectedBeforeAuditRecord_Issue3104()
public void ToolsCall_OversizedRequestId_IsRejectedBeforeAuditRecord_Issue3104_3308()
{
using var sink = new AuditLogSink(_auditPath, AuditLogSink.DefaultMaxBytes, includeValues: false);
using var server = CreateServer(sink);
Expand Down Expand Up @@ -462,12 +463,13 @@ public void ToolsCall_CapsAuditArgumentKeyCount_Issue3237()
}

[Fact]
public void ToolsCall_MaxLengthRequestId_PreservesAuditRequestId_Issue3237()
public void ToolsCall_MaxLengthRequestId_PreservesAuditRequestId_Issue3237_3307()
{
using var sink = new AuditLogSink(_auditPath, AuditLogSink.DefaultMaxBytes, includeValues: false);
using var server = CreateServer(sink);
var id = new string('r', McpServer.MaxRequestIdCharacterCount);
var serializedId = JsonSerializer.Serialize(id);
Assert.True(serializedId.Length <= AuditLogSink.MaxRequestIdChars);
var request = new JsonObject
{
["jsonrpc"] = "2.0",
Expand All @@ -492,6 +494,39 @@ public void ToolsCall_MaxLengthRequestId_PreservesAuditRequestId_Issue3237()
Assert.False(record.TryGetProperty("request_id_truncated", out _));
}

[Fact]
public void ToolsCall_EscapedRequestId_TruncatesAuditRequestId_Issue3306()
{
using var sink = new AuditLogSink(_auditPath, AuditLogSink.DefaultMaxBytes, includeValues: false);
using var server = CreateServer(sink);
var id = new string('\u3042', 43);
Assert.True(id.Length <= McpServer.MaxRequestIdCharacterCount);
Assert.True(Encoding.UTF8.GetByteCount(id) <= McpServer.MaxRequestIdByteLength);
var serializedId = JsonSerializer.Serialize(id);
Assert.True(serializedId.Length > AuditLogSink.MaxRequestIdChars);
var requestIdDisplay = McpBoundedText.ForDisplay(serializedId, AuditLogSink.MaxRequestIdChars);
var request = new JsonObject
{
["jsonrpc"] = "2.0",
["id"] = id,
["method"] = "tools/call",
["params"] = new JsonObject
{
["name"] = "ping",
["arguments"] = new JsonObject(),
},
};

var response = server.HandleMessage(request)!;

Assert.False(response.AsObject().ContainsKey("error"));
Assert.NotNull(response["result"]);
var record = ReadOnlyRecord();
Assert.Equal(requestIdDisplay.Text, record.GetProperty("request_id").GetString());
Assert.Equal(serializedId.Length, record.GetProperty("request_id_length").GetInt32());
Assert.True(record.GetProperty("request_id_truncated").GetBoolean());
}

[Fact]
public void ToolsCall_ValuesOmitted_ByDefault()
{
Expand Down
Loading