Skip to content

Conversation

@lrfalslev
Copy link

@lrfalslev lrfalslev commented Jan 12, 2026

Description

Collect EnrollmentAgent Restrictions For AllTemplates, add CertAbuseProcessor Tests

Motivation and Context

BED-7044

How Has This Been Tested?

See: Common PR

Screenshots (if appropriate):

Types of changes

  • Chore (a change that does not modify the application functionality)
  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)

Checklist:

  • Documentation updates are needed, and have been made accordingly.
  • I have added and/or updated tests to cover my changes.
  • All new and existing tests passed.
  • My changes include a database migration.

Summary by CodeRabbit

  • Refactor
    • Refactored internal status reporting architecture to improve resource management and error handling robustness. Event handlers are now properly cleaned up after processing to prevent resource leaks.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link

coderabbitai bot commented Jan 12, 2026

Walkthrough

The PR refactors CSVComputerStatus channel propagation across the codebase. Instead of passing the compStatus channel as a method parameter to ProcessObject, the channel is now injected into ObjectProcessors via constructor dependency injection. Event subscriptions handle status updates, and a new ClearEventHandlers method manages lifecycle cleanup.

Changes

Cohort / File(s) Summary
Consumer Integration Updates
src/Runtime/CollectionTask.cs, src/Runtime/LDAPConsumer.cs
Updated ObjectProcessors instantiation to pass compStatusChannel via constructor rather than per-method calls. Added processor.ClearEventHandlers() invocations post-processing to ensure event handler cleanup.
Core Channel Management Refactor
src/Runtime/ObjectProcessors.cs
Introduced channel dependency injection into constructor; replaced per-call channel parameters with event-driven HandleCompStatusEvent method. Added event subscriptions across multiple processors; updated ProcessObject, ProcessEnterpriseCA, and ProcessComputerObject signatures to remove channel parameters. Implemented ClearEventHandlers and HandleCompStatusEvent methods with exception handling around channel writes. Extended ProcessObject switch flow to handle additional object types (Domain, OU, Container, RootCA).

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

  • SharpHound#161: Modifies CSVComputerStatus propagation in ObjectProcessors and ProcessEnterpriseCA method signatures with overlapping refactoring scope.

Suggested labels

bug

Suggested reviewers

  • ktstrader
  • definitelynotagoblin

Poem

🐰 A channel once passed through every call,
Now whispers through events one and all,
Injected with grace, dependencies clear,
Handlers cleaned up—no leaks to fear!
The refactor is done, the flow is bright. ✨

🚥 Pre-merge checks | ❌ 3
❌ Failed checks (2 warnings, 1 inconclusive)
Check name Status Explanation Resolution
Title check ⚠️ Warning The title references collecting EnrollmentAgent restrictions and adding tests, but the actual changes focus on refactoring ObjectProcessors to use event-driven status reporting with channel injection, which is not mentioned in the title. Update the title to reflect the primary architectural change: refactoring ObjectProcessors to use dependency injection and event-driven status reporting instead of per-call channel parameters.
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Description check ❓ Inconclusive The description lacks detail about the significant refactoring work shown in the code changes, which involves moving from per-call channel parameters to constructor-injected channels and event handlers. Expand the description to explain the architectural changes to ObjectProcessors, including the shift from per-call channel parameters to dependency injection and event-driven mechanisms, in addition to the EnrollmentAgent and test changes.

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

✨ Finishing touches
  • 📝 Generate docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@lrfalslev lrfalslev self-assigned this Jan 12, 2026
@lrfalslev lrfalslev marked this pull request as ready for review January 12, 2026 22:49
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (1)
src/Runtime/ObjectProcessors.cs (1)

769-843: Redundant HostingComputer resolution when both CertServices and CARegistry flags are set.

The HostingComputer resolution and assignment occurs in both the CertServices block (lines 774-786) and the CARegistry block (lines 804-816). When both flags are enabled, this results in:

  1. Duplicate DNS-to-SID resolution calls
  2. Two status events emitted for the same CA

Consider extracting the host resolution to run once before these blocks and reusing the result.

♻️ Suggested refactor
+           // Resolve hosting computer once for both CertServices and CARegistry
+           string hostingSid = null;
+           var caName = entry.GetProperty(LDAPProperties.Name);
+           var dnsHostName = entry.GetProperty(LDAPProperties.DNSHostName);
+           if (caName != null && dnsHostName != null) {
+               if (await _context.LDAPUtils.ResolveHostToSid(dnsHostName, resolvedSearchResult.DomainSid) is
+                       (true, var sid) && sid.StartsWith("S-1-")) {
+                   hostingSid = sid;
+                   ret.HostingComputer = hostingSid;
+                   await HandleCompStatusEvent(new CSVComputerStatus {
+                       Status = ComputerStatus.Success,
+                       ComputerName = resolvedSearchResult.DisplayName,
+                       Task = nameof(ProcessEnterpriseCA),
+                       ObjectId = resolvedSearchResult.ObjectId,
+                   });
+               } else {
+                   _log.LogWarning("CA {Name} host ({Dns}) could not be resolved to a SID.", caName, dnsHostName);
+               }
+           }
+
            if (_methods.HasFlag(CollectionMethod.CertServices)) {
-               var caName = entry.GetProperty(LDAPProperties.Name);
-               var dnsHostName = entry.GetProperty(LDAPProperties.DNSHostName);
-
                if (caName != null && dnsHostName != null) {
-                   if (await _context.LDAPUtils.ResolveHostToSid(dnsHostName, resolvedSearchResult.DomainSid) is
-                           (true, var sid) && sid.StartsWith("S-1-")) {
-                       ret.HostingComputer = sid;
-                       await HandleCompStatusEvent(new CSVComputerStatus
-                           {
-                               Status = ComputerStatus.Success,
-                               ...
-                           });
-                   } else {
-                       _log.LogWarning("CA {Name} host ({Dns}) could not be resolved to a SID.", caName, dnsHostName);
-                   }
                    var caEnrollmentProcessor = new CAEnrollmentProcessor(dnsHostName, caName, _log);
                    ...
                }
            }

            if (_methods.HasFlag(CollectionMethod.CARegistry)) {
-               var caName = entry.GetProperty(LDAPProperties.Name);
-               var dnsHostName = entry.GetProperty(LDAPProperties.DNSHostName);
                if (caName != null && dnsHostName != null) {
-                   if (await _context.LDAPUtils.ResolveHostToSid(...) { ... }
                    CARegistryData cARegistryData = new() {
-                       IsUserSpecifiesSanEnabled = await _certAbuseProcessor.IsUserSpecifiesSanEnabled(dnsHostName, caName, ret.HostingComputer),
+                       IsUserSpecifiesSanEnabled = await _certAbuseProcessor.IsUserSpecifiesSanEnabled(dnsHostName, caName, hostingSid),
                        ...
                    };
                }
            }
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ed84a79 and cbd3c35.

📒 Files selected for processing (3)
  • src/Runtime/CollectionTask.cs
  • src/Runtime/LDAPConsumer.cs
  • src/Runtime/ObjectProcessors.cs
🧰 Additional context used
🧬 Code graph analysis (2)
src/Runtime/CollectionTask.cs (1)
src/Runtime/ObjectProcessors.cs (3)
  • ObjectProcessors (22-952)
  • ObjectProcessors (46-78)
  • ClearEventHandlers (80-88)
src/Runtime/ObjectProcessors.cs (2)
src/Runtime/CollectionTask.cs (2)
  • Task (64-109)
  • Task (111-153)
src/Producers/ComputerFileProducer.cs (2)
  • Task (32-119)
  • Task (122-126)
🔇 Additional comments (14)
src/Runtime/LDAPConsumer.cs (3)

21-21: LGTM!

The constructor now correctly passes the computerStatusChannel to ObjectProcessors, aligning with the new dependency injection pattern for centralized status reporting.


37-37: LGTM!

The ProcessObject call correctly omits the compStatusChannel parameter, consistent with the refactored method signature that now uses event-driven status reporting internally.


54-55: LGTM!

ClearEventHandlers() is appropriately called after the foreach loop completes to detach event subscriptions and prevent memory leaks. The placement outside the loop ensures cleanup occurs once per consumer lifecycle.

src/Runtime/ObjectProcessors.cs (8)

44-77: LGTM - Clean event-driven status reporting setup.

The constructor properly:

  1. Stores the channel reference for centralized status writing
  2. Subscribes to ComputerStatusEvent on all relevant processors

The event subscriptions are symmetric with ClearEventHandlers(), ensuring proper lifecycle management.


80-88: LGTM!

The ClearEventHandlers() method correctly unsubscribes from all events that were subscribed in the constructor, preventing memory leaks and ensuring clean lifecycle management.


98-132: LGTM!

The ProcessObject method signature correctly removes the compStatusChannel parameter, and the switch statement properly delegates to the refactored processing methods.


238-241: LGTM!

ProcessComputerObject signature correctly updated to remove the compStatusChannel parameter.


311-332: LGTM!

Status reporting for computer availability and session enumeration correctly uses HandleCompStatusEvent instead of direct channel writes.


343-361: LGTM!

Privileged sessions and registry sessions status reporting correctly migrated to the event-driven pattern via HandleCompStatusEvent.


734-734: LGTM!

ProcessEnterpriseCA signature correctly updated to remove the compStatusChannel parameter.


90-96: The event handler signature is correct. The HandleCompStatusEvent method's async Task return type is properly designed for event handlers that need asynchronous behavior. Since this is production code (version 2.8.1) that compiles successfully, the ComputerStatusEvent delegate signature from SharpHoundCommonLib.Processors must be compatible with a Func<CSVComputerStatus, Task> or similar async-aware delegate. C# enforces delegate/handler signature compatibility at compile-time, so a mismatch would have prevented compilation. The implementation correctly awaits the asynchronous operation and handles exceptions appropriately.

Likely an incorrect or invalid review comment.

src/Runtime/CollectionTask.cs (3)

114-114: LGTM!

The ObjectProcessors instantiation correctly passes _compStatusChannel to the constructor, consistent with the new dependency injection pattern.


132-132: LGTM!

The ProcessObject call correctly uses the updated signature without the compStatusChannel parameter.


149-150: LGTM!

ClearEventHandlers() is appropriately called after the consumer loop completes, ensuring event subscriptions are cleaned up and preventing memory leaks.

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