-
Notifications
You must be signed in to change notification settings - Fork 52
[BED-6027] - Filter GPOs with Computer Configuration Setting Disabled #241
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: v4
Are you sure you want to change the base?
Conversation
WalkthroughThe LDAP query in GPOLocalGroupProcessor now requests GPCFileSysPath and Flags. Processing skips GPOs when Flags indicate disabled/computer-disabled (2 or 3). LdapPropertyProcessor reads Flags into gpostatus. A unit test validates enabled GPOs are applied and disabled ones are skipped. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
participant Caller
participant GPOLocalGroupProcessor
participant LDAP
participant FileSystem
Caller->>GPOLocalGroupProcessor: ReadGPOLocalGroups(gplink)
GPOLocalGroupProcessor->>LDAP: Query GPCFileSysPath, Flags
LDAP-->>GPOLocalGroupProcessor: path, flags
alt Missing path OR flags in {2,3}
GPOLocalGroupProcessor->>GPOLocalGroupProcessor: Cache current actions for link
GPOLocalGroupProcessor-->>Caller: Result (skipped)
else Enabled (flags in {0,1})
GPOLocalGroupProcessor->>FileSystem: Load Groups.xml / GptTmpl.inf
FileSystem-->>GPOLocalGroupProcessor: Data
GPOLocalGroupProcessor->>GPOLocalGroupProcessor: Process and cache actions
GPOLocalGroupProcessor-->>Caller: Result (applied)
end
note over GPOLocalGroupProcessor: New guard based on Flags
sequenceDiagram
autonumber
participant Caller
participant LdapPropertyProcessor
participant LDAP
Caller->>LdapPropertyProcessor: ReadGPOProperties(dn)
LdapPropertyProcessor->>LDAP: Query properties incl. Flags
LDAP-->>LdapPropertyProcessor: attributes
LdapPropertyProcessor-->>Caller: dict{..., gpostatus=Flags}
note over LdapPropertyProcessor: Adds gpostatus without changing signature
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Suggested reviewers
Poem
Pre-merge checks and finishing touches✅ Passed checks (3 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this 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 (5)
src/CommonLib/Processors/LdapPropertyProcessor.cs (1)
168-169
: Prefer numeric flags with derived boolean for clarity and future-proofingStoring Flags as a raw string works, but using a numeric type and exposing a derived boolean makes downstream logic simpler and less error-prone (e.g., bit tests). The GPO status field is bitwise: bit 1 indicates "computer configuration disabled," covering values 2 and 3.
Consider this change:
- entry.TryGetProperty(LDAPProperties.Flags, out var flags); - props.Add("gpostatus", flags); + if (entry.TryGetLongProperty(LDAPProperties.Flags, out var gpoFlags)) { + props.Add("gpostatus", gpoFlags); + // true when Computer Configuration is disabled (flags 2 or 3) + props.Add("gpocomputerconfigdisabled", (gpoFlags & 0x2) != 0); + }src/CommonLib/Processors/GPOLocalGroupProcessor.cs (1)
143-146
: Use bitwise flag check and normalize cache key to avoid duplicate entriesTwo improvements:
- Flags check: interpret Flags numerically and test bit 1 to cover both "Computer Configuration Disabled" (2) and "All Settings Disabled" (3), instead of string comparisons.
- Cache key: you read from GpoActionCache using linkDn.ToLower(), but in this early-return branch you add with the original casing. This can create duplicate entries and unnecessary LDAP hits. Normalize the key consistently.
Apply:
- if (!result.Value.TryGetProperty(LDAPProperties.GPCFileSYSPath, out var filePath) || - // Filter out GPOs that are disabled or the computer configuration is disabled - (result.Value.TryGetProperty(LDAPProperties.Flags, out var flags) && flags is "2" or "3")) { - GpoActionCache.TryAdd(linkDn, actions); - continue; - } + if (!result.Value.TryGetProperty(LDAPProperties.GPCFileSYSPath, out var filePath)) { + GpoActionCache.TryAdd(linkDn.ToLower(), actions); + continue; + } + // Filter out GPOs whose computer configuration is disabled: bit 1 set (2 or 3) + if (result.Value.TryGetLongProperty(LDAPProperties.Flags, out var gpoFlags) && (gpoFlags & 0x2) != 0) { + GpoActionCache.TryAdd(linkDn.ToLower(), actions); + continue; + }test/unit/GPOLocalGroupProcessorTest.cs (3)
162-171
: Use a unique temp subdirectory to avoid cross-test interferenceUsing Path.GetTempPath() as the root can cause collisions across parallel runs. Prefer a unique subdir per test.
- var gpcFileSysPath = Path.GetTempPath(); + var gpcFileSysPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString("N")); + Directory.CreateDirectory(gpcFileSysPath);Optionally wrap the test body in try/finally and delete the directory in finally to keep the temp clean.
172-174
: Remove unused variablemockComputerEntry is declared but never used.
- var mockComputerEntry = new Mock<IDirectoryObject>(); - var mockSearchResultEntry = new Mock<IDirectoryObject>(); + var mockSearchResultEntry = new Mock<IDirectoryObject>();
292-296
: Assertions match intended behavior; consider asserting that actions are empty for skipped GPOsThe LocalAdmins asserts are good. For completeness, you could also assert that other groups (RemoteDesktopUsers, DcomUsers, PSRemoteUsers) are empty for flags 2/3 to guard against regressions in parsing.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (3)
src/CommonLib/Processors/GPOLocalGroupProcessor.cs
(2 hunks)src/CommonLib/Processors/LdapPropertyProcessor.cs
(1 hunks)test/unit/GPOLocalGroupProcessorTest.cs
(1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (3)
src/CommonLib/Processors/LdapPropertyProcessor.cs (5)
src/CommonLib/DirectoryObjects/DirectoryEntryWrapper.cs (1)
TryGetProperty
(35-50)src/CommonLib/DirectoryObjects/IDirectoryObject.cs (1)
TryGetProperty
(8-8)src/CommonLib/DirectoryObjects/SearchResultEntryWrapper.cs (1)
TryGetProperty
(22-38)test/unit/Facades/MockDirectoryObject.cs (1)
TryGetProperty
(30-49)src/CommonLib/Enums/LDAPProperties.cs (1)
LDAPProperties
(3-99)
src/CommonLib/Processors/GPOLocalGroupProcessor.cs (5)
src/CommonLib/Enums/LDAPProperties.cs (1)
LDAPProperties
(3-99)src/CommonLib/DirectoryObjects/DirectoryEntryWrapper.cs (1)
TryGetProperty
(35-50)src/CommonLib/DirectoryObjects/IDirectoryObject.cs (1)
TryGetProperty
(8-8)src/CommonLib/DirectoryObjects/SearchResultEntryWrapper.cs (1)
TryGetProperty
(22-38)test/unit/Facades/MockDirectoryObject.cs (1)
TryGetProperty
(30-49)
test/unit/GPOLocalGroupProcessorTest.cs (4)
src/CommonLib/Processors/GPOLocalGroupProcessor.cs (6)
Task
(52-58)Task
(60-249)Task
(346-374)TypedPrincipal
(514-519)GPOLocalGroupProcessor
(16-578)GPOLocalGroupProcessor
(47-50)test/unit/Facades/MockDirectoryObject.cs (3)
TryGetSecurityIdentifier
(137-140)MockDirectoryObject
(12-180)MockDirectoryObject
(18-23)src/CommonLib/LdapQueries/LdapFilter.cs (17)
LdapFilter
(8-276)LdapFilter
(47-51)LdapFilter
(58-62)LdapFilter
(69-75)LdapFilter
(82-86)LdapFilter
(93-97)LdapFilter
(104-108)LdapFilter
(115-119)LdapFilter
(126-130)LdapFilter
(137-141)LdapFilter
(150-153)LdapFilter
(160-163)LdapFilter
(170-174)LdapFilter
(181-184)LdapFilter
(191-194)LdapFilter
(201-204)GetFilter
(237-256)src/CommonLib/Enums/LDAPProperties.cs (1)
LDAPProperties
(3-99)
🔇 Additional comments (1)
src/CommonLib/Processors/GPOLocalGroupProcessor.cs (1)
134-135
: Good: Requesting Flags alongside GPCFileSYSPathFetching both GPCFileSYSPath and Flags in the same query reduces roundtrips and enables early filtering. Looks good.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Holy moly those tests 👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
src/CommonLib/Processors/LdapPropertyProcessor.cs
(1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
src/CommonLib/Processors/LdapPropertyProcessor.cs (5)
src/CommonLib/DirectoryObjects/DirectoryEntryWrapper.cs (1)
TryGetProperty
(35-50)src/CommonLib/DirectoryObjects/IDirectoryObject.cs (1)
TryGetProperty
(8-8)src/CommonLib/DirectoryObjects/SearchResultEntryWrapper.cs (1)
TryGetProperty
(22-38)test/unit/Facades/MockDirectoryObject.cs (1)
TryGetProperty
(30-49)src/CommonLib/Enums/LDAPProperties.cs (1)
LDAPProperties
(3-99)
⏰ 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). (1)
- GitHub Check: build
entry.TryGetProperty(LDAPProperties.Flags, out var flags); | ||
props.Add("gpostatus", flags); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion | 🟠 Major
🧩 Analysis chain
LGTM! Consider explicit handling for missing Flags attribute.
The implementation correctly reads the Flags LDAP property and stores it as "gpostatus", following the existing pattern used for GPCFileSYSPath on lines 166-167. When the Flags attribute doesn't exist, TryGetProperty
sets flags
to an empty string, which won't match "2" or "3" in downstream filtering logic—resulting in the GPO being treated as enabled. This behavior appears correct.
Optional: Make the handling more explicit
While the current implementation works correctly, consider checking the return value of TryGetProperty
to make the behavior more explicit:
-entry.TryGetProperty(LDAPProperties.Flags, out var flags);
-props.Add("gpostatus", flags);
+if (entry.TryGetProperty(LDAPProperties.Flags, out var flags)) {
+ props.Add("gpostatus", flags);
+}
Alternatively, add a comment explaining that an empty string represents "enabled" when Flags is absent:
+// Flags attribute may not exist; empty string indicates enabled GPO
entry.TryGetProperty(LDAPProperties.Flags, out var flags);
props.Add("gpostatus", flags);
Verification: Confirm downstream handling
Ensure that GPOLocalGroupProcessor correctly handles the empty string case when the Flags attribute is missing from LDAP.
🏁 Script executed:
#!/bin/bash
# Description: Verify how GPOLocalGroupProcessor handles the gpostatus property
# Expected: Code should handle empty string case or explicitly check for "2" or "3"
# Search for where gpostatus is read and used
rg -n -C5 'gpostatus'
# Search for Flags comparison logic in GPOLocalGroupProcessor
rg -n -C10 'class GPOLocalGroupProcessor' -A 200 | rg -C5 '"2"|"3"'
Length of output: 10231
---
🏁 Script executed:
```shell
#!/bin/bash
# Find all usages of ReadGPOProperties to see where the props dictionary is consumed
rg -n 'ReadGPOProperties' -C5
# Also search for where props might be used or returned
rg -n 'props\[' -C3 src/CommonLib/Processors/LdapPropertyProcessor.cs
Length of output: 3265
---
🏁 Script executed:
```shell
#!/bin/bash
# Get the full test to see if gpostatus is validated
cat git/test/unit/LdapPropertyTests.cs | sed -n '84,110p'
# Find all production calls to ReadGPOProperties (exclude test files)
rg -n 'ReadGPOProperties' --type cs -g '!test' -C8
Length of output: 1632
Unconsumed gpostatus property
The gpostatus
entry added by ReadGPOProperties (src/CommonLib/Processors/LdapPropertyProcessor.cs:168-169) is never consumed by production code nor asserted in tests. Remove this dead code or refactor callers to leverage gpostatus
instead of invoking TryGetProperty
directly.
🤖 Prompt for AI Agents
In src/CommonLib/Processors/LdapPropertyProcessor.cs around lines 168-169, the
props.Add("gpostatus", flags) call adds a gpostatus entry that is never used;
either remove this dead code or update callers to consume the value from the
props dictionary instead of calling TryGetProperty directly. If you choose
removal: delete the props.Add line and run tests to ensure nothing relied on the
key. If you choose refactor: change callers to read props["gpostatus"] (with
proper null checks/type handling) and then remove the direct TryGetProperty
usage so the processor is the single source of truth.
Description
Filter GPOs that are disabled or, computer configuration disabled while doing local group processing. Adding the GPO status as one of the collected properties.
Motivation and Context
Resolves BED-6027
How Has This Been Tested?
This has been tested by running collection in an environment with and without the GPO enabled and monitoring the results of the collection to ensure that the results are affected.
Screenshots (if appropriate):
Types of changes
Checklist:
Summary by CodeRabbit
New Features
Bug Fixes
Tests