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/3109.security.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
---
category: security
issues:
- 3109
affected:
- src/CodeIndex/Cli/ConsoleUi.cs
- src/CodeIndex/Cli/ProgramRunner.cs
- tests/CodeIndex.Tests/ProgramRunnerTests.cs
---

## English

- **Doctor output now truncates oversized environment values (#3109)** — `cdidx doctor` bounds terminal and `CDIDX_*` environment values before printing them, with an explicit original-length marker when truncation occurs.

## 日本語

- **doctor 出力で過大な環境変数値を短縮するようになりました (#3109)** — `cdidx doctor` は terminal および `CDIDX_*` 環境変数値を表示前に制限し、短縮時は元の長さを示す marker を付けます。
17 changes: 17 additions & 0 deletions changelog.d/unreleased/3110.security.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
---
category: security
issues:
- 3110
affected:
- src/CodeIndex/Cli/ConsoleUi.cs
- src/CodeIndex/Cli/QueryCommandRunner.cs
- tests/CodeIndex.Tests/ProgramRunnerTests.cs
---

## English

- **Query default environment parse errors now truncate raw values (#3110)** — invalid `CDIDX_DEFAULT_*` numeric defaults report a bounded display value instead of embedding an arbitrarily long environment string.

## 日本語

- **query default 環境変数の parse error で raw 値を短縮するようになりました (#3110)** — 不正な `CDIDX_DEFAULT_*` 数値 default は、任意長の環境変数文字列をそのまま埋め込まず、制限済みの表示値を報告します。
17 changes: 17 additions & 0 deletions changelog.d/unreleased/3123.security.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
---
category: security
issues:
- 3123
affected:
- src/CodeIndex/Cli/ConsoleUi.cs
- src/CodeIndex/Cli/ProgramRunner.cs
- tests/CodeIndex.Tests/ProgramRunnerTests.cs
---

## English

- **Query trace parameters now cap captured values and path arrays (#3123)** — trace JSON bounds parameter strings and repeated `--path` / `--exclude-path` arrays, and emits truncation metadata when values or counts are reduced.

## 日本語

- **query trace parameter が取得値と path 配列を制限するようになりました (#3123)** — trace JSON は parameter 文字列と繰り返し指定された `--path` / `--exclude-path` 配列を制限し、値や件数が削減された場合は truncation metadata を出します。
16 changes: 16 additions & 0 deletions changelog.d/unreleased/3164.security.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
---
category: security
issues:
- 3164
affected:
- src/CodeIndex/Cli/ActiveWorkspace.cs
- tests/CodeIndex.Tests/WorkspaceCommandRunnerTests.cs
---

## English

- **Active workspace environment loading now caps and validates paths (#3164)** — oversized or invalid `CDIDX_ACTIVE_WORKSPACE` values are ignored with bounded warnings instead of throwing or leaking huge paths.

## 日本語

- **active workspace の環境変数読み込みで path を制限・検証するようになりました (#3164)** — 過大または不正な `CDIDX_ACTIVE_WORKSPACE` 値は、例外や巨大 path の露出ではなく、制限済み warning とともに無視されます。
16 changes: 16 additions & 0 deletions changelog.d/unreleased/3166.security.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
---
category: security
issues:
- 3166
affected:
- src/CodeIndex/Cli/GlobalToolLog.cs
- tests/CodeIndex.Tests/GlobalToolLogTests.cs
---

## English

- **Persistent stderr mirroring now truncates oversized writes (#3166)** — global tool logs bound each mirrored stderr string write while preserving the original console stderr output.

## 日本語

- **永続 stderr mirror で過大な write を短縮するようになりました (#3166)** — global tool log は console stderr の元出力を維持しつつ、mirror される stderr 文字列 write ごとにサイズを制限します。
16 changes: 16 additions & 0 deletions changelog.d/unreleased/3218.security.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
---
category: security
issues:
- 3218
affected:
- src/CodeIndex/Cli/ProgramRunner.cs
- tests/CodeIndex.Tests/ProgramRunnerTests.cs
---

## English

- **`.cdidx-version` read warnings now avoid raw paths and exception messages (#3218)** — version-pin read failures and malformed pin warnings now report sanitized reasons without printing full local paths.

## 日本語

- **`.cdidx-version` 読み取り warning が raw path と例外 message を避けるようになりました (#3218)** — version pin の読み取り失敗や不正 pin warning は、完全なローカル path を出さずに sanitize 済みの理由を報告します。
16 changes: 16 additions & 0 deletions changelog.d/unreleased/3219.security.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
---
category: security
issues:
- 3219
affected:
- src/CodeIndex/Cli/ActiveWorkspace.cs
- tests/CodeIndex.Tests/WorkspaceCommandRunnerTests.cs
---

## English

- **Active workspace state warnings now avoid raw paths and exception messages (#3219)** — malformed state files now produce sanitized warnings with reset guidance instead of printing the state path or provider exception text.

## 日本語

- **active workspace state warning が raw path と例外 message を避けるようになりました (#3219)** — 不正な state file は、state path や provider 例外 text を出さず、reset guidance 付きの sanitize 済み warning を出します。
40 changes: 35 additions & 5 deletions src/CodeIndex/Cli/ActiveWorkspace.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ internal sealed record ActiveWorkspaceState(string Name, string Root, string DbP
internal static class ActiveWorkspace
{
internal const string EnvironmentVariable = "CDIDX_ACTIVE_WORKSPACE";
internal const int MaxEnvironmentPathChars = 4096;
private const int MaxStateBytes = 64 * 1024;
internal const int MaxStateJsonDepth = 16;
private static readonly JsonDocumentOptions StateJsonDocumentOptions = new()
Expand All @@ -31,7 +32,7 @@ internal static string StatePath
{
var envPath = Environment.GetEnvironmentVariable(EnvironmentVariable);
if (!string.IsNullOrWhiteSpace(envPath))
return new ActiveWorkspaceState("env", Path.GetDirectoryName(Path.GetFullPath(envPath)) ?? Environment.CurrentDirectory, Path.GetFullPath(envPath));
return LoadFromEnvironment(envPath);

var path = StatePath;
if (!File.Exists(LongPath.EnsureWindowsPrefix(path)))
Expand All @@ -42,7 +43,7 @@ internal static string StatePath
var text = DataDirectorySecurity.ReadTextWithinLimit(path, MaxStateBytes, FileShare.ReadWrite);
if (text is null)
{
WriteLoadWarning(path, $"file exceeds {MaxStateBytes} bytes");
WriteLoadWarning("state file", $"file exceeds {MaxStateBytes} bytes");
return null;
}

Expand All @@ -57,7 +58,7 @@ internal static string StatePath
}
catch (Exception ex) when (ex is JsonException or IOException or UnauthorizedAccessException or ArgumentException or NotSupportedException)
{
WriteLoadWarning(path, ex.Message);
WriteLoadWarning("state file", DescribeLoadFailure(ex));
return null;
}
}
Expand All @@ -72,6 +73,35 @@ internal static void Save(ActiveWorkspaceState state)
private static string? ReadString(JsonElement element, string name)
=> element.TryGetProperty(name, out var value) && value.ValueKind == JsonValueKind.String ? value.GetString() : null;

private static void WriteLoadWarning(string path, string reason)
=> Console.Error.WriteLine($"[cdidx] Ignoring active workspace state at {path}: {reason}");
private static ActiveWorkspaceState? LoadFromEnvironment(string envPath)
{
if (envPath.Length > MaxEnvironmentPathChars)
{
WriteLoadWarning($"environment variable {EnvironmentVariable}", $"value exceeds {MaxEnvironmentPathChars} characters");
return null;
}

try
{
var fullPath = Path.GetFullPath(envPath);
return new ActiveWorkspaceState("env", Path.GetDirectoryName(fullPath) ?? Environment.CurrentDirectory, fullPath);
}
catch (Exception ex) when (ex is ArgumentException or NotSupportedException or PathTooLongException)
{
WriteLoadWarning($"environment variable {EnvironmentVariable}", DescribeLoadFailure(ex));
return null;
}
}

private static string DescribeLoadFailure(Exception ex) => ex switch
{
JsonException => "invalid JSON",
UnauthorizedAccessException => "permission denied",
ArgumentException or NotSupportedException or PathTooLongException => "invalid path",
IOException => "read failed",
_ => "load failed",
};

private static void WriteLoadWarning(string source, string reason)
=> Console.Error.WriteLine($"[cdidx] Ignoring active workspace {source}: {ConsoleUi.FormatBoundedValue(reason)}. Hint: inspect or reset the active workspace configuration.");
}
25 changes: 25 additions & 0 deletions src/CodeIndex/Cli/ConsoleUi.cs
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,31 @@ private static readonly (string Command, string Note)[] CommandUsageNotes =
public static string FormatSummaryLine(string label, object? value, int labelWidth = SummaryLabelWidth, string indent = "")
=> $"{indent}{label.PadRight(labelWidth)}: {value}";

internal const int DefaultDiagnosticValueCharLimit = 120;

internal readonly record struct BoundedDisplayText(string Text, bool Truncated, int OriginalLength);

internal static BoundedDisplayText BoundDisplayText(string? value, int maxChars = DefaultDiagnosticValueCharLimit)
{
if (maxChars < 0)
throw new ArgumentOutOfRangeException(nameof(maxChars), maxChars, "Display limit must be non-negative.");

if (value == null)
return new BoundedDisplayText("<null>", Truncated: false, OriginalLength: 0);

if (value.Length <= maxChars)
return new BoundedDisplayText(value, Truncated: false, value.Length);

var marker = string.Create(CultureInfo.InvariantCulture, $"... <truncated; original length {value.Length} chars>");
var text = maxChars == 0
? marker.TrimStart('.', ' ')
: value[..maxChars] + marker;
return new BoundedDisplayText(text, Truncated: true, value.Length);
}

internal static string FormatBoundedValue(string? value, int maxChars = DefaultDiagnosticValueCharLimit)
=> BoundDisplayText(value, maxChars).Text;

private const int SpinnerFrameDelayMs = 100;
private const int SpinnerStopDelayMs = 20;
private const int ConsoleLineMargin = 1;
Expand Down
8 changes: 6 additions & 2 deletions src/CodeIndex/Cli/GlobalToolLog.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ internal static class GlobalToolLog
internal const string LogMaxSizeMbEnvironmentVariable = "CDIDX_LOG_MAX_SIZE_MB";
internal const string GlobalToolLogMaxBytesEnvironmentVariable = "CDIDX_GLOBAL_TOOL_LOG_MAX_BYTES";
private const long DefaultLogMaxSizeBytes = 50L * 1024L * 1024L;
internal const int MirroredStderrWriteMaxChars = 8192;
internal const int MaxLogSizeMb = 1024;
internal const long MaxLogSizeBytes = MaxLogSizeMb * 1024L * 1024L;
internal const int RedactionArgumentLengthLimit = 8192;
Expand Down Expand Up @@ -720,15 +721,18 @@ public override void Write(char value)
public override void Write(string? value)
{
TryWrite(() => primary.Write(value));
TryWrite(() => secondary.Write(value));
TryWrite(() => secondary.Write(FormatMirroredWrite(value)));
}

public override void WriteLine(string? value)
{
TryWrite(() => primary.WriteLine(value));
TryWrite(() => secondary.WriteLine(value));
TryWrite(() => secondary.WriteLine(FormatMirroredWrite(value)));
}

private static string? FormatMirroredWrite(string? value)
=> value == null ? null : ConsoleUi.FormatBoundedValue(value, MirroredStderrWriteMaxChars);

private static void TryWrite(Action write)
{
try
Expand Down
Loading
Loading