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 USER_GUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -1067,11 +1067,11 @@ cdidx report --output report.tgz
cdidx report --output report.tgz --json
```

`cdidx report --output <path>` packages a redacted `.tar.gz` you can attach to a GitHub issue. The bundle includes the cdidx version, .NET runtime, OS / process architecture, and a `schema.txt` listing each SQLite table with its row count (no user content). It also tails the recent cdidx lifecycle log (`stderr-yyyyMMdd.log`), with `cwd=` and `args=` lines replaced by `[redacted]` so working-directory paths and literal query strings never leave your machine.
`cdidx report --output <path>` packages a redacted `.tar.gz` you can attach to a GitHub issue. The bundle includes the cdidx version, .NET runtime, OS / process architecture, and a `schema.txt` listing each SQLite table with its row count (no user content). It also tails the recent cdidx lifecycle log (`stderr-yyyyMMdd.log`), with the database path, lifecycle-log source directory, `process_path=`, `base_dir=`, `cwd=`, `db=`, `path=`, and `args=` lines replaced by `[redacted]` so local filesystem paths and literal query strings never leave your machine.

| Flag | Default | Effect |
|---|---|---|
| `--output <path>` / `-o <path>` | (required) | Destination `.tar.gz`. The directory is created if missing. |
| `--output <path>` / `-o <path>` | (required) | Destination `.tar.gz`. The directory is created if missing; on POSIX, the archive and tar entries are owner-readable/writable only. |
| `--db <path>` | `.cdidx/codeindex.db` | Override the database whose schema is summarized. If absent, `schema.txt` records that no DB was found. |
| `--log-lines <n>` | `200` | How many trailing lifecycle-log lines to include (`0` disables the tail). |
| `--no-log` | | Skip the lifecycle log entirely. |
Expand Down Expand Up @@ -3172,11 +3172,11 @@ cdidx report --output report.tgz
cdidx report --output report.tgz --json
```

`cdidx report --output <path>` は GitHub Issue に添付できる匿名化済み `.tar.gz` を生成します。バンドルには cdidx のバージョン、.NET ランタイム、OS / プロセスアーキテクチャ、各 SQLite テーブル名と整数の行数のみを記録した `schema.txt`(ユーザコンテンツは含まれません)が入ります。さらに直近のライフサイクルログ(`stderr-yyyyMMdd.log`)の末尾も含まれますが、`cwd=` と `args=` 行は `[redacted]` に置換されるため、作業ディレクトリのパスや具体的なクエリ文字列が端末から外に出ることはありません
`cdidx report --output <path>` は GitHub Issue に添付できる匿名化済み `.tar.gz` を生成します。バンドルには cdidx のバージョン、.NET ランタイム、OS / プロセスアーキテクチャ、各 SQLite テーブル名と整数の行数のみを記録した `schema.txt`(ユーザコンテンツは含まれません)が入ります。さらに直近のライフサイクルログ(`stderr-yyyyMMdd.log`)の末尾も含まれますが、DB パス、ライフサイクルログの source directory、`process_path=`、`base_dir=`、`cwd=`、`db=`、`path=`、`args=` 行は `[redacted]` に置換されるため、ローカルファイルシステムのパスや具体的なクエリ文字列が端末から外に出ることはありません

| フラグ | 既定値 | 効果 |
|---|---|---|
| `--output <path>` / `-o <path>` | (必須) | 出力先 `.tar.gz`。親ディレクトリが無ければ作成します。 |
| `--output <path>` / `-o <path>` | (必須) | 出力先 `.tar.gz`。親ディレクトリが無ければ作成します。POSIX では archive と tar entry は owner の読み書きのみになります。 |
| `--db <path>` | `.cdidx/codeindex.db` | スキーマ要約対象の DB を上書きします。存在しなければ `schema.txt` に「DB が見つからなかった」旨が記録されます。 |
| `--log-lines <n>` | `200` | ライフサイクルログ末尾を何行含めるか(`0` で末尾を含めません)。 |
| `--no-log` | | ライフサイクルログを完全に省略します。 |
Expand Down
17 changes: 17 additions & 0 deletions changelog.d/unreleased/2836.security.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
---
category: security
issues:
- 2836
affected:
- src/CodeIndex/Cli/ReportCommandRunner.cs
- tests/CodeIndex.Tests/ReportCommandRunnerTests.cs
- USER_GUIDE.md
---

## English

- **Report bundles now redact database and log-directory paths (#2836)** - `cdidx report` no longer writes local SQLite database paths or lifecycle-log source directories into the generated support bundle.

## 日本語

- **report bundle で DB パスとログディレクトリのパスを伏せるようになりました (#2836)** - `cdidx report` は生成するサポート用 bundle に、ローカル SQLite DB パスやライフサイクルログの source directory を書き込まなくなりました。
17 changes: 17 additions & 0 deletions changelog.d/unreleased/2840.security.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
---
category: security
issues:
- 2840
affected:
- src/CodeIndex/Cli/ReportCommandRunner.cs
- tests/CodeIndex.Tests/ReportCommandRunnerTests.cs
- USER_GUIDE.md
---

## English

- **Report log tails now redact lifecycle executable paths (#2840)** - `cdidx report` now redacts `process_path=`, `base_dir=`, and other path-bearing lifecycle fields by default, while `--include-args` only restores literal `args=` lines.

## 日本語

- **report のログ末尾でライフサイクル実行パスを伏せるようになりました (#2840)** - `cdidx report` は既定で `process_path=`、`base_dir=` などの path-bearing lifecycle fields を伏せ、`--include-args` は `args=` 行だけを literal に戻すようになりました。
17 changes: 17 additions & 0 deletions changelog.d/unreleased/2841.security.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
---
category: security
issues:
- 2841
affected:
- src/CodeIndex/Cli/ReportCommandRunner.cs
- tests/CodeIndex.Tests/ReportCommandRunnerTests.cs
- USER_GUIDE.md
---

## English

- **Report bundles now use owner-only permissions (#2841)** - `cdidx report` creates support archives and tar entries with user-read/write permissions only on POSIX filesystems.

## 日本語

- **report bundle が owner-only permission を使うようになりました (#2841)** - `cdidx report` は POSIX ファイルシステム上で、サポート用 archive と tar entry を user の読み書きのみの permission で作成します。
48 changes: 37 additions & 11 deletions src/CodeIndex/Cli/ReportCommandRunner.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ public static class ReportCommandRunner
{
internal const int DefaultLogLines = 200;
internal const string RedactedPlaceholder = "[redacted]";
internal const UnixFileMode BundleFileMode = UnixFileMode.UserRead | UnixFileMode.UserWrite;

public static int Run(string[] cmdArgs, JsonSerializerOptions jsonOptions, string? appVersion = null)
{
Expand Down Expand Up @@ -169,8 +170,8 @@ internal static string BuildReadme(string version, bool includeLog, bool include
{
sb.AppendLine("- `log/stderr-recent.log` — last N lines of the cdidx lifecycle log");
sb.AppendLine(includeArgs
? " (includes literal `args=` lines; rerun without `--include-args` to redact them)."
: " (`args=` lines are redacted; rerun with `--include-args` to keep them literal).");
? " (includes literal `args=` lines; path-bearing lifecycle fields stay redacted)."
: " (`args=` and path-bearing lifecycle fields are redacted; rerun with `--include-args` to keep arguments literal).");
}
else
{
Expand All @@ -180,6 +181,7 @@ internal static string BuildReadme(string version, bool includeLog, bool include
sb.AppendLine("## Redactions");
sb.AppendLine();
sb.AppendLine("- Indexed source content, file paths, query strings, and `args=` lines are not included by default.");
sb.AppendLine("- Path-bearing lifecycle fields such as `process_path=`, `base_dir=`, `cwd=`, `db=`, and `path=` are redacted by default.");
sb.AppendLine("- Schema reporting only emits table names and integer row counts.");
return sb.ToString();
}
Expand All @@ -188,7 +190,7 @@ internal static (string Text, List<ReportSchemaTable> Tables, string? DbPath, bo
{
if (!File.Exists(LongPath.EnsureWindowsPrefix(dbPath)))
{
var missingText = $"no SQLite index found at: {dbPath}\nRun `cdidx index <projectPath>` first if you want schema details attached.\n";
var missingText = $"no SQLite index found at: {RedactedPlaceholder}\nRun `cdidx index <projectPath>` first if you want schema details attached.\n";
return (missingText, new List<ReportSchemaTable>(), dbPath, false);
}

Expand Down Expand Up @@ -228,7 +230,7 @@ internal static (string Text, List<ReportSchemaTable> Tables, string? DbPath, bo
}

var sb = new StringBuilder();
sb.AppendLine($"database: {Path.GetFullPath(dbPath)}");
sb.AppendLine($"database: {RedactedPlaceholder}");
sb.AppendLine($"tables : {tables.Count}");
sb.AppendLine();
sb.AppendLine("name | row_count");
Expand All @@ -244,14 +246,14 @@ internal static string BuildRecentLogTail(int maxLines, bool includeArgs, out in
linesIncluded = 0;
var logDir = GlobalToolLog.ResolveLogDirectoryForReport();
if (string.IsNullOrWhiteSpace(logDir) || !Directory.Exists(logDir))
return $"no cdidx lifecycle log directory found (looked at: {logDir ?? "<unknown>"}).\n";
return $"no cdidx lifecycle log directory found (looked at: {RedactedPlaceholder}).\n";

var logFiles = new DirectoryInfo(logDir)
.EnumerateFiles("stderr-*.log", SearchOption.TopDirectoryOnly)
.OrderByDescending(f => f.Name, StringComparer.Ordinal)
.ToList();
if (logFiles.Count == 0)
return $"no cdidx lifecycle log files found in: {logDir}\n";
return $"no cdidx lifecycle log files found in: {RedactedPlaceholder}\n";

var collected = new LinkedList<string>();
foreach (var file in logFiles)
Expand All @@ -273,11 +275,11 @@ internal static string BuildRecentLogTail(int maxLines, bool includeArgs, out in

var sb = new StringBuilder();
sb.AppendLine($"# cdidx lifecycle log (last {collected.Count} lines, newest last)");
sb.AppendLine($"# source directory: {logDir}");
sb.AppendLine($"# source directory: {RedactedPlaceholder}");
sb.AppendLine();
foreach (var line in collected)
{
sb.AppendLine(includeArgs ? line : RedactSensitiveFields(line));
sb.AppendLine(includeArgs ? RedactPathFields(line) : RedactSensitiveFields(line));
}
linesIncluded = collected.Count;
return sb.ToString();
Expand All @@ -286,7 +288,16 @@ internal static string BuildRecentLogTail(int maxLines, bool includeArgs, out in
internal static string RedactSensitiveFields(string line)
{
var redacted = RedactKeyValue(line, "args=");
redacted = RedactKeyValue(redacted, "cwd=");
return RedactPathFields(redacted);
}

private static string RedactPathFields(string line)
{
var redacted = RedactKeyValue(line, "cwd=");
redacted = RedactKeyValue(redacted, "process_path=");
redacted = RedactKeyValue(redacted, "base_dir=");
redacted = RedactKeyValue(redacted, "db=");
redacted = RedactKeyValue(redacted, "path=");
return redacted;
}

Expand All @@ -304,7 +315,22 @@ private static void WriteBundle(string outputPath, ReportBundle bundle)
if (!string.IsNullOrEmpty(dir))
Directory.CreateDirectory(dir);

using var fileStream = new FileStream(outputPath, FileMode.Create, FileAccess.Write, FileShare.None);
if (!OperatingSystem.IsWindows() && File.Exists(outputPath))
File.SetUnixFileMode(outputPath, BundleFileMode);

var streamOptions = new FileStreamOptions
{
Mode = FileMode.Create,
Access = FileAccess.Write,
Share = FileShare.None,
};
if (!OperatingSystem.IsWindows())
streamOptions.UnixCreateMode = BundleFileMode;

using var fileStream = new FileStream(outputPath, streamOptions);
if (!OperatingSystem.IsWindows())
File.SetUnixFileMode(outputPath, BundleFileMode);

using var gz = new GZipStream(fileStream, CompressionLevel.Optimal);
using var tar = new TarWriter(gz, TarEntryFormat.Pax, leaveOpen: true);

Expand All @@ -313,7 +339,7 @@ private static void WriteBundle(string outputPath, ReportBundle bundle)
var entry = new PaxTarEntry(TarEntryType.RegularFile, name)
{
DataStream = new MemoryStream(bytes, writable: false),
Mode = UnixFileMode.UserRead | UnixFileMode.UserWrite | UnixFileMode.GroupRead | UnixFileMode.OtherRead,
Mode = BundleFileMode,
ModificationTime = DateTimeOffset.UtcNow,
};
tar.WriteEntry(entry);
Expand Down
Loading
Loading