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
16 changes: 16 additions & 0 deletions DEVELOPER_GUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -910,6 +910,14 @@ across live-text eviction and are cleared by `didClose`, so an evicted newer
version cannot be replaced by a stale change. Other providers return empty
arrays or null when the database cannot answer safely instead of inventing
language-server analysis.
Every inbound message must first be an object whose `jsonrpc` member is exactly
the string `"2.0"`. Missing, null, non-string, or other-version envelope values
return `-32600` (`Invalid Request`) with a valid request ID preserved; validation
runs before receive-time lifecycle reservation, cancellation fast paths, method
dispatch, state mutation, or database access. `workspace/symbol` separately
requires `params.query` to be a JSON string. Missing, null, or non-string query
values return `-32602` (`Invalid params`) before query-snapshot refresh or symbol
search, while the valid empty string remains a supported workspace-wide query.
Before URI/path resolution, live-document access, or query-snapshot refresh,
one shared coordinate validator checks every supported position/range-bearing
method. `definition`, `declaration`, `references`, `hover`, `completion`, and
Expand Down Expand Up @@ -5127,6 +5135,14 @@ indexed symbol に fallback する。numeric document-version tombstone は live
後も上限付きで保持し、`didClose` で消去するため、evict 済みの新しい version を stale change が
置き換えることはない。それ以外の provider は database が安全に答えられない場合、
language-server analysis を作り上げず、空配列または null を返す。
すべての inbound message は、まず object であり、その `jsonrpc` member が文字列 `"2.0"` と
完全一致しなければならない。欠落、null、文字列以外、または別 version の envelope 値には、
有効な request ID を維持して `-32600`(`Invalid Request`)を返す。この検証は receive-time の
lifecycle reservation、cancellation fast path、method dispatch、state mutation、database access
より前に行う。`workspace/symbol` では、これとは別に `params.query` を JSON string として必須に
する。query が欠落、null、または文字列以外なら query snapshot の refresh や symbol search より
前に `-32602`(`Invalid params`)を返す一方、正当な空文字列は workspace-wide query として
引き続き対応する。
URI / path 解決、live document へのアクセス、query snapshot の refresh より前に、1つの共通
coordinate validator ですべての対応済み position / range method を検証する。
`definition`、`declaration`、`references`、`hover`、`completion`、
Expand Down
2 changes: 2 additions & 0 deletions TESTING_GUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -692,6 +692,7 @@ Candidate-ordered parallel-index recovery tests must prove that the fatal result
- `LspServerTests.cs`
LSP JSON-RPC behavior and indexed editor semantics. Reference parity coverage must compare LSP locations with the matching CLI candidate bundle, including overload identity and both `includeDeclaration` states. Keep document/workspace identifier ranges and explicit-versus-inferred inlay hints in one source-semantics fixture so persisted-column anchoring, source confirmation, and hint suppression cannot drift independently.
Lifecycle transport coverage must stage initialization before sending shutdown/exit, use a caller-owned stream whose read after the exit frame cannot be interrupted by cancellation, and assert the shutdown response, success versus exit-before-shutdown status, and absence of any post-exit read. Keep finite EOF, malformed-frame, and caller-cancellation coverage alongside it so early transport completion cannot weaken those paths.
Issue-5231 request-boundary coverage uses framed transcripts to prove that an invalid-version initialize cannot mutate lifecycle state before a valid initialize, then sends every missing/null/non-string/wrong-version `jsonrpc` and missing/null/non-string `workspace/symbol.query` case after initialization. Keep valid empty and non-empty query controls in the same seeded fixture, preserve typed response IDs and stable error codes, and assert that rejected frames never reach receive-time reservation, method dispatch, symbol search, or `lsp.request` activity. Separately stage an invalid-version cancellation while a symbol request is active, prove it remains active until a valid cancellation arrives, and pin malformed-envelope/invalid-query error precedence over the overload response.
The issue-4870 kind-mapping matrix seeds every registered built-in internal kind plus semantic constructor and enum-member rows, then asserts decoded document-symbol, workspace-symbol, and completion responses. It also feeds real TypeScript, Kotlin, Solidity, Java, Swift, Dart, Visual Basic, Pascal, C#, and Shell extractor output through the shared mapper so keyword, compact, named, dedicated-name, and verbatim-identifier constructors; same-name, return-typed, object/static, and shell non-constructors; decorated, escaped, indirect, and comma-grouped enum entries; and nested enums cannot invert classification. It separately pins the conservative parameter and unknown/plugin fallback. Keep all three protocol surfaces in that one fixture so constructor/import handling and deliberate fallback mappings cannot drift.
Coordinate-validation coverage keeps every supported position provider in one negative/malformed/overflow matrix, with missing-document precedence and missing/reversed inlay ranges in the same read-only fixture. Keep UTF-16 and EOF success controls together, but retain invalid ranged-`didChange` as a separate mutable-state fixture so a rejected notification cannot replace accepted live text (#4869).
C# semantic-token coverage decodes LSP delta coordinates and pairs the same keyword/modifier, namespace/type, method/property/event/enum-member, parameter/variable/field, generic/record-struct, explicit-interface-member, attribute, using-declaration, accessor-local, numeric member-access, escaped/contextual-identifier, interpolation, comma-separated declaration, and declaration-kind fixture with `QueryCommandRunnerTests.RunExcerpt_JsonCSharpSemanticKindsMatchLspClassifier_Issue4852`. Keep both surfaces on the shared source classifier, retain ordering/non-overlap assertions, preserve the narrow excerpt check whose parameter/type-parameter declarations are outside the returned range, and retain `RunExcerpt_JsonCSharpSemanticTokensUseVisibleRangeBudget_Issue4852` so earlier tokens cannot empty a late excerpt while indexed-source reconstruction remains line/character bounded. `HandleMessage_SemanticTokens_FallsBackToIndexedCSharpSymbolsWhenSourceReadFails_Issue4852` keeps indexed declaration tokens available when bounded source loading fails.
Expand Down Expand Up @@ -1849,6 +1850,7 @@ dotnet test --filter "FullyQualifiedName~GitHelperTests"
- `LspServerTests.cs`
LSP の JSON-RPC 挙動と indexed editor semantics のテスト。reference parity coverage では、overload identity と `includeDeclaration` の両状態を含め、LSP location と対応する CLI candidate bundle を比較してください。document/workspace の identifier range と explicit/inferred inlay hint は 1 つの source-semantics fixture にまとめ、保存済み column の anchoring、source 上の確認、hint 抑制が別々に drift しないようにします。
lifecycle transport coverage では、shutdown/exit の送信前に initialization を stage し、exit frame 後の read が cancellation では中断されない caller-owned stream を使い、shutdown response、success と shutdown 前 exit の status の違い、post-exit read が一度も始まらないことを検証してください。早期 transport completion によって他経路が弱まらないよう、finite EOF、malformed frame、caller cancellation の coverage も並行して維持します。
issue-5231 の request boundary coverage は framed transcript を使い、不正 version の initialize が lifecycle state を変更せず、その後の正当な initialize が成功することを検証します。続いて initialization 後に、`jsonrpc` の欠落・null・文字列以外・別 version と、`workspace/symbol.query` の欠落・null・文字列以外をすべて送ります。同じ seed 済み fixture に空文字列と非空文字列の正当な query control を保ち、型付き response ID と安定した error code を維持し、拒否 frame が receive-time reservation、method dispatch、symbol search、`lsp.request` activity のいずれにも到達しないことを確認してください。別途、symbol request の実行中に不正 version の cancellation を stage し、正当な cancellation が届くまで request が active のままであること、および malformed envelope / invalid query の error が overload response より優先されることを固定してください。
issue-4870 の kind-mapping matrix は、登録済みの全 built-in internal kind に加えて semantic な constructor と enum-member の row を seed し、decode 済みの document-symbol、workspace-symbol、completion response を検証します。また、実際の TypeScript、Kotlin、Solidity、Java、Swift、Dart、Visual Basic、Pascal、C#、Shell extractor 出力を shared mapper に通し、keyword / compact / named / 専用名 / 逐語識別子 constructor、同名 / 戻り値型付き / object・static / shell の非 constructor、annotation 付き / escape 済み / indirect / comma-grouped enum entry、nested enum の分類が逆転しないようにします。保守的な parameter および未知/plugin fallback は別途固定します。constructor/import の処理と意図的な fallback mapping がずれないよう、3つの protocol surface を同じ fixture に維持してください。
coordinate validation coverage は、対応済みの全 position provider を1つの負数 / malformed / overflow matrix にまとめ、missing document より検証を優先することと、inlay range の欠落 / 逆順も同じ read-only fixture で検証します。UTF-16 と EOF の成功 control は一緒に維持し、不正な range 付き `didChange` は mutable state の独立 fixture として残して、拒否された notification が受理済み live text を置き換えられないことを検証してください(#4869)。
C# semantic-token coverage は LSP の delta 座標を decode し、同じ keyword/modifier、namespace/type、method/property/event/enum-member、parameter/variable/field、generic/record struct、explicit interface member、attribute、using declaration、accessor local、numeric member access、escaped/contextual identifier、interpolation、comma-separated declaration、declaration kind の fixture を `QueryCommandRunnerTests.RunExcerpt_JsonCSharpSemanticKindsMatchLspClassifier_Issue4852` と対にします。両 surface が shared source classifier を使い続け、順序と非 overlap の assertion、parameter / type-parameter の declaration が返却範囲外にある narrow excerpt の検証、および indexed-source reconstruction を line / character 上限内に保ちながら手前の token によって file 後半の excerpt が空にならないことを保証する `RunExcerpt_JsonCSharpSemanticTokensUseVisibleRangeBudget_Issue4852` を維持してください。`HandleMessage_SemanticTokens_FallsBackToIndexedCSharpSymbolsWhenSourceReadFails_Issue4852` は bounded source loading が失敗しても indexed declaration token を利用できる状態を維持します。
Expand Down
20 changes: 16 additions & 4 deletions USER_GUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -3297,10 +3297,16 @@ message bodies, so Ctrl-C / host cancellation can interrupt pending frame reads
instead of waiting for another complete request.
Unknown-method diagnostics echo at most 240 method-name characters with `...`
when the method name is longer. Request IDs must be bounded JSON-RPC scalar
values: strings are capped at 256
values. Every inbound LSP message must be an object whose `jsonrpc` member is
exactly the string `"2.0"`; missing, null, non-string, or other-version values
return `-32600` (`Invalid Request`) before lifecycle reservation, cancellation,
dispatch, state mutation, or database access, while preserving a valid request
ID. Request-ID strings are capped at 256
characters, integer IDs must fit in `Int64`, and non-scalar IDs are rejected as
invalid requests before response IDs are cloned. `workspace/symbol` query
strings are capped at 1000 characters before symbol search runs.
invalid requests before response IDs are cloned. `workspace/symbol.params.query`
is a required JSON string; missing, null, or non-string values return `-32602`
(`Invalid params`) before symbol search, while the empty string remains valid.
Query strings are capped at 1000 characters before symbol search runs.
`workspace/symbol` accepts optional numeric `limit` / `maxResults` parameters
and clamps them to 1000 results. `textDocument/documentSymbol` returns
hierarchical `DocumentSymbol` children when container metadata is available,
Expand Down Expand Up @@ -6929,10 +6935,16 @@ stdio loop は header / message body 読み取り中も CLI cancellation token
Ctrl-C や host cancellation が次の完全な request を待たずに pending frame read を中断できます。
method-not-found diagnostic で echo する method name は最大 240 文字に制限され、
長い場合は `...` を付けて切り詰めます。
すべての受信 LSP message は object であり、`jsonrpc` member が文字列 `"2.0"` と完全一致する
必要があります。欠落、null、文字列以外、または別 version の値には、有効な request ID を
維持して `-32600`(`Invalid Request`)を返します。この検証は lifecycle reservation、
cancellation、dispatch、state mutation、database access より前に行います。
request ID は bounded な JSON-RPC scalar value に限定され、string は 256 文字まで、
integer ID は `Int64` に収まるものだけを受理し、non-scalar ID は response ID を複製する前に
invalid request として拒否します。
`workspace/symbol` の query string は symbol search を実行する前に 1000 文字で上限をかけます。
`workspace/symbol.params.query` は必須の JSON string です。欠落、null、文字列以外の値には
symbol search より前に `-32602`(`Invalid params`)を返す一方、空文字列は引き続き有効です。
query string は symbol search を実行する前に 1000 文字で上限をかけます。
`workspace/symbol` は任意の numeric `limit` / `maxResults` parameter を受け取り、1000 件までに
clamp します。`textDocument/documentSymbol` は container metadata がある場合に階層化された
`DocumentSymbol` children を返し、最後に受理した live buffer または index から最大 1000 件の
Expand Down
21 changes: 21 additions & 0 deletions changelog.d/unreleased/5231.fixed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
---
category: fixed
issues:
- 5231
affected:
- src/CodeIndex/Lsp/LspServer.cs
- src/CodeIndex/Lsp/LspServer.Protocol.cs
- src/CodeIndex/Lsp/LspServer.SymbolRequests.cs
- tests/CodeIndex.Tests/LspServerTests.cs
- DEVELOPER_GUIDE.md
- TESTING_GUIDE.md
- USER_GUIDE.md
---

## English

- **LSP now rejects malformed JSON-RPC envelopes and invalid workspace-symbol query types (#5231)** — extending the protocol-boundary hardening from #4336, #4869, and #3128, the server now requires `jsonrpc: "2.0"` and a string `workspace/symbol.params.query` before lifecycle reservation, dispatch, or database access, while preserving valid empty queries.

## 日本語

- **LSP が不正な JSON-RPC envelope と workspace symbol query の型を拒否するようになりました (#5231)** — #4336、#4869、#3128 の protocol boundary hardening を拡張し、server は lifecycle reservation、dispatch、database access より前に `jsonrpc: "2.0"` と文字列の `workspace/symbol.params.query` を必須にしつつ、正当な空 query は引き続き受け付けます。
11 changes: 8 additions & 3 deletions src/CodeIndex/Lsp/LspServer.Protocol.cs
Original file line number Diff line number Diff line change
Expand Up @@ -149,13 +149,18 @@ private static string GetTextDocumentUri(JsonElement root)
return uri;
}

private static string? GetString(JsonElement root, params string[] path)
private static string GetRequiredString(JsonElement root, params string[] path)
{
if (!TryGet(root, out var value, path) || value.ValueKind != JsonValueKind.String)
return null;
return value.GetString();
throw new ArgumentException("A required string parameter is missing or invalid.");
return value.GetString()!;
}

private static bool HasValidJsonRpcEnvelope(JsonElement root) =>
root.TryGetProperty("jsonrpc", out var jsonRpc)
&& jsonRpc.ValueKind == JsonValueKind.String
&& string.Equals(jsonRpc.GetString(), "2.0", StringComparison.Ordinal);

private static bool? GetBool(JsonElement root, params string[] path)
{
if (!TryGet(root, out var value, path))
Expand Down
12 changes: 9 additions & 3 deletions src/CodeIndex/Lsp/LspServer.SymbolRequests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -93,9 +93,7 @@ private SymbolResponse CreateWorkspaceSymbolResponse(
bool createPartialItems,
CancellationToken cancellationToken)
{
var query = GetString(root, "params", "query");
if (query != null && query.Length > QueryLimits.MaxQueryLength)
throw new ArgumentException(QueryLimits.FormatQueryTooLongError());
var query = GetWorkspaceSymbolQuery(root);

var limit = GetLimit(root, DefaultLimit, MaxWorkspaceSymbols, "params", "limit")
?? GetLimit(root, DefaultLimit, MaxWorkspaceSymbols, "params", "maxResults")
Expand Down Expand Up @@ -141,6 +139,14 @@ private SymbolResponse CreateWorkspaceSymbolResponse(
return new SymbolResponse(array, [], array.Count, truncated);
}

private static string GetWorkspaceSymbolQuery(JsonElement root)
{
var query = GetRequiredString(root, "params", "query");
if (query.Length > QueryLimits.MaxQueryLength)
throw new ArgumentException(QueryLimits.FormatQueryTooLongError());
return query;
}

private IEnumerable<JsonNode> EnumerateWorkspaceSymbolItems(
IReadOnlyList<SymbolResult> symbols,
CancellationToken cancellationToken)
Expand Down
Loading
Loading