Skip to content

Conversation

@mykeelium
Copy link
Contributor

@mykeelium mykeelium commented Sep 19, 2025

Reverts #173 reapplying the work for BED-6221

Summary by CodeRabbit

  • New Features

    • AdminSDHolder-aware ACL collection: computes and stores an authoritative SD hash per domain and applies it during collection.
    • Emits an adminsdholderprotected flag for Users, Computers, and Groups when detected, improving visibility into protected objects.
  • Chores

    • Normalized project file formatting (no functional impact).

@mykeelium mykeelium self-assigned this Sep 19, 2025
@coderabbitai
Copy link

coderabbitai bot commented Sep 19, 2025

Walkthrough

Adds per-domain, case-insensitive AdminSDHolderHash (ConcurrentDictionary) to context; LdapProducer collects the AdminSDHolder SD and computes/stores its authoritative hash when ACL collection is enabled; ObjectProcessors consult that hash to mark objects as AdminSDHolder-protected. Also adjusts EOF newline in Sharphound.csproj.

Changes

Cohort / File(s) Summary
Project file newline
Sharphound.csproj
No functional change; adjusted end-of-file newline.
Context model updates
src/BaseContext.cs, src/Client/Context.cs
Added public ConcurrentDictionary<string, string> AdminSDHolderHash on BaseContext and IContext; added using System.Collections.Concurrent and initialized the dictionary with StringComparer.OrdinalIgnoreCase.
LDAP AdminSDHolder collection
src/Producers/LdapProducer.cs
When ACL collection is enabled, queries the domain naming context for the AdminSDHolder object (including SD), computes an authoritative SD hash via ACLProcessor, stores it in Context.AdminSDHolderHash[domain], and logs success/warning; this runs before the existing per-filter LDAP queries.
ACL processing enhancements
src/Runtime/ObjectProcessors.cs
Added helper to retrieve AdminSDHolder hash from context and, in User/Computer/Group ACL paths, call _aclProcessor.IsAdminSDHolderProtected(entry, adminSdHolderHash) and expose adminsdholderprotected when applicable; plus minor brace/formatting adjustments.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant Runner as Collector
  participant Ldap as LdapProducer
  participant AD as Domain LDAP
  participant ACL as ACLProcessor
  participant Ctx as Context
  participant Proc as ObjectProcessors

  Note over Ldap,Ctx: Start collection with ACLs enabled

  Runner->>Ldap: Start LDAP collection
  Ldap->>AD: Query domain NC for AdminSDHolder (SD, DN, SID)
  AD-->>Ldap: AdminSDHolder entry (or none)

  alt AdminSDHolder found
    Ldap->>ACL: Compute authoritative SD hash
    ACL-->>Ldap: adminSdHolderHash
    Ldap->>Ctx: Store AdminSDHolderHash[domain] = hash
  else Not found / error
    Ldap-->>Runner: Warn and continue
  end

  Note over Ldap: Proceed with normal LDAP queries

  loop For each directory object
    Ldap-->>Proc: Emit entry for processing
    Proc->>Ctx: Get AdminSDHolderHash[domain]
    Proc->>ACL: IsAdminSDHolderProtected(entry, hash)
    ACL-->>Proc: true/false/null
    Proc-->>Runner: Emit object (+ adminsdholderprotected if set)
  end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested labels

enhancement, external

Suggested reviewers

  • definitelynotagoblin

Poem

I hop through LDAP groves at night,
Nose twitching for SDs just right.
I stash a hash for each domain,
Marking protections in my lane.
A carrot, a hop — the audit's bright. 🥕🐇

Pre-merge checks and finishing touches

❌ Failed checks (2 warnings)
Check name Status Explanation Resolution
Description Check ⚠️ Warning The PR description provided is a single sentence: "Reverts #173 reapplying the work for BED-6221." While this briefly identifies the purpose, it does not follow the repository's description template structure. The template requires detailed sections including a comprehensive description of changes, motivation and context, testing methodology, types of changes (with checkboxes), and a completion checklist. The author's submission is largely incomplete, missing most critical template sections such as detailed description, how testing was performed, which type of change this represents, and confirmation of the pre-merge checklist items. The description should be expanded to follow the template structure. The author should add: a detailed description of the AdminSDHolder changes across the four modified files, motivation explaining why BED-6221 work needed to be reapplied, an explanation of how the changes were tested (including whether existing tests still pass), a selection of the appropriate change type (likely "New feature" based on the additions), and completion of the checklist items. Consider also providing context about what PR #173 reverted and why this reapplication is necessary.
Docstring Coverage ⚠️ Warning Docstring coverage is 66.67% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (1 passed)
Check name Status Explanation
Title Check ✅ Passed The PR title "Reapply 'feat: AdminSDHolder updates - BED-6221'" directly and clearly describes the main purpose of the changeset. The changes across multiple files (BaseContext.cs, Context.cs, LdapProducer.cs, and ObjectProcessors.cs) all work together to implement AdminSDHolder hash functionality and ACL-aware processing. The title accurately reflects that this PR is reapplying previously reverted AdminSDHolder updates associated with ticket BED-6221. The title is concise, specific, and uses meaningful terminology that would help a reviewer quickly understand the changeset's purpose without being vague or generic.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch revert-173-revert-165-jsykora/BED-6178

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.

@mykeelium mykeelium marked this pull request as ready for review September 22, 2025 17:36
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: 3

🧹 Nitpick comments (4)
src/BaseContext.cs (1)

126-130: Optional: restrict mutability of the property.

No external re-assignment needed; only per‑entry updates. Prefer get‑only to prevent accidental wholesale replacement.

-        public ConcurrentDictionary<string, string> AdminSDHolderHash { get; set; }
+        public ConcurrentDictionary<string, string> AdminSDHolderHash { get; }

Initialize in the constructor (already shown in the other diff).

src/Producers/LdapProducer.cs (3)

97-107: Lower log level for sensitive-ish hash and reduce noise.

The computed hash needn’t be Info; Debug/Trace is sufficient.

-                                Context.Logger.LogInformation(
-                                    "AdminSDHolder ACL hash {Hash} calculated for {Domain}.", authoritativeSd,
-                                    domain.Name);
+                                Context.Logger.LogDebug(
+                                    "AdminSDHolder ACL hash {Hash} calculated for {Domain}.", authoritativeSd,
+                                    domain.Name);

110-116: Log “collected” only on actual success.

Currently logs Trace even if the SD wasn’t retrieved; gate it after the hash is stored.

-                            Context.Logger.LogTrace("AdminSDHolder data collected for {Domain}", domain.Name);
+                            if (Context.AdminSDHolderHash.ContainsKey(domain.Name))
+                            {
+                                Context.Logger.LogTrace("AdminSDHolder data collected for {Domain}", domain.Name);
+                            }

97-99: Optional: reuse ACLProcessor instance.

Instantiate once per producer (or per domain iteration) instead of per AdminSDHolder hit. Minor allocation win.

Example (outside the shown hunk):

// class field
private readonly ACLProcessor _aclProcessor;

// in constructor
_aclProcessor = new ACLProcessor(Context.LDAPUtils);

// here, replace new ACLProcessor(...) with _aclProcessor
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d53cc04 and 57aa962.

📒 Files selected for processing (5)
  • Sharphound.csproj (1 hunks)
  • src/BaseContext.cs (2 hunks)
  • src/Client/Context.cs (1 hunks)
  • src/Producers/LdapProducer.cs (4 hunks)
  • src/Runtime/ObjectProcessors.cs (8 hunks)
🔇 Additional comments (7)
Sharphound.csproj (1)

47-47: No functional change.

EOF newline tweak only. Nothing to review.

src/BaseContext.cs (1)

32-33: Good: case‑insensitive keying for domain names.

Using StringComparer.OrdinalIgnoreCase avoids duplicate keys due to case differences.

src/Runtime/ObjectProcessors.cs (4)

123-132: Helper is fine; null‑safe and domain‑scoped.

Lookup is efficient; leave absent key as null for downstream logic to skip.


149-168: Correct integration: exposes adminsdholderprotected for Users when available.

Pattern matches existing ACL flags and avoids emitting when unknown.


227-244: Correct integration: adminsdholderprotected for Computers.

Consistent with Users; no control‑flow side effects.


435-452: Correct integration: adminsdholderprotected for Groups.

Consistent semantics across security principals.

src/Producers/LdapProducer.cs (1)

58-75: Good gating: only collect AdminSDHolder when ACL collection is enabled.

Avoids extra LDAP round‑trip when unnecessary.

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

♻️ Duplicate comments (1)
src/Producers/LdapProducer.cs (1)

103-106: Race fix confirmed: thread-safe update of AdminSDHolderHash.

Using ConcurrentDictionary.AddOrUpdate resolves the earlier concurrent write/read race on AdminSDHolderHash. Nice.

Run to confirm the dictionary type and case-insensitive comparer in Context/BaseContext/IContext:

#!/bin/bash
set -euo pipefail
rg -n -C2 'AdminSDHolderHash' --hidden
rg -nP 'ConcurrentDictionary\s*<\s*string\s*,\s*string\s*>' --hidden
rg -nP 'new\s+ConcurrentDictionary<\s*string\s*,\s*string\s*>\s*\(\s*StringComparer\.OrdinalIgnoreCase' --hidden
🧹 Nitpick comments (3)
src/Producers/LdapProducer.cs (3)

58-125: AdminSDHolder prefetch: add guardrails and tighten logging.

Solid flow and ACL gating. Recommend:

  • Wrap hash compute/store in try/catch to avoid aborting domain on unexpected parsing errors.
  • Guard against null/empty hashes before storing/logging.
  • Log Trace only when SD processed; warn if SD missing.
  • Consider downgrading the hash log to Debug to reduce noise.

Apply this diff within this block:

-                            // Create an instance of ACLProcessor - _aclProcessor from ObjectProcessors isn't in this context
-                             var aclProcessor = new ACLProcessor(Context.LDAPUtils);
-
-                            // Calculate the authoritative SD based on a hash of the implicit ACLs & AclProtected
-                            var authoritativeSd = aclProcessor.CalculateImplicitACLHash(sd);
-
-                            // Store the hashes in the Context for later use
-                            Context.AdminSDHolderHash.AddOrUpdate(
-                                domain.Name,
-                                _ => authoritativeSd,
-                                (_, _) => authoritativeSd);
-
-                            Context.Logger.LogInformation(
-                                "AdminSDHolder ACL hash {Hash} calculated for {Domain}.", authoritativeSd,
-                                domain.Name);
+                            try
+                            {
+                                var aclProcessor = new ACLProcessor(Context.LDAPUtils);
+                                var authoritativeSd = aclProcessor.CalculateImplicitACLHash(sd);
+                                if (!string.IsNullOrEmpty(authoritativeSd))
+                                {
+                                    Context.AdminSDHolderHash.AddOrUpdate(
+                                        domain.Name,
+                                        _ => authoritativeSd,
+                                        (_, _) => authoritativeSd);
+                                    Context.Logger.LogDebug(
+                                        "AdminSDHolder ACL hash {Hash} calculated for {Domain}.",
+                                        authoritativeSd, domain.Name);
+                                    Context.Logger.LogTrace("AdminSDHolder data collected for {Domain}", domain.Name);
+                                }
+                                else
+                                {
+                                    Context.Logger.LogWarning(
+                                        "Empty AdminSDHolder ACL hash for {Domain}; skipping store.", domain.Name);
+                                }
+                            }
+                            catch (Exception ex)
+                            {
+                                Context.Logger.LogWarning(ex,
+                                    "Failed to compute/store AdminSDHolder hash for {Domain}", domain.Name);
+                            }
                         }
-
-                        Context.Logger.LogTrace("AdminSDHolder data collected for {Domain}", domain.Name);
+                        else
+                        {
+                            Context.Logger.LogWarning("AdminSDHolder SD missing for {Domain}", domain.Name);
+                        }

148-155: Use culture-invariant lowercasing for DN checks.

Avoid culture-sensitive casing for protocol strings. Prefer ToLowerInvariant().

-    var lower = distinguishedName.ToLower();
+    var lower = distinguishedName.ToLowerInvariant();

166-166: Typo in flag name (non-blocking).

ParititonLdapQueries appears misspelled. If not intentional, consider introducing a correctly spelled alias (PartitionLdapQueries) and deprecating the old name to avoid breaking configs/CLIs.

Can you confirm if this name is public/serialized? If so, we can add a non-breaking alias.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4ddf2cd and ecd1f35.

📒 Files selected for processing (1)
  • src/Producers/LdapProducer.cs (4 hunks)
🔇 Additional comments (2)
src/Producers/LdapProducer.cs (2)

10-10: Import looks correct.

SharpHoundCommonLib.Processors is required for ACLProcessor. No concerns.


127-139: Main query gating of security descriptors is correct.

IncludeSecurityDescriptor tied to CollectionMethod.ACL is appropriate; avoids unnecessary SD fetches when ACLs aren’t collected.

@mykeelium mykeelium merged commit 61853ff into 2.X Oct 23, 2025
2 checks passed
@mykeelium mykeelium deleted the revert-173-revert-165-jsykora/BED-6178 branch October 23, 2025 18:03
@github-actions github-actions bot locked and limited conversation to collaborators Oct 23, 2025
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants