Skip to content

fix: security hardening (#319)#361

Merged
laurentiu021 merged 1 commit into
mainfrom
fix/security-319
May 14, 2026
Merged

fix: security hardening (#319)#361
laurentiu021 merged 1 commit into
mainfrom
fix/security-319

Conversation

@laurentiu021

@laurentiu021 laurentiu021 commented May 14, 2026

Copy link
Copy Markdown
Owner

Summary

Addresses 3 security findings from the code review.

Changes

  1. UninstallerService (SEC-002) — UninstallLocalAsync now validates the parsed executable: must exist on disk and have .exe extension. MsiExec and rundll32 are exempted (system binaries). Prevents execution of arbitrary commands planted in HKCU uninstall registry keys (writable without admin).
  2. EventLogService (SEC-003) — XPath BuildXPath sanitization now strips quotes, brackets, and slashes in addition to single quotes. Prevents XPath injection via crafted provider names.
  3. LogService (SEC-004) — UserPathRegex now matches any drive letter (A-Z) instead of only C:. Ensures usernames are redacted regardless of Windows installation drive.

Closes #319

Summary by CodeRabbit

Release Notes

  • Bug Fixes
    • Enhanced security during software uninstallation by validating executable paths and preventing execution of potentially compromised uninstall commands
    • Strengthened event log functionality with improved protection against injection-based attacks via comprehensive character filtering
    • Expanded user path recognition to properly support user paths across all available system drive letters

Review Change Stack

@coderabbitai

coderabbitai Bot commented May 14, 2026

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

This PR adds three security hardening fixes across UninstallerService, EventLogService, and LogService to prevent command execution from tampered registry entries, XPath injection in event log queries, and incomplete path sanitization across drive letters.

Changes

Security Hardening: Registry Validation, XPath Injection, and Path Sanitization

Layer / File(s) Summary
UninstallerService executable validation
SysManager/SysManager/Services/UninstallerService.cs
UninstallLocalAsync validates that non-standard executables exist on disk and have a .exe extension before execution, rejecting tampered HKCU registry uninstall entries.
EventLogService XPath metacharacter sanitization
SysManager/SysManager/Services/EventLogService.cs
BuildXPath strips XPath metacharacters (', ", [, ], /, \) from ProviderName to prevent injection attacks in XPath query construction.
LogService drive-letter-agnostic path sanitization
SysManager/SysManager/Services/LogService.cs
UserPathRegex generalized to match any drive letter ([A-Z]:\Users\) instead of only C:\Users\, maintaining the same username masking behavior.
Security fixes changelog
CHANGELOG.md
0.48.8 release notes document the three security hardening fixes (SEC-002, SEC-003, SEC-004) for registry validation, XPath sanitization, and drive-letter expansion.

🎯 2 (Simple) | ⏱️ ~12 minutes

🐰 Three fixes hop through the code so neat,
SEC-002, 003, 004—security complete!
Registry checked, XPath clean, paths wide—
No injection sneaks past our warren's pride! ✨

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The pull request title 'fix: security hardening (#319)' clearly and concisely summarizes the main changes—three security-related fixes—and directly corresponds to the primary objective of the PR.
Linked Issues check ✅ Passed All three coding requirements from issue #319 are implemented: UninstallerService validates executables [SEC-002], EventLogService strengthens XPath sanitization [SEC-003], and LogService expands path regex to all drives [SEC-004].
Out of Scope Changes check ✅ Passed All changes are directly related to the three security hardening objectives in issue #319; no out-of-scope modifications are present.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ 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/security-319

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: 1

🤖 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 `@SysManager/SysManager/Services/UninstallerService.cs`:
- Around line 253-267: The current StartsWith allowlist is unsafe because names
like "MsiExecEvil.exe" pass; update the validation in UninstallerService to
parse the executable token from the exe string (trim surrounding quotes and take
the substring before any whitespace), then use System.IO.Path.GetFileName on
that token and compare case-insensitively for exact basenames "msiexec.exe" and
"rundll32.exe" (instead of StartsWith). Keep the existing existence and
extension checks but apply them to the resolved executable path/token so only
exact legitimate system executables are allowed.
🪄 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: ccdbebca-89e9-46b5-9aca-0d04366dc65b

📥 Commits

Reviewing files that changed from the base of the PR and between e50c3d6 and b2a37b2.

📒 Files selected for processing (4)
  • CHANGELOG.md
  • SysManager/SysManager/Services/EventLogService.cs
  • SysManager/SysManager/Services/LogService.cs
  • SysManager/SysManager/Services/UninstallerService.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/EventLogService.cs (1)

149-157: LGTM!

SysManager/SysManager/Services/LogService.cs (1)

19-22: LGTM!

CHANGELOG.md (1)

9-19: LGTM!

Comment on lines +253 to +267
// SEC-002: Validate the executable exists and is a real file (not a
// script or arbitrary command). HKCU uninstall keys can be modified
// without admin, so we must not blindly execute whatever is there.
if (!exe.StartsWith("MsiExec", StringComparison.OrdinalIgnoreCase) &&
!exe.StartsWith("rundll32", StringComparison.OrdinalIgnoreCase))
{
if (!System.IO.File.Exists(exe))
throw new InvalidOperationException(
$"Uninstall executable not found: '{exe}'. The app may have been removed already.");

var ext = System.IO.Path.GetExtension(exe);
if (!ext.Equals(".exe", StringComparison.OrdinalIgnoreCase))
throw new InvalidOperationException(
$"Uninstall target is not an executable (.exe): '{exe}'. Refusing to run for security.");
}

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 | 🔴 Critical | ⚡ Quick win

StartsWith allowlist can be bypassed by similarly named executables.

Line [256]-Line [257] trusts any executable whose name begins with MsiExec/rundll32 (for example, MsiExecEvil.exe), which re-opens arbitrary command execution from HKCU uninstall data.

🔧 Proposed fix
-        if (!exe.StartsWith("MsiExec", StringComparison.OrdinalIgnoreCase) &&
-            !exe.StartsWith("rundll32", StringComparison.OrdinalIgnoreCase))
+        var exeName = System.IO.Path.GetFileName(exe);
+        var isTrustedSystemBinary =
+            exeName.Equals("MsiExec", StringComparison.OrdinalIgnoreCase) ||
+            exeName.Equals("MsiExec.exe", StringComparison.OrdinalIgnoreCase) ||
+            exeName.Equals("rundll32", StringComparison.OrdinalIgnoreCase) ||
+            exeName.Equals("rundll32.exe", StringComparison.OrdinalIgnoreCase);
+
+        if (!isTrustedSystemBinary)
         {
             if (!System.IO.File.Exists(exe))
                 throw new InvalidOperationException(
                     $"Uninstall executable not found: '{exe}'. The app may have been removed already.");
🤖 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/UninstallerService.cs` around lines 253 - 267,
The current StartsWith allowlist is unsafe because names like "MsiExecEvil.exe"
pass; update the validation in UninstallerService to parse the executable token
from the exe string (trim surrounding quotes and take the substring before any
whitespace), then use System.IO.Path.GetFileName on that token and compare
case-insensitively for exact basenames "msiexec.exe" and "rundll32.exe" (instead
of StartsWith). Keep the existing existence and extension checks but apply them
to the resolved executable path/token so only exact legitimate system
executables are allowed.

@laurentiu021
laurentiu021 merged commit 0fb8233 into main May 14, 2026
5 checks passed
@laurentiu021
laurentiu021 deleted the fix/security-319 branch May 14, 2026 11:34
laurentiu021 added a commit that referenced this pull request May 22, 2026
…idation (#361)

## Summary

Batch 1 of the full code audit. Fixes 4 critical issues and 1
high-severity pattern across 5 service files.

### Critical fixes

**C1 — 50MB upload array on LOH** (SpeedTestService.cs)
- Replaced \
ew byte[50MB]\ with a streaming \RandomChunkStream\ that produces random
data in 256KB chunks
- Eliminates Large Object Heap pressure during speed test uploads

**C2 — COM object leak** (StartupService.cs)
- \WScript.Shell\ and shortcut COM objects created via
\Activator.CreateInstance\ were never released
- Added \Marshal.ReleaseComObject()\ in a \ inally\ block for both
objects

**C3 — Potential deadlock in SetTaskSchedulerEnabled**
(StartupService.cs)
- \proc.WaitForExit(5000)\ was called BEFORE
\proc.StandardError.ReadToEnd()\
- If schtasks writes enough to fill the pipe buffer, the child blocks on
write, WaitForExit times out, then ReadToEnd blocks forever
- Fix: read stderr/stdout async BEFORE WaitForExit, increased timeout to
10s, added Kill on timeout

**H5 — Command argument injection prevention** (5 files)
- StartupService: validate task path rejects quotes and null chars
- ServiceManagerService: validate serviceName + whitelist startType
values for sc.exe
- UninstallerService: validate packageId for winget uninstall
- WingetService: validate packageId for winget upgrade

### Additional cleanup (StartupService.cs)
- All 6 bare \catch { }\ blocks replaced with specific exception types
(\SecurityException\, \UnauthorizedAccessException\, \IOException\,
\COMException\, \InvalidOperationException\)
- Added Serilog debug logging to all catch blocks for diagnostics
- SpeedTestService: bare catch in MeasurePingAsync replaced with
\PingException\, \SocketException\, \InvalidOperationException\

### Files changed
- \Services/StartupService.cs\ — COM leak, deadlock, bare catches, input
validation
- \Services/SpeedTestService.cs\ — 50MB array, bare catch
- \Services/ServiceManagerService.cs\ — input validation
- \Services/UninstallerService.cs\ — input validation
- \Services/WingetService.cs\ — input validation

### Testing
- Build: 0 errors (main project + tests project)
- Existing tests unaffected (no behavioral changes, only safety
improvements)

Co-authored-by: laurentiu021 <laurentiu021@users.noreply.github.com>
laurentiu021 added a commit that referenced this pull request May 22, 2026
…ion, path regex (#319) (#361)

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.

[Enhancement]: Security — UninstallerService registry validation, EventLog XPath, LogService path sanitization

1 participant