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
6 changes: 6 additions & 0 deletions .github/workflows/dotnet.yml
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,12 @@ jobs:
- name: Restore dependencies
run: dotnet restore FolderDiffIL4DotNet.sln

- name: Test NuGet audit gate
run: python3 -m unittest discover -s scripts/tests -p 'test_*.py'

- name: Audit NuGet dependencies
run: python3 scripts/nuget_audit_gate.py --solution FolderDiffIL4DotNet.sln

- name: Build
run: dotnet build FolderDiffIL4DotNet.sln --configuration Release --no-restore

Expand Down
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -32,5 +32,9 @@ api/toc.yml
# Node.js (Jest JS tests)
node_modules/

# Python
__pycache__/
*.py[cod]

# Claude Code runtime state
.claude/scheduled_tasks.lock
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),

#### Added

- **Transitive NuGet vulnerability CI gate** — The main CI workflow now audits every solution project for direct and transitive NuGet advisories, publishes the complete result in the log and GitHub job summary, and fails closed on High/Critical findings or invalid audit output. `FsCheck.Xunit` was upgraded from 2.16.6 to 3.3.3, removing the vulnerable `System.Net.Http` and `System.Security.Cryptography.X509Certificates` test dependency chain; the property tests were migrated to the FsCheck 3 fluent API. Local reproduction and the temporary-exception policy are documented in the testing guide. Affected: `.github/workflows/dotnet.yml`, `.gitignore`, `scripts/nuget_audit_gate.py`, `scripts/tests/test_nuget_audit_gate.py`, `FolderDiffIL4DotNet.Tests/FolderDiffIL4DotNet.Tests.csproj`, `FolderDiffIL4DotNet.Tests/PropertyBased/PropertyBasedTests.cs`, `FolderDiffIL4DotNet.Tests/Architecture/CiAutomationConfigurationTests.cs`, `doc/TESTING_GUIDE.md`.

- **First-class npm/Jest CI gate** — The main CI workflow now pins Node.js through `.node-version`, restores the exact `package-lock.json` graph with cached `npm ci`, runs the complete HTML-report Jest suite, and rejects High/Critical npm advisories. The lockfile refresh remediates the five findings reported when this work began. A newly published `brace-expansion` advisory (`GHSA-mh99-v99m-4gvg`) is isolated to development-only Jest tooling and has a documented exception that expires on 2026-08-31; every other High/Critical finding still fails the gate. Dependabot now opens weekly npm update pull requests that run the same checks. Affected: `.node-version`, `.github/workflows/dotnet.yml`, `.github/dependabot.yml`, `package.json`, `package-lock.json`, `npm-audit-exceptions.json`, `scripts/npm-audit-gate.js`, `doc/TESTING_GUIDE.md`. Tests: `npm_audit_gate.test.js`, `CiAutomationConfigurationTests`.

- **Opt-in `--fail-on-diff` CI gating** — Normal completed comparisons continue to return `0` by default. With `--fail-on-diff`, nildiff now returns dedicated exit code `5` when the final reportable Added/Removed/Modified sets are non-empty. The decision is made only after every enabled report, audit log, and post-process action completes, and ignored extensions or other suppressed/filtered differences do not trigger the gate. Affected: `Runner/CliParser.cs`, `Runner/CliOptions.cs`, `ProgramRunner.cs`, `Runner/ProgramRunner.Types.cs`, `Runner/ProgramRunner.Wizard.cs`, `Runner/ProgramRunner.HelpText.cs`, `README.md`, `USER_GUIDE.md`. Tests: `CliOptionsTests`, `ProgramRunnerTests`.
Expand Down Expand Up @@ -1678,6 +1680,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),

#### 追加

- **推移的 NuGet 脆弱性 CI ゲート** — メイン CI ワークフローはソリューション内の全 project を対象に NuGet の直接・推移的 advisory を監査し、完全な結果をログと GitHub job summary に公開して、High/Critical の検出または不正な監査出力を fail-closed で失敗させるようになりました。`FsCheck.Xunit` を 2.16.6 から 3.3.3 へ更新し、脆弱な `System.Net.Http` と `System.Security.Cryptography.X509Certificates` のテスト依存チェーンを解消しました。プロパティテストは FsCheck 3 fluent API へ移行しています。ローカル再現手順と一時例外ポリシーはテストガイドに記載しました。対象: `.github/workflows/dotnet.yml`, `.gitignore`, `scripts/nuget_audit_gate.py`, `scripts/tests/test_nuget_audit_gate.py`, `FolderDiffIL4DotNet.Tests/FolderDiffIL4DotNet.Tests.csproj`, `FolderDiffIL4DotNet.Tests/PropertyBased/PropertyBasedTests.cs`, `FolderDiffIL4DotNet.Tests/Architecture/CiAutomationConfigurationTests.cs`, `doc/TESTING_GUIDE.md`。

- **npm/Jest を第一級 CI ゲート化** — メイン CI ワークフローは `.node-version` で Node.js を固定し、`package-lock.json` の依存グラフをキャッシュ付き `npm ci` で厳密に復元して、HTML レポートの Jest 全テストと High/Critical npm advisory の拒否を実行するようになりました。ロックファイル更新により着手時点で報告されていた High 5 件を解消しました。作業中に新規公開された `brace-expansion` advisory(`GHSA-mh99-v99m-4gvg`)は開発専用の Jest ツールに限定されるため、2026-08-31 に失効する例外として明記し、それ以外の High/Critical はすべて引き続きゲートを失敗させます。Dependabot は同じチェックを通る npm 更新 PR を週次で作成します。対象: `.node-version`, `.github/workflows/dotnet.yml`, `.github/dependabot.yml`, `package.json`, `package-lock.json`, `npm-audit-exceptions.json`, `scripts/npm-audit-gate.js`, `doc/TESTING_GUIDE.md`。テスト: `npm_audit_gate.test.js`, `CiAutomationConfigurationTests`。

- **opt-in の `--fail-on-diff` CI ゲート** — 正常に完了した比較は既定で従来どおり `0` を返します。`--fail-on-diff` を指定した場合は、最終的なレポート対象の Added/Removed/Modified が空でなければ専用終了コード `5` を返します。判定は有効なレポート、監査ログ、ポストプロセス処理をすべて完了した後にだけ行い、無視拡張子やその他の抑制・フィルタ済み差分はゲートを発火させません。対象: `Runner/CliParser.cs`, `Runner/CliOptions.cs`, `ProgramRunner.cs`, `Runner/ProgramRunner.Types.cs`, `Runner/ProgramRunner.Wizard.cs`, `Runner/ProgramRunner.HelpText.cs`, `README.md`, `USER_GUIDE.md`。テスト: `CliOptionsTests`, `ProgramRunnerTests`。
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,36 @@ public void DotNetWorkflow_RunsPinnedJavaScriptTestsAndAuditGate()
Assert.False(string.IsNullOrWhiteSpace(auditException.GetProperty("scope").GetString()));
}

/// <summary>
/// Verifies that CI audits direct and transitive NuGet packages and fails on High/Critical findings.
/// CI が NuGet の直接・推移的 package を監査し、High/Critical の検出を失敗させることを検証します。
/// </summary>
[Fact]
public void DotNetWorkflow_AuditsDirectAndTransitiveNuGetPackages()
{
var workflow = File.ReadAllText(GetRepositoryFilePath(".github", "workflows", "dotnet.yml"));
var auditGate = File.ReadAllText(GetRepositoryFilePath("scripts", "nuget_audit_gate.py"));
var testProject = File.ReadAllText(
GetRepositoryFilePath("FolderDiffIL4DotNet.Tests", "FolderDiffIL4DotNet.Tests.csproj"));

Assert.Contains("Test NuGet audit gate", workflow, StringComparison.Ordinal);
Assert.Contains(
"python3 -m unittest discover -s scripts/tests -p 'test_*.py'",
workflow,
StringComparison.Ordinal);
Assert.Contains("Audit NuGet dependencies", workflow, StringComparison.Ordinal);
Assert.Contains(
"python3 scripts/nuget_audit_gate.py --solution FolderDiffIL4DotNet.sln",
workflow,
StringComparison.Ordinal);
Assert.Contains("\"--vulnerable\"", auditGate, StringComparison.Ordinal);
Assert.Contains("\"--include-transitive\"", auditGate, StringComparison.Ordinal);
Assert.Contains("BLOCKING_SEVERITIES = {\"high\", \"critical\"}", auditGate, StringComparison.Ordinal);
Assert.Contains("NUGET_AUDIT_SOURCE = \"https://api.nuget.org/v3/index.json\"", auditGate, StringComparison.Ordinal);
Assert.Contains("GITHUB_STEP_SUMMARY", auditGate, StringComparison.Ordinal);
Assert.Contains("FsCheck.Xunit\" Version=\"3.3.3\"", testProject, StringComparison.Ordinal);
}

/// <summary>
/// Verifies that tagged builds create a GitHub release with attached publish and documentation artifacts.
/// タグ付きビルドが公開・ドキュメント成果物を添付した GitHub リリースを作成することを検証します。
Expand Down
2 changes: 1 addition & 1 deletion FolderDiffIL4DotNet.Tests/FolderDiffIL4DotNet.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.8.1" />
<PackageReference Include="FsCheck.Xunit" Version="2.16.6" />
<PackageReference Include="FsCheck.Xunit" Version="3.3.3" />
<PackageReference Include="xunit" Version="2.9.3" />
<PackageReference Include="Xunit.SkippableFact" Version="1.5.61" />
<PackageReference Include="xunit.runner.visualstudio" Version="3.1.5">
Expand Down
9 changes: 7 additions & 2 deletions FolderDiffIL4DotNet.Tests/PropertyBased/PropertyBasedTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,11 @@
using FolderDiffIL4DotNet.Core.Text;
using FolderDiffIL4DotNet.Models;
using FsCheck;
using FsCheck.Fluent;
using FsCheck.Xunit;
using Xunit;
using FluentArbMap = FsCheck.Fluent.ArbMap;
using FluentGen = FsCheck.Fluent.Gen;

namespace FolderDiffIL4DotNet.Tests.PropertyBased
{
Expand Down Expand Up @@ -164,9 +167,11 @@ public static class NonNullStringArrayArbitrary
{
public static Arbitrary<string[]> StringArray()
{
return Gen.Choose(0, 30)
return FluentGen.Choose(0, 30)
.SelectMany(len =>
Gen.ArrayOf(len, Arb.Generate<NonNull<string>>().Select(s => s.Get)))
FluentGen.ArrayOf(
FluentArbMap.Default.GeneratorFor<NonNull<string>>().Select(s => s.Get),
len))
.ToArbitrary();
}
}
Expand Down
18 changes: 18 additions & 0 deletions doc/TESTING_GUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,15 @@ npm run audit:high

The audit gate fails on every High/Critical advisory except the exact, time-bounded entries in [`npm-audit-exceptions.json`](../npm-audit-exceptions.json). The current `GHSA-mh99-v99m-4gvg` exception is limited to the development-only Jest dependency chain because the latest Jest 30.4.x graph cannot yet select the patched `brace-expansion` major safely; a separate `--omit=dev` audit enforces that scope, and the exception expires on 2026-08-31. [`scripts/npm-audit-gate.js`](../scripts/npm-audit-gate.js) also fails on an expired exception or if npm audit cannot return a valid report.

Run the NuGet audit-gate tests and the same full-solution direct/transitive dependency audit used by CI:

```bash
python3 -m unittest discover -s scripts/tests -p 'test_*.py'
python3 scripts/nuget_audit_gate.py --solution FolderDiffIL4DotNet.sln
```

[`scripts/nuget_audit_gate.py`](../scripts/nuget_audit_gate.py) runs `dotnet list FolderDiffIL4DotNet.sln package --vulnerable --include-transitive --format json --output-version 1 --source https://api.nuget.org/v3/index.json`, prints every reported advisory, writes the result to the GitHub job summary when available, and fails on any direct or transitive High/Critical finding. It explicitly requires the advisory-capable nuget.org source and fails closed if the command or JSON report is invalid or incomplete. There are no active NuGet audit exceptions; any proposed temporary exception must document its advisory, rationale, exact dependency/project scope, and expiry before the CI policy is changed.

Run performance benchmarks (BenchmarkDotNet):

```bash
Expand Down Expand Up @@ -375,6 +384,15 @@ npm run audit:high

監査ゲートは、[`npm-audit-exceptions.json`](../npm-audit-exceptions.json) に完全一致かつ期限付きで記録した項目を除き、High/Critical advisory をすべて失敗させます。現在の `GHSA-mh99-v99m-4gvg` 例外は、最新 Jest 30.4.x の依存グラフが修正版 `brace-expansion` のメジャーバージョンをまだ安全に選択できないため、開発専用 Jest 依存チェーンだけに限定します。この範囲は別の `--omit=dev` 監査で強制し、例外は 2026-08-31 に失効します。[`scripts/npm-audit-gate.js`](../scripts/npm-audit-gate.js) は、例外が期限切れの場合や npm audit から有効なレポートを取得できない場合も失敗します。

NuGet 監査ゲートのテストと、CI と同じソリューション全体の直接・推移的依存関係監査を実行する場合:

```bash
python3 -m unittest discover -s scripts/tests -p 'test_*.py'
python3 scripts/nuget_audit_gate.py --solution FolderDiffIL4DotNet.sln
```

[`scripts/nuget_audit_gate.py`](../scripts/nuget_audit_gate.py) は `dotnet list FolderDiffIL4DotNet.sln package --vulnerable --include-transitive --format json --output-version 1 --source https://api.nuget.org/v3/index.json` を実行し、報告された advisory をすべて表示します。利用可能な場合は GitHub job summary にも結果を書き込み、直接・推移的依存関係の High/Critical 検出を失敗させます。advisory 対応の nuget.org source を明示的に必須とし、コマンドや JSON レポートが不正・不完全な場合も fail-closed で失敗します。現在有効な NuGet 監査例外はありません。将来一時例外を提案する場合は、CI ポリシーを変更する前に advisory、理由、正確な依存関係/project の範囲、失効日を記録する必要があります。

パフォーマンスベンチマーク(BenchmarkDotNet)を実行する場合:

```bash
Expand Down
Loading
Loading