From 39fab86a9445e19baebc4019ca2bbe859826468b Mon Sep 17 00:00:00 2001 From: Widthdom Date: Sat, 6 Jun 2026 14:41:29 +0900 Subject: [PATCH 1/2] Fix version metadata fallback for #3025 --- changelog.d/unreleased/3025.fixed.md | 16 +++++++++ src/CodeIndex/Cli/ConsoleUi.cs | 21 ++++++++++-- tests/CodeIndex.Tests/ConsoleUiTests.cs | 43 +++++++++++++++++++++++++ 3 files changed, 78 insertions(+), 2 deletions(-) create mode 100644 changelog.d/unreleased/3025.fixed.md diff --git a/changelog.d/unreleased/3025.fixed.md b/changelog.d/unreleased/3025.fixed.md new file mode 100644 index 0000000000..09c01df2d1 --- /dev/null +++ b/changelog.d/unreleased/3025.fixed.md @@ -0,0 +1,16 @@ +--- +category: fixed +issues: + - 3025 +affected: + - src/CodeIndex/Cli/ConsoleUi.cs + - tests/CodeIndex.Tests/ConsoleUiTests.cs +--- + +## English + +- **Version metadata loading now falls back on invalid files (#3025)** — malformed, missing, unreadable, or schema-invalid `version.json` files no longer throw from CLI version-display paths and instead return the existing `0.0.0` fallback. + +## 日本語 + +- **不正な version metadata 読み込み時に fallback するようになりました (#3025)** — malformed、missing、unreadable、schema-invalid な `version.json` でも CLI の version 表示経路で例外を投げず、既存の `0.0.0` fallback を返します。 diff --git a/src/CodeIndex/Cli/ConsoleUi.cs b/src/CodeIndex/Cli/ConsoleUi.cs index 9ea82aebce..8b29ef3bac 100644 --- a/src/CodeIndex/Cli/ConsoleUi.cs +++ b/src/CodeIndex/Cli/ConsoleUi.cs @@ -67,6 +67,7 @@ public static class ConsoleUi public const string DisableProgressEnvironmentVariable = "CDIDX_DISABLE_PROGRESS"; public const string PrefersReducedMotionEnvironmentVariable = "PREFERS_REDUCED_MOTION"; public const int SummaryLabelWidth = 9; + private const string FallbackVersion = "0.0.0"; private static readonly (string Command, string Usage)[] CommandUsageLines = [ @@ -714,13 +715,29 @@ public static string LoadVersion() } var ioPath = LongPath.EnsureWindowsPrefix(path); if (File.Exists(ioPath)) + return LoadVersionFromFile(ioPath); + + return FallbackVersion; + } + + internal static string LoadVersionFromFile(string ioPath) + { + try { var json = File.ReadAllText(ioPath); using var doc = System.Text.Json.JsonDocument.Parse(json); if (doc.RootElement.TryGetProperty("version", out var ver)) - return ver.GetString() ?? "0.0.0"; + return ver.GetString() ?? FallbackVersion; + } + catch (Exception ex) when (ex is IOException + or UnauthorizedAccessException + or System.Text.Json.JsonException + or InvalidOperationException) + { + return FallbackVersion; } - return "0.0.0"; + + return FallbackVersion; } /// diff --git a/tests/CodeIndex.Tests/ConsoleUiTests.cs b/tests/CodeIndex.Tests/ConsoleUiTests.cs index 3615332ed7..9ca758060e 100644 --- a/tests/CodeIndex.Tests/ConsoleUiTests.cs +++ b/tests/CodeIndex.Tests/ConsoleUiTests.cs @@ -640,6 +640,49 @@ public void LoadVersion_ReturnsActualVersion_NotFallback() Assert.Contains('.', version); } + [Fact] + public void LoadVersionFromFile_MalformedJson_ReturnsFallback() + { + var path = WriteTempVersionJson("""{"version":"""); + try + { + Assert.Equal("0.0.0", ConsoleUi.LoadVersionFromFile(path)); + } + finally + { + File.Delete(path); + } + } + + [Fact] + public void LoadVersionFromFile_NonStringVersion_ReturnsFallback() + { + var path = WriteTempVersionJson("""{"version":{}}"""); + try + { + Assert.Equal("0.0.0", ConsoleUi.LoadVersionFromFile(path)); + } + finally + { + File.Delete(path); + } + } + + [Fact] + public void LoadVersionFromFile_MissingFile_ReturnsFallback() + { + var path = Path.Combine(Path.GetTempPath(), $"cdidx_missing_version_{Guid.NewGuid():N}.json"); + + Assert.Equal("0.0.0", ConsoleUi.LoadVersionFromFile(path)); + } + + private static string WriteTempVersionJson(string content) + { + var path = Path.Combine(Path.GetTempPath(), $"cdidx_version_{Guid.NewGuid():N}.json"); + File.WriteAllText(path, content, Encoding.UTF8); + return path; + } + [Fact] public void LoadBuildMetadata_PopulatesAllFields() { From d169a4ee4d002c560c285a16cb6aaa36643677ed Mon Sep 17 00:00:00 2001 From: Widthdom Date: Sat, 6 Jun 2026 14:44:12 +0900 Subject: [PATCH 2/2] Bound version metadata loading for #3244 --- changelog.d/unreleased/3244.fixed.md | 16 ++++++++++++ src/CodeIndex/Cli/ConsoleUi.cs | 17 ++++++++++--- tests/CodeIndex.Tests/ConsoleUiTests.cs | 34 +++++++++++++++++++++++++ 3 files changed, 64 insertions(+), 3 deletions(-) create mode 100644 changelog.d/unreleased/3244.fixed.md diff --git a/changelog.d/unreleased/3244.fixed.md b/changelog.d/unreleased/3244.fixed.md new file mode 100644 index 0000000000..8b902792c6 --- /dev/null +++ b/changelog.d/unreleased/3244.fixed.md @@ -0,0 +1,16 @@ +--- +category: fixed +issues: + - 3244 +affected: + - src/CodeIndex/Cli/ConsoleUi.cs + - tests/CodeIndex.Tests/ConsoleUiTests.cs +--- + +## English + +- **Version metadata reads are now bounded (#3244)** — `version.json` loading now reads at most 16 KiB and parses with an explicit JSON depth limit before falling back to `0.0.0` on invalid metadata. + +## 日本語 + +- **version metadata の読み込みに上限を設定しました (#3244)** — `version.json` の読み込みは最大 16 KiB になり、明示的な JSON depth 上限で parse したうえで、不正な metadata では `0.0.0` に fallback します。 diff --git a/src/CodeIndex/Cli/ConsoleUi.cs b/src/CodeIndex/Cli/ConsoleUi.cs index 8b29ef3bac..fe66e6528e 100644 --- a/src/CodeIndex/Cli/ConsoleUi.cs +++ b/src/CodeIndex/Cli/ConsoleUi.cs @@ -4,6 +4,7 @@ using System.Reflection; using System.Runtime.InteropServices; using System.Text; +using System.Text.Json; namespace CodeIndex.Cli; @@ -67,8 +68,15 @@ public static class ConsoleUi public const string DisableProgressEnvironmentVariable = "CDIDX_DISABLE_PROGRESS"; public const string PrefersReducedMotionEnvironmentVariable = "PREFERS_REDUCED_MOTION"; public const int SummaryLabelWidth = 9; + internal const int MaxVersionJsonBytes = 16 * 1024; + internal const int MaxVersionJsonDepth = 8; private const string FallbackVersion = "0.0.0"; + private static readonly JsonDocumentOptions VersionJsonDocumentOptions = new() + { + MaxDepth = MaxVersionJsonDepth, + }; + private static readonly (string Command, string Usage)[] CommandUsageLines = [ ("index", "cdidx index [--db ] [--rebuild] [--optimize] [--verbose] [--dry-run] [--force] [--quiet] [--json] [--memory-trace] [--duration-format ] [--notify ] [--max-file-bytes ] [--max-symbols-per-file ] [--follow-symlinks ] [--include-symbol-kind [,]] [--exclude-symbol-kind [,]] [--watch [--debounce ]]"), @@ -724,14 +732,17 @@ internal static string LoadVersionFromFile(string ioPath) { try { - var json = File.ReadAllText(ioPath); - using var doc = System.Text.Json.JsonDocument.Parse(json); + var json = DataDirectorySecurity.ReadTextWithinLimit(ioPath, MaxVersionJsonBytes); + if (json is null) + return FallbackVersion; + + using var doc = JsonDocument.Parse(json, VersionJsonDocumentOptions); if (doc.RootElement.TryGetProperty("version", out var ver)) return ver.GetString() ?? FallbackVersion; } catch (Exception ex) when (ex is IOException or UnauthorizedAccessException - or System.Text.Json.JsonException + or JsonException or InvalidOperationException) { return FallbackVersion; diff --git a/tests/CodeIndex.Tests/ConsoleUiTests.cs b/tests/CodeIndex.Tests/ConsoleUiTests.cs index 9ca758060e..7d9f7f1bd6 100644 --- a/tests/CodeIndex.Tests/ConsoleUiTests.cs +++ b/tests/CodeIndex.Tests/ConsoleUiTests.cs @@ -676,6 +676,40 @@ public void LoadVersionFromFile_MissingFile_ReturnsFallback() Assert.Equal("0.0.0", ConsoleUi.LoadVersionFromFile(path)); } + [Fact] + public void LoadVersionFromFile_OversizedJson_ReturnsFallback() + { + var path = WriteTempVersionJson("{\"version\":\"" + new string('1', ConsoleUi.MaxVersionJsonBytes) + "\"}"); + try + { + Assert.Equal("0.0.0", ConsoleUi.LoadVersionFromFile(path)); + } + finally + { + File.Delete(path); + } + } + + [Fact] + public void LoadVersionFromFile_TooDeepJson_ReturnsFallback() + { + var nesting = ConsoleUi.MaxVersionJsonDepth + 1; + var path = WriteTempVersionJson( + """{"version":"1.2.3","nested":""" + + new string('[', nesting) + + "0" + + new string(']', nesting) + + "}"); + try + { + Assert.Equal("0.0.0", ConsoleUi.LoadVersionFromFile(path)); + } + finally + { + File.Delete(path); + } + } + private static string WriteTempVersionJson(string content) { var path = Path.Combine(Path.GetTempPath(), $"cdidx_version_{Guid.NewGuid():N}.json");