Skip to content

fix: WMI disposal, Ookla timeout, docs accuracy (CQ-001, CQ-003)#370

Merged
laurentiu021 merged 1 commit into
mainfrom
fix/docs-code-quality
May 15, 2026
Merged

fix: WMI disposal, Ookla timeout, docs accuracy (CQ-001, CQ-003)#370
laurentiu021 merged 1 commit into
mainfrom
fix/docs-code-quality

Conversation

@laurentiu021

@laurentiu021 laurentiu021 commented May 15, 2026

Copy link
Copy Markdown
Owner

Summary

Addresses 3 HIGH findings from QA audit + 2 documentation accuracy issues.

Code Quality Fixes

CQ-001: DiskHealthService WMI disposal (HIGH)

  • ManagementObjectCollection now wrapped in using statement
  • Each ManagementObject disposed via using(mo) block
  • Prevents COM resource leaks during SMART/reliability queries

CQ-003: SpeedTestService Ookla timeout (HIGH)

  • Added 5-minute linked CancellationTokenSource for process execution
  • Prevents indefinite hangs if Ookla CLI freezes

Documentation Fixes

CONTRIBUTING.md

  • .NET SDK reference corrected from 8 to 9
  • Added SysManager.IntegrationTests to project layout

SECURITY.md

  • Supported versions table updated (0.48.x + 0.47.x supported)

Build

  • 0 errors, 0 new warnings

Summary by CodeRabbit

Release Notes - v0.48.10

  • Bug Fixes

    • Improved resource management in disk health monitoring
    • Added independent timeout for speed test operations
  • Documentation

    • Updated .NET SDK requirement to version 9
    • Clarified test project structure in contributing guide
    • Updated security policy supported versions table

Review Change Stack

@coderabbitai

coderabbitai Bot commented May 15, 2026

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

This PR is a maintenance release (0.48.10) that fixes two service-level resource management issues: proper disposal of WMI ManagementObject instances in disk health queries, and a 5-minute timeout for Ookla CLI execution. Documentation updates reflect the .NET 9 SDK requirement, clarify test project layout, and refresh the supported versions policy.

Changes

Release 0.48.10

Layer / File(s) Summary
DiskHealthService WMI resource disposal
SysManager/SysManager/Services/DiskHealthService.cs
Both the MSFT_PhysicalDisk query loop and the reliability counter enrichment loop now store searcher.Get() in a disposable collection and wrap each ManagementObject in a using block to prevent resource leaks.
SpeedTestService Ookla CLI timeout
SysManager/SysManager/Services/SpeedTestService.cs
The RunOoklaAsync method now creates a linked CancellationTokenSource with a fixed 5-minute timeout and passes it to the process start, output read, and exit wait operations.
Release documentation
CHANGELOG.md, CONTRIBUTING.md, SECURITY.md
CHANGELOG documents the bug fixes and documentation changes; CONTRIBUTING.md updates the .NET SDK requirement from 8 to 9 and splits SysManager.Tests and SysManager.IntegrationTests entries; SECURITY.md updates the supported versions table to show 0.48.x as supported and removes older unsupported versions.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Possibly related PRs

  • laurentiu021/SystemManager#322: Both PRs implement WMI resource-leak fixes by disposing ManagementObject instances during enumeration (main in DiskHealthService; related in BatteryService).
  • laurentiu021/SystemManager#366: Both PRs modify SysManager/Services/SpeedTestService.cs in the Ookla execution flow (main adds 5-minute linked timeout, related adds debug logging for cleanup failures).

Poem

🐰 A rabbit hops through resource streams,
Disposing what no longer dreams—
Five minutes for the speed to test,
WMI objects put to rest,
A tidy .NET 9 quest! 🚀

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 33.33% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely summarizes the main changes: WMI disposal fixes, Ookla timeout handling, and documentation accuracy updates, directly reflecting the changeset's primary objectives.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/docs-code-quality

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@SECURITY.md`:
- Around line 15-18: The support table in SECURITY.md lists 0.48.x and 0.47.x as
supported but the written policy line stating "fixes apply to the latest minor
only" is inconsistent; either update that sentence to state that fixes apply to
the latest two minors or change the table to mark only the latest minor as
supported. Locate the sentence containing "fixes apply to the latest minor only"
and either amend it to "latest two minor releases" (to match rows 0.48.x and
0.47.x) or adjust the table rows (0.48.x, 0.47.x, 0.46.x) so only the intended
supported minor remains checked—ensure the wording and the table entries
(0.48.x, 0.47.x, 0.46.x) are consistent.

In `@SysManager/SysManager/Services/SpeedTestService.cs`:
- Around line 201-210: The awaits using linked cancellation token around
proc.Start(), proc.StandardOutput.ReadToEndAsync(linked),
proc.StandardError.ReadToEndAsync(linked), and proc.WaitForExitAsync(linked) can
throw OperationCanceledException on timeout but currently do not terminate the
child process; wrap these awaits in try/catch/finally (or try/finally with a
catch for OperationCanceledException) and in the finally check the Process
instance (proc) and if !proc.HasExited call proc.Kill(entireProcessTree: true)
(or proc.Kill() if older framework) and await a safe WaitForExit to ensure the
speedtest.exe is terminated before disposing; reference the existing
variables/procedures proc, psi, timeoutCts/linked, Start,
StandardOutput.ReadToEndAsync, StandardError.ReadToEndAsync, and
WaitForExitAsync when making the change.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 778b6855-beae-4482-ac5c-c90f42557374

📥 Commits

Reviewing files that changed from the base of the PR and between 8167322 and 3c88de6.

📒 Files selected for processing (5)
  • CHANGELOG.md
  • CONTRIBUTING.md
  • SECURITY.md
  • SysManager/SysManager/Services/DiskHealthService.cs
  • SysManager/SysManager/Services/SpeedTestService.cs
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Build & unit tests
  • GitHub Check: Analyze (csharp)
🔇 Additional comments (3)
SysManager/SysManager/Services/DiskHealthService.cs (1)

32-53: LGTM!

Also applies to: 76-90

CHANGELOG.md (1)

9-22: LGTM!

CONTRIBUTING.md (1)

42-42: LGTM!

Also applies to: 78-79

Comment thread SECURITY.md
Comment on lines +15 to +18
| 0.48.x | :white_check_mark: |
| 0.47.x | :white_check_mark: |
| 0.46.x | :white_check_mark: |
| 0.45.x | :white_check_mark: |
| 0.44.x | :x: |
| < 0.44 | :x: |
| 0.46.x | :x: |
| < 0.46 | :x: |

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Align the support table with the written support policy.

Lines 15-18 now show two supported minors, but Line 10 still says fixes apply to the latest minor only. Please make them consistent to avoid ambiguity in security support commitments.

Suggested doc fix
-Security fixes are applied to the latest minor release only. If you're on an
-older build, the first step is usually to update.
+Security fixes are applied to the latest two minor release lines. If you're on an
+older build, the first step is usually to update.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@SECURITY.md` around lines 15 - 18, The support table in SECURITY.md lists
0.48.x and 0.47.x as supported but the written policy line stating "fixes apply
to the latest minor only" is inconsistent; either update that sentence to state
that fixes apply to the latest two minors or change the table to mark only the
latest minor as supported. Locate the sentence containing "fixes apply to the
latest minor only" and either amend it to "latest two minor releases" (to match
rows 0.48.x and 0.47.x) or adjust the table rows (0.48.x, 0.47.x, 0.46.x) so
only the intended supported minor remains checked—ensure the wording and the
table entries (0.48.x, 0.47.x, 0.46.x) are consistent.

Comment on lines +201 to +210
using var timeoutCts = CancellationTokenSource.CreateLinkedTokenSource(ct);
timeoutCts.CancelAfter(TimeSpan.FromMinutes(5));
var linked = timeoutCts.Token;

using var proc = new Process();
proc.StartInfo = psi;
await Task.Run(() => proc.Start(), ct).ConfigureAwait(false);
var stdout = await proc.StandardOutput.ReadToEndAsync(ct);
var stderr = await proc.StandardError.ReadToEndAsync(ct);
await proc.WaitForExitAsync(ct);
await Task.Run(() => proc.Start(), linked).ConfigureAwait(false);
var stdout = await proc.StandardOutput.ReadToEndAsync(linked);
var stderr = await proc.StandardError.ReadToEndAsync(linked);
await proc.WaitForExitAsync(linked);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Verify whether RunOoklaAsync has a cancellation path that kills the spawned process.
fd -i 'SpeedTestService.cs' | while read -r f; do
  echo "== $f =="
  rg -n -C4 'RunOoklaAsync|CreateLinkedTokenSource|WaitForExitAsync|OperationCanceledException|Kill\(' "$f"
done

Repository: laurentiu021/SystemManager

Length of output: 1315


🏁 Script executed:

#!/bin/bash
# Get the complete RunOoklaAsync method to verify exception handling
rg -n -A 60 'public async Task<SpeedTestResult> RunOoklaAsync' 'SysManager/SysManager/Services/SpeedTestService.cs' | head -100

Repository: laurentiu021/SystemManager

Length of output: 2759


Kill the Ookla process on timeout/cancellation to prevent orphan speedtest.exe instances.

Lines 207–210 await with the timeout token, but have no exception handler. If the 5-minute timeout triggers, OperationCanceledException propagates uncaught, leaving speedtest.exe running. The using statement on proc only disposes handles—it doesn't terminate the child process.

Proposed fix
 using var proc = new Process();
 proc.StartInfo = psi;
-await Task.Run(() => proc.Start(), linked).ConfigureAwait(false);
-var stdout = await proc.StandardOutput.ReadToEndAsync(linked);
-var stderr = await proc.StandardError.ReadToEndAsync(linked);
-await proc.WaitForExitAsync(linked);
+try
+{
+    await Task.Run(() => proc.Start(), linked).ConfigureAwait(false);
+    var stdout = await proc.StandardOutput.ReadToEndAsync(linked);
+    var stderr = await proc.StandardError.ReadToEndAsync(linked);
+    await proc.WaitForExitAsync(linked);
+}
+catch (OperationCanceledException) when (timeoutCts.IsCancellationRequested && !ct.IsCancellationRequested)
+{
+    try { if (!proc.HasExited) proc.Kill(entireProcessTree: true); } catch { }
+    throw new TimeoutException("Ookla CLI timed out after 5 minutes.");
+}
+catch (OperationCanceledException)
+{
+    try { if (!proc.HasExited) proc.Kill(entireProcessTree: true); } catch { }
+    throw;
+}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
using var timeoutCts = CancellationTokenSource.CreateLinkedTokenSource(ct);
timeoutCts.CancelAfter(TimeSpan.FromMinutes(5));
var linked = timeoutCts.Token;
using var proc = new Process();
proc.StartInfo = psi;
await Task.Run(() => proc.Start(), ct).ConfigureAwait(false);
var stdout = await proc.StandardOutput.ReadToEndAsync(ct);
var stderr = await proc.StandardError.ReadToEndAsync(ct);
await proc.WaitForExitAsync(ct);
await Task.Run(() => proc.Start(), linked).ConfigureAwait(false);
var stdout = await proc.StandardOutput.ReadToEndAsync(linked);
var stderr = await proc.StandardError.ReadToEndAsync(linked);
await proc.WaitForExitAsync(linked);
using var timeoutCts = CancellationTokenSource.CreateLinkedTokenSource(ct);
timeoutCts.CancelAfter(TimeSpan.FromMinutes(5));
var linked = timeoutCts.Token;
using var proc = new Process();
proc.StartInfo = psi;
try
{
await Task.Run(() => proc.Start(), linked).ConfigureAwait(false);
var stdout = await proc.StandardOutput.ReadToEndAsync(linked);
var stderr = await proc.StandardError.ReadToEndAsync(linked);
await proc.WaitForExitAsync(linked);
}
catch (OperationCanceledException) when (timeoutCts.IsCancellationRequested && !ct.IsCancellationRequested)
{
try { if (!proc.HasExited) proc.Kill(entireProcessTree: true); } catch { }
throw new TimeoutException("Ookla CLI timed out after 5 minutes.");
}
catch (OperationCanceledException)
{
try { if (!proc.HasExited) proc.Kill(entireProcessTree: true); } catch { }
throw;
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@SysManager/SysManager/Services/SpeedTestService.cs` around lines 201 - 210,
The awaits using linked cancellation token around proc.Start(),
proc.StandardOutput.ReadToEndAsync(linked),
proc.StandardError.ReadToEndAsync(linked), and proc.WaitForExitAsync(linked) can
throw OperationCanceledException on timeout but currently do not terminate the
child process; wrap these awaits in try/catch/finally (or try/finally with a
catch for OperationCanceledException) and in the finally check the Process
instance (proc) and if !proc.HasExited call proc.Kill(entireProcessTree: true)
(or proc.Kill() if older framework) and await a safe WaitForExit to ensure the
speedtest.exe is terminated before disposing; reference the existing
variables/procedures proc, psi, timeoutCts/linked, Start,
StandardOutput.ReadToEndAsync, StandardError.ReadToEndAsync, and
WaitForExitAsync when making the change.

@codecov-commenter

Copy link
Copy Markdown

⚠️ Please install the 'codecov app svg image' to ensure uploads and comments are reliably processed by Codecov.

Codecov Report

❌ Patch coverage is 72.22222% with 10 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
SysManager/SysManager/Services/SpeedTestService.cs 0.00% 7 Missing ⚠️
...ysManager/SysManager/Services/DiskHealthService.cs 89.65% 0 Missing and 3 partials ⚠️

📢 Thoughts on this report? Let us know!

@laurentiu021
laurentiu021 merged commit 8321bf3 into main May 15, 2026
5 checks passed
@laurentiu021
laurentiu021 deleted the fix/docs-code-quality branch May 15, 2026 06:43
laurentiu021 added a commit that referenced this pull request May 22, 2026
…ceService (#370)

## Summary

Fix 2 CodeQL `cs/missed-using-statement` alerts.

## Changes

### ServiceManagerService.cs
- `ServiceController` objects from `GetServices()` now wrapped in
`using` block instead of manual `try/finally Dispose()`

### PerformanceService.cs  
- `Process` objects from `GetProcesses()` in `TrimWorkingSets` now
wrapped in `using` block instead of manual `try/finally Dispose()`

## Testing
- Build: 0 errors
- No behavioral changes — same disposal semantics, cleaner pattern

Co-authored-by: laurentiu021 <laurentiu021@users.noreply.github.com>
laurentiu021 added a commit that referenced this pull request May 22, 2026
Co-authored-by: laurentiu021 <laurentiu021@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants