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
8 changes: 4 additions & 4 deletions DEVELOPER_GUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -1999,10 +1999,10 @@ who opens a cloud session, not by a real user after release.

Contract guarantees that downstream consumers can rely on:

- **Field stability.** `timestamp`, `tool`, `arg_keys`, `arg_lengths`, `elapsed_ms`, `error_code` are emitted on every record. `caller`, `caller_version`, `request_id`, `arg_values`, `result_count`, `error` are emitted only when non-null; renaming or repurposing any published field is a breaking change, the same policy as the CLI `--metrics` schema.
- **Field stability.** `timestamp`, `tool`, `arg_keys`, `arg_lengths`, `elapsed_ms`, `error_code` are emitted on every record. `caller`, `caller_version`, `request_id`, `request_id_length`, `request_id_truncated`, `arg_key_lengths`, `arg_keys_truncated`, `arg_key_truncation_reasons`, `arg_values`, `arg_values_redacted`, `arg_values_truncated`, `arg_values_truncation_reasons`, `arg_values_serialized_bytes`, `arg_values_max_bytes`, `result_count`, `error` are emitted only when non-null or true; renaming or repurposing any published field is a breaking change, the same policy as the CLI `--metrics` schema.
- **Error code semantics.** `0` = success, `1` = MCP tool error (`isError: true`), negative = the verbatim JSON-RPC error code (e.g. `-32602` for invalid params, `-32603` for internal error). The companion `error` string is one of `jsonrpc_error`, `tool_error`, `missing_tool_name`, or the sanitized exception type name (`McpServer.BuildSanitizedToolErrorMessage` keeps `ex.Message` out of the wire and out of the audit, #1530).
- **Result count.** `ExtractResultCount` prefers `structuredContent.count` over `structuredContent.results.length`; tool errors and JSON-RPC errors omit the field. Tools that return no count-shaped payload (e.g. `ping`) leave `result_count` absent rather than emitting `0`.
- **Argument privacy.** `arg_keys` and `arg_lengths` are always recorded so query *shape* is recoverable. `arg_values` is gated behind `--audit-log-include-values` because cdidx queries can carry literal source snippets or secret-shaped strings. The echo is a `DeepClone` so later mutation of the request payload cannot retroactively change the audit trail.
- **Argument privacy.** `arg_keys` and `arg_lengths` are always recorded so query *shape* is recoverable, but argument-key count and displayed key length are capped and marked with `arg_keys_truncated`. `arg_values` is gated behind `--audit-log-include-values` because cdidx queries can carry literal source snippets or secret-shaped strings. The echo is a sanitized, budgeted clone: secret-like keys and known token patterns are replaced with `[REDACTED]`, and depth, object-property, array-item, total-node, string-length, serialized-byte, and event-byte limits can mark `arg_values_truncated` before values are written.
- **Caller identity.** `_clientName` / `_clientVersion` are captured from every `initialize.clientInfo` and overwrite on reconnection within the same session, so a long-running MCP loop with multiple `initialize` handshakes attributes records to the *currently connected* client rather than the first one.
- **Rotation.** Writes go through an open-append-close cycle so external `tail -F` consumers follow rotations and so the file is closed during the rename. When `_bytesWritten >= MaxBytes`, `RotateLocked` drops `<path>.(RotationKeep-1)` (currently `<path>.2`), cascades surviving slots up by one, and moves `<path>` to `<path>.1`. `RotationKeep = 3`, so `<path>.3` is never created — exercised by `AuditLogSinkTests.Record_KeepsAtMostThreeFiles_DropsOldestOnRotationOverflow`.
- **Best effort.** Serialization failures, IO failures, and rotation failures are swallowed (the audit must not crash the underlying tool call). The constructor still fails fast on impossible paths so the operator sees the misconfiguration before any tool dispatch happens.
Expand Down Expand Up @@ -3576,10 +3576,10 @@ Cloud セッションは開発ループの中で `dotnet build` にフォール

下流コンシューマが依存できる契約:

- **フィールドの安定性。** `timestamp`、`tool`、`arg_keys`、`arg_lengths`、`elapsed_ms`、`error_code` は全レコードで出力する。`caller`、`caller_version`、`request_id`、`arg_values`、`result_count`、`error` は値が non-null のときだけ含める。既存フィールドの改名や流用は破壊的変更扱い(CLI `--metrics` と同じ運用)。
- **フィールドの安定性。** `timestamp`、`tool`、`arg_keys`、`arg_lengths`、`elapsed_ms`、`error_code` は全レコードで出力する。`caller`、`caller_version`、`request_id`、`request_id_length`、`request_id_truncated`、`arg_key_lengths`、`arg_keys_truncated`、`arg_key_truncation_reasons`、`arg_values`、`arg_values_redacted`、`arg_values_truncated`、`arg_values_truncation_reasons`、`arg_values_serialized_bytes`、`arg_values_max_bytes`、`result_count`、`error` は値が non-null または true のときだけ含める。既存フィールドの改名や流用は破壊的変更扱い(CLI `--metrics` と同じ運用)。
- **エラーコード意味論。** `0`=成功、`1`=MCP ツールエラー (`isError: true`)、負値=JSON-RPC エラーコードそのまま(例: invalid params なら `-32602`、internal error なら `-32603`)。同伴する `error` 文字列は `jsonrpc_error` / `tool_error` / `missing_tool_name` / サニタイズ済み例外型名のいずれか。`McpServer.BuildSanitizedToolErrorMessage` が `ex.Message` をワイヤーと audit から除外している(#1530)。
- **result count。** `ExtractResultCount` は `structuredContent.count` を優先し、無ければ `structuredContent.results.length`、いずれも無ければ省略する。ツールエラー / JSON-RPC エラー時も省略する(`0` ではなく欠落)。
- **引数のプライバシー。** `arg_keys` / `arg_lengths` は常に記録するので呼び出しの *形状* は復元できる。`arg_values` は `--audit-log-include-values` に gated(cdidx クエリにはソース片や secret 風文字列が混入しうる)。echo は `DeepClone` で取るので、後段のリクエスト改変が監査記録を遡及的に書き換えることはない
- **引数のプライバシー。** `arg_keys` / `arg_lengths` は常に記録するので呼び出しの *形状* は復元できるが、引数キー数と表示キー長は capped され `arg_keys_truncated` で明示される。`arg_values` は `--audit-log-include-values` に gated(cdidx クエリにはソース片や secret 風文字列が混入しうる)。echo は sanitize と budget を適用した clone として作り、secret 風のキーや既知 token pattern は `[REDACTED]` に置換し、depth / object property / array item / total node / string length / serialized byte / event byte の上限に達した場合は値を書き出す前に `arg_values_truncated` を記録する
- **呼び出し元の特定。** `_clientName` / `_clientVersion` は `initialize.clientInfo` から毎回キャプチャし、同一セッション内で再 `initialize` があれば上書きされる。複数 handshake が走る長寿命 MCP ループでも、*現在接続中の*クライアントに対して記録が紐付く。
- **ローテーション。** 1 レコードごとに open-append-close する。外部 `tail -F` の追従と rename 時の close-state 維持のため。`_bytesWritten >= MaxBytes` を超えた時点で `RotateLocked` が `<path>.(RotationKeep-1)`(現在は `<path>.2`)を破棄し、生存スロットを 1 つ古い側へ寄せ、`<path>` を `<path>.1` へ移す。`RotationKeep = 3` なので `<path>.3` は決して生成されない(`AuditLogSinkTests.Record_KeepsAtMostThreeFiles_DropsOldestOnRotationOverflow` で常時検証)。
- **ベストエフォート。** シリアライズ失敗・IO 失敗・rotation 失敗はすべて握り潰す(監査の失敗で本体ツール呼び出しを壊さない)。一方、構築時の不正パスはコンストラクタが早期失敗させ、ディスパッチ前にオペレーターに気付かせる。
Expand Down
32 changes: 26 additions & 6 deletions USER_GUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -1376,8 +1376,8 @@ Example output:
| Flag | Default | Effect |
|---|---|---|
| `--audit-log <path>` | (off) | Enable audit emission and write JSONL records to `<path>`. The parent directory is created if missing. |
| `--audit-log-include-values` | off | Echo the full argument payload into each record. Requires `--audit-log`. Off by default because `query` / `name` arguments may contain literal source snippets or secret-shaped strings. |
| `--audit-log-max-bytes <n>` | `52428800` (50 MiB) | Size threshold (bytes) at which the active log rotates. Must be 4096. |
| `--audit-log-include-values` | off | Echo a redacted copy of the argument payload into each record. Requires `--audit-log`. Off by default because `query` / `name` arguments may contain literal source snippets or secret-shaped strings. |
| `--audit-log-max-bytes <n>` | `52428800` (50 MiB) | Size threshold (bytes) at which the active log rotates. Must be between 4096 and 1073741824. |

Each record is a single JSON object on its own line with these fields:

Expand All @@ -1388,9 +1388,19 @@ Each record is a single JSON object on its own line with these fields:
| `caller` | string (optional) | `initialize.clientInfo.name` from the connected MCP client |
| `caller_version` | string (optional) | `initialize.clientInfo.version` from the connected MCP client |
| `request_id` | string (optional) | JSON-encoded JSON-RPC request id, when present |
| `request_id_length` | number (optional) | Original request id length when `request_id` is truncated |
| `request_id_truncated` | boolean (optional) | `true` when `request_id` was shortened for the audit record |
| `arg_keys` | string[] | Ordered list of argument names supplied to the tool |
| `arg_key_lengths` | object (optional) | Original key lengths for truncated argument names |
| `arg_keys_truncated` | boolean (optional) | `true` when argument names or the argument-key list were truncated |
| `arg_key_truncation_reasons` | string[] (optional) | Stable truncation reason codes for argument-key truncation |
| `arg_lengths` | object | Per-argument length sketch — string→char count, array→element count, object→key count, scalar→0 |
| `arg_values` | object (optional) | Full argument payload. Present only when `--audit-log-include-values` is enabled |
| `arg_values` | object (optional) | Redacted and budgeted argument payload. Present only when `--audit-log-include-values` is enabled |
| `arg_values_redacted` | boolean (optional) | `true` when secret-like keys or token patterns were replaced with `[REDACTED]` |
| `arg_values_truncated` | boolean (optional) | `true` when include-values output hit a depth, count, string, or byte budget |
| `arg_values_truncation_reasons` | string[] (optional) | Stable truncation reason codes when `arg_values_truncated` is true |
| `arg_values_serialized_bytes` | number (optional) | Approximate serialized-byte budget consumed by retained `arg_values` |
| `arg_values_max_bytes` | number (optional) | Maximum serialized-byte budget for retained `arg_values` |
| `result_count` | number (optional) | `structuredContent.count` or `structuredContent.results.length` for successful calls; omitted otherwise |
| `elapsed_ms` | number | Wall-clock duration in milliseconds (3 decimal places) |
| `error_code` | number | `0` on success, `1` for MCP tool errors (`isError: true`), or the verbatim JSON-RPC error code (e.g. `-32602`) |
Expand Down Expand Up @@ -3582,8 +3592,8 @@ MCP ツールで catch-all まで突き抜けた例外(想定外の SQLite 例
| フラグ | 既定 | 効果 |
|---|---|---|
| `--audit-log <path>` | (無効) | 監査出力を有効化し `<path>` に JSONL を書き出す。親ディレクトリは無ければ自動作成 |
| `--audit-log-include-values` | off | 引数の値をレコードに含める。`--audit-log` 必須。既定で off なのは `query` / `name` 引数にソース片や secret 風の文字列が入りうるため |
| `--audit-log-max-bytes <n>` | `52428800` (50 MiB) | ローテーションの閾値(バイト)。最小値は 4096 |
| `--audit-log-include-values` | off | redaction 済みの引数値をレコードに含める。`--audit-log` 必須。既定で off なのは `query` / `name` 引数にソース片や secret 風の文字列が入りうるため |
| `--audit-log-max-bytes <n>` | `52428800` (50 MiB) | ローテーションの閾値(バイト)。4096 以上 1073741824 以下 |

各レコードは独立した行に 1 つの JSON オブジェクトとして書き出され、フィールドは次の通りです。

Expand All @@ -3594,9 +3604,19 @@ MCP ツールで catch-all まで突き抜けた例外(想定外の SQLite 例
| `caller` | string(任意) | 接続中クライアントの `initialize.clientInfo.name` |
| `caller_version` | string(任意) | 接続中クライアントの `initialize.clientInfo.version` |
| `request_id` | string(任意) | JSON-RPC リクエスト id を JSON エンコードしたもの |
| `request_id_length` | number(任意) | `request_id` が短縮された場合の元の長さ |
| `request_id_truncated` | boolean(任意) | audit record 用に `request_id` が短縮された場合に `true` |
| `arg_keys` | string[] | ツールへ渡された引数名の順序付きリスト |
| `arg_key_lengths` | object(任意) | 短縮された引数名の元の長さ |
| `arg_keys_truncated` | boolean(任意) | 引数名または引数キー一覧が短縮された場合に `true` |
| `arg_key_truncation_reasons` | string[](任意) | 引数キー truncation の安定した reason code |
| `arg_lengths` | object | 引数ごとの長さ概算(文字列→文字数、配列→要素数、オブジェクト→キー数、スカラ→0) |
| `arg_values` | object(任意) | 引数本体。`--audit-log-include-values` 指定時のみ付与 |
| `arg_values` | object(任意) | redaction および budget 適用済みの引数本体。`--audit-log-include-values` 指定時のみ付与 |
| `arg_values_redacted` | boolean(任意) | secret 風のキーまたは token pattern が `[REDACTED]` に置き換えられた場合に `true` |
| `arg_values_truncated` | boolean(任意) | include-values 出力が depth / count / string / byte budget に到達した場合に `true` |
| `arg_values_truncation_reasons` | string[](任意) | `arg_values_truncated` が true の場合の安定した truncation reason code |
| `arg_values_serialized_bytes` | number(任意) | 保持された `arg_values` が消費した概算 serialized byte budget |
| `arg_values_max_bytes` | number(任意) | 保持される `arg_values` の最大 serialized byte budget |
| `result_count` | number(任意) | 成功時の `structuredContent.count` または `structuredContent.results.length`。それ以外は省略 |
| `elapsed_ms` | number | ウォールクロック経過ミリ秒(小数 3 桁) |
| `error_code` | number | 成功=`0`、MCP ツールエラー(`isError: true`)=`1`、JSON-RPC エラー=そのコード(例: `-32000` のレート制限、`-32602` の引数エラー) |
Expand Down
20 changes: 20 additions & 0 deletions changelog.d/unreleased/3067.security.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
---
category: security
issues:
- 3067
affected:
- src/CodeIndex/Mcp/AuditLogSink.cs
- src/CodeIndex/Mcp/McpServer.cs
- tests/CodeIndex.Tests/AuditLogSinkTests.cs
- tests/CodeIndex.Tests/McpAuditLogTests.cs
- USER_GUIDE.md
- DEVELOPER_GUIDE.md
---

## English

- **MCP audit-log include-values now redacts secret-like values (#3067)** — include-values audit records replace secret-like argument keys and known token patterns with `[REDACTED]` and mark the record with `arg_values_redacted`.

## 日本語

- **MCP 監査ログの include-values が secret 風の値を redaction するようになりました (#3067)** — include-values の監査レコードは secret 風の引数キーや既知 token pattern を `[REDACTED]` に置き換え、`arg_values_redacted` で記録します。
20 changes: 20 additions & 0 deletions changelog.d/unreleased/3106.security.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
---
category: security
issues:
- 3106
affected:
- src/CodeIndex/Mcp/AuditLogSink.cs
- src/CodeIndex/Mcp/McpServer.cs
- tests/CodeIndex.Tests/AuditLogSinkTests.cs
- tests/CodeIndex.Tests/McpAuditLogTests.cs
- USER_GUIDE.md
- DEVELOPER_GUIDE.md
---

## English

- **MCP audit-log include-values now budgets argument payloads (#3106)** — audit argument values are cloned through depth, count, string, and serialized-byte budgets before they are written, with truncation metadata recorded on oversized payloads.

## 日本語

- **MCP 監査ログの include-values が引数 payload に budget を適用するようになりました (#3106)** — 監査ログの引数値は書き出し前に depth / count / string / serialized-byte budget を通して clone され、過大 payload では truncation metadata を記録します。
18 changes: 18 additions & 0 deletions changelog.d/unreleased/3180.security.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
---
category: security
issues:
- 3180
affected:
- src/CodeIndex/Mcp/AuditLogSink.cs
- src/CodeIndex/Cli/ProgramRunner.cs
- tests/CodeIndex.Tests/ProgramRunnerTests.cs
- USER_GUIDE.md
---

## English

- **MCP audit-log rotation now rejects oversized caps (#3180)** — `--audit-log-max-bytes` now enforces a documented 1 GiB upper bound while preserving the existing 4 KiB lower bound.

## 日本語

- **MCP 監査ログのローテーション上限が過大値を拒否するようになりました (#3180)** — `--audit-log-max-bytes` は既存の 4 KiB 下限を維持しつつ、ドキュメント化された 1 GiB 上限を超える値を拒否します。
20 changes: 20 additions & 0 deletions changelog.d/unreleased/3237.security.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
---
category: security
issues:
- 3237
affected:
- src/CodeIndex/Mcp/AuditLogSink.cs
- src/CodeIndex/Mcp/McpServer.cs
- tests/CodeIndex.Tests/AuditLogSinkTests.cs
- tests/CodeIndex.Tests/McpAuditLogTests.cs
- USER_GUIDE.md
- DEVELOPER_GUIDE.md
---

## English

- **MCP audit log events now cap field and record sizes (#3237)** — audit records now bound request ids, argument key lists, nested include-values keys, and oversized event payloads while marking truncation explicitly.

## 日本語

- **MCP 監査ログイベントが field と record size を制限するようになりました (#3237)** — audit record は request id、引数キー一覧、include-values 内の nested key、過大 event payload を制限し、truncation を明示的に記録します。
5 changes: 3 additions & 2 deletions src/CodeIndex/Cli/ProgramRunner.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2833,9 +2833,10 @@ private static bool TryConsumeAuditLogMaxBytes(
}

if (!long.TryParse(raw, System.Globalization.NumberStyles.Integer, System.Globalization.CultureInfo.InvariantCulture, out var parsed)
|| parsed < AuditLogSink.MinMaxBytes)
|| parsed < AuditLogSink.MinMaxBytes
|| parsed > AuditLogSink.MaxMaxBytes)
{
error = $"Error: --audit-log-max-bytes must be an integer >= {AuditLogSink.MinMaxBytes}.";
error = $"Error: --audit-log-max-bytes must be an integer between {AuditLogSink.MinMaxBytes} and {AuditLogSink.MaxMaxBytes}.";
return false;
}

Expand Down
Loading
Loading