diff --git a/USER_GUIDE.md b/USER_GUIDE.md index 1c7cf7b0e4..e4ab3c7e65 100644 --- a/USER_GUIDE.md +++ b/USER_GUIDE.md @@ -1968,6 +1968,9 @@ server over stdio. It reuses the existing CodeIndex database and exposes `initialize`, `workspace/symbol`, `textDocument/documentSymbol`, `textDocument/definition`, and `textDocument/references` for editors that can launch an arbitrary LSP command but do not speak MCP. +Incoming `textDocument.uri` values are rejected before URI parsing when they +exceed 4096 characters, matching the MCP resource URI limit and keeping error +responses bounded. Tool results include structured JSON in `structuredContent` plus a short text summary in `content`, so AI tools can parse typed data without scraping large text blocks. @@ -4230,6 +4233,8 @@ cdidxには**MCP(Model Context Protocol)サーバー**が組み込まれて 任意の LSP command を起動できるが MCP には対応していない editor 向けに `initialize`、`workspace/symbol`、`textDocument/documentSymbol`、 `textDocument/definition`、`textDocument/references` を公開します。 +受信した `textDocument.uri` は 4096 文字を超える場合、URI parse の前に拒否されます。 +これは MCP resource URI の上限と揃えており、エラー応答が過大にならないようにします。 ツール結果は `structuredContent` に構造化JSON、`content` に短い要約テキストを返すため、AIツールは巨大なテキストをパースせずに型付きデータを扱えます。 diff --git a/changelog.d/unreleased/3129.security.md b/changelog.d/unreleased/3129.security.md new file mode 100644 index 0000000000..3d0eb17df1 --- /dev/null +++ b/changelog.d/unreleased/3129.security.md @@ -0,0 +1,17 @@ +--- +category: security +issues: + - 3129 +affected: + - src/CodeIndex/Lsp/LspServer.cs + - tests/CodeIndex.Tests/LspServerTests.cs + - USER_GUIDE.md +--- + +## English + +- **LSP text-document URIs are length-capped before parsing (#3129)** — LSP handlers now reject oversized `textDocument.uri` values before URI parsing or path normalization and keep the JSON-RPC error bounded. + +## 日本語 + +- **LSP の text-document URI を parse 前に長さ制限するようになりました (#3129)** — LSP handler は過大な `textDocument.uri` を URI parse や path normalization の前に拒否し、JSON-RPC error を bounded に保ちます。 diff --git a/src/CodeIndex/Lsp/LspServer.cs b/src/CodeIndex/Lsp/LspServer.cs index 401d62623e..ca69278320 100644 --- a/src/CodeIndex/Lsp/LspServer.cs +++ b/src/CodeIndex/Lsp/LspServer.cs @@ -5,6 +5,7 @@ using System.Text.Json.Nodes; using CodeIndex.Cli; using CodeIndex.Database; +using CodeIndex.Mcp; using CodeIndex.Models; namespace CodeIndex.Lsp; @@ -15,6 +16,7 @@ internal sealed class LspServer : IDisposable internal const int MaxLspFrameBytes = 8 * 1024 * 1024; internal const int MaxLspHeaderLineBytes = 8 * 1024; internal const int MaxPositionDocumentBytes = 4 * 1024 * 1024; + internal const int MaxTextDocumentUriChars = McpBoundedText.MaxResourceUriChars; internal const int MaxJsonDepth = 32; private const int JsonRpcInvalidParamsCode = -32602; private const int JsonRpcInternalErrorCode = -32603; @@ -480,6 +482,9 @@ private static string GetDocumentPath(JsonElement root) var uri = GetString(root, "params", "textDocument", "uri"); if (string.IsNullOrWhiteSpace(uri)) throw new ArgumentException("textDocument.uri is required."); + if (uri.Length > MaxTextDocumentUriChars) + throw new ArgumentException( + $"textDocument.uri is too long. Max length is {MaxTextDocumentUriChars} characters; actual length is {uri.Length}."); return UriToPath(uri); } diff --git a/tests/CodeIndex.Tests/LspServerTests.cs b/tests/CodeIndex.Tests/LspServerTests.cs index 808fe62609..7b316afd2a 100644 --- a/tests/CodeIndex.Tests/LspServerTests.cs +++ b/tests/CodeIndex.Tests/LspServerTests.cs @@ -345,6 +345,43 @@ public void HandleMessage_DocumentSymbol_ResolvesDuplicateBasenamesByRelativePat } } + [Fact] + public void HandleMessage_DocumentSymbol_RejectsOversizedTextDocumentUri_Issue3129() + { + var projectRoot = TestProjectHelper.CreateTempProject("cdidx_lsp_document_symbol_long_uri"); + try + { + var dbPath = TestProjectHelper.CreateProjectDb(projectRoot); + using var db = new DbContext(dbPath); + using var server = new LspServer(new DbReader(db), "1.2.3", ProgramRunner.CreateDefaultJsonOptions(), projectRoot); + var oversizedUri = "file:///" + new string('a', LspServer.MaxTextDocumentUriChars); + var request = JsonSerializer.Serialize(new + { + jsonrpc = "2.0", + id = 3129, + method = "textDocument/documentSymbol", + @params = new + { + textDocument = new { uri = oversizedUri }, + }, + }); + + var response = server.HandleMessage(request); + + Assert.NotNull(response); + var error = response!["error"]!; + Assert.Equal(-32602, error["code"]!.GetValue()); + var message = error["message"]!.GetValue(); + Assert.Equal("Invalid params", message); + Assert.True(message.Length < 120); + Assert.DoesNotContain(oversizedUri, message, StringComparison.Ordinal); + } + finally + { + TestProjectHelper.DeleteDirectory(projectRoot); + } + } + [Fact] public void HandleMessage_Definition_ReturnsLocationForTokenAtPosition() {