fix(postprocess): Fix multi-forest ADCS false positives BED-5572#2900
Conversation
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthrough
ChangesADCS Forest-Scoping
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ 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.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
packages/go/analysis/ad/adcscache.go (1)
338-389:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winResolve forest state independently from the hosting-computer lookup.
GetChainedDomains()relies onecaForestDomains, but aHostsCAServicefetch error skips forest resolution entirely, so Line 597 treats the CA as forest-unknown and keeps cross-forest chained domains. Move forest cache population before the host query, and clear entries when resolution is unknown to avoid stale map-presence behavior on rebuilds.🐛 Proposed fix
+ // Resolve and cache the CA forest independently from HostsCAService so + // GetChainedDomains can still apply forest scoping if host lookup fails. + forestDomains, forestKnown, forestResolutionErr := resolveEnterpriseCAForest(tx, eca, domainsBySID) + if forestResolutionErr != nil { + slog.WarnContext( + ctx, + "Error resolving forest for enterprise ca", + slog.Uint64("enterprise_ca", uint64(eca.ID)), + attr.Error(forestResolutionErr), + ) + } + if forestKnown { + s.ecaForestDomains[eca.ID] = forestDomains + } else { + delete(s.ecaForestDomains, eca.ID) + delete(s.hasInForestHostingComputer, eca.ID) + } + if hostingComputers, err := fetchFirstDegreeNodes(tx, eca, ad.HostsCAService); err != nil { slog.ErrorContext( ctx, "Error fetching hosting computers for enterprise ca", slog.Uint64("enterprise_ca", uint64(eca.ID)), attr.Error(err), ) } else { - // Resolve the forest the CA itself belongs to (the SameForestTrust - // closure of the CA's domain). When this can't be determined we fall - // back to forest-agnostic behavior to avoid dropping real findings. - forestDomains, forestKnown, err := resolveEnterpriseCAForest(tx, eca, domainsBySID) - if err != nil { - slog.WarnContext( - ctx, - "Error resolving forest for enterprise ca", - slog.Uint64("enterprise_ca", uint64(eca.ID)), - attr.Error(err), - ) - } - var ( hasHostingComputer = false hostInForest = false ) @@ s.hasHostingComputer[eca.ID] = hasHostingComputer if forestKnown { - s.ecaForestDomains[eca.ID] = forestDomains s.hasInForestHostingComputer[eca.ID] = hostInForest + } else { + delete(s.hasInForestHostingComputer, eca.ID) } }🤖 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 `@packages/go/analysis/ad/adcscache.go` around lines 338 - 389, The forest resolution logic is currently nested inside the else block that only executes when the hosting computer fetch succeeds, which means if fetchFirstDegreeNodes fails, the forest is never resolved and ecaForestDomains is never populated. Move the resolveEnterpriseCAForest call to execute independently before checking the hosting computer fetch result, so that forest domains are resolved and cached in s.ecaForestDomains regardless of whether the HostsCAService query succeeds or fails. This ensures GetChainedDomains can reliably check forestKnown status without relying on the hosting computer lookup outcome.
🧹 Nitpick comments (1)
packages/go/analysis/ad/adcscache_test.go (1)
55-60: ⚡ Quick winRemove named returns from
adcsForestFilterNodes(Line 55).This helper uses named returns plus a naked
return, which violates the Go guideline for this repo.Proposed fix
-func adcsForestFilterNodes() (eca, inForestDomain, foreignDomain *graph.Node) { - eca = graph.NewNode(10, graph.NewProperties(), ad.EnterpriseCA) - inForestDomain = graph.NewNode(1, graph.NewProperties(), ad.Domain) - foreignDomain = graph.NewNode(2, graph.NewProperties(), ad.Domain) - return eca, inForestDomain, foreignDomain +func adcsForestFilterNodes() (*graph.Node, *graph.Node, *graph.Node) { + enterpriseCA := graph.NewNode(10, graph.NewProperties(), ad.EnterpriseCA) + inForestDomain := graph.NewNode(1, graph.NewProperties(), ad.Domain) + foreignDomain := graph.NewNode(2, graph.NewProperties(), ad.Domain) + return enterpriseCA, inForestDomain, foreignDomain }As per coding guidelines, “Named returns are not allowed; all return variables must be defined in the function.”
🤖 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 `@packages/go/analysis/ad/adcscache_test.go` around lines 55 - 60, The adcsForestFilterNodes function uses named returns in its signature (eca, inForestDomain, foreignDomain *graph.Node) combined with a naked return statement, which violates the repo's Go coding guidelines. Remove the named return variables from the function signature and specify only the return types (*graph.Node, *graph.Node, *graph.Node), keeping the explicit return statement with the three variables as is. This will make the function compliant with the guideline that all return variables must be defined in the function body.Source: Coding guidelines
🤖 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.
Outside diff comments:
In `@packages/go/analysis/ad/adcscache.go`:
- Around line 338-389: The forest resolution logic is currently nested inside
the else block that only executes when the hosting computer fetch succeeds,
which means if fetchFirstDegreeNodes fails, the forest is never resolved and
ecaForestDomains is never populated. Move the resolveEnterpriseCAForest call to
execute independently before checking the hosting computer fetch result, so that
forest domains are resolved and cached in s.ecaForestDomains regardless of
whether the HostsCAService query succeeds or fails. This ensures
GetChainedDomains can reliably check forestKnown status without relying on the
hosting computer lookup outcome.
---
Nitpick comments:
In `@packages/go/analysis/ad/adcscache_test.go`:
- Around line 55-60: The adcsForestFilterNodes function uses named returns in
its signature (eca, inForestDomain, foreignDomain *graph.Node) combined with a
naked return statement, which violates the repo's Go coding guidelines. Remove
the named return variables from the function signature and specify only the
return types (*graph.Node, *graph.Node, *graph.Node), keeping the explicit
return statement with the three variables as is. This will make the function
compliant with the guideline that all return variables must be defined in the
function body.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository YAML (base), Organization UI (inherited)
Review profile: CHILL
Plan: Pro
Run ID: 9150cb7a-1299-4679-9e66-79e9d9d1f176
📒 Files selected for processing (3)
packages/go/analysis/ad/adcs_forest_integration_test.gopackages/go/analysis/ad/adcscache.gopackages/go/analysis/ad/adcscache_test.go
There was a problem hiding this comment.
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 `@packages/go/analysis/ad/adcscache_test.go`:
- Around line 55-60: Remove the named return values from the function signature
of adcsForestFilterNodes. Replace the named return parameters (eca,
inForestDomain, foreignDomain *graph.Node) with unnamed return types
(*graph.Node, *graph.Node, *graph.Node). Keep the return statement unchanged
since it already explicitly returns the three variables by name.
🪄 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: Repository YAML (base), Organization UI (inherited)
Review profile: CHILL
Plan: Pro
Run ID: 82ea7a02-126e-4860-9959-e20123e3012c
📒 Files selected for processing (3)
packages/go/analysis/ad/adcs_forest_integration_test.gopackages/go/analysis/ad/adcscache.gopackages/go/analysis/ad/adcscache_test.go
🚧 Files skipped from review as they are similar to previous changes (1)
- packages/go/analysis/ad/adcscache.go
| func adcsForestFilterNodes() (eca, inForestDomain, foreignDomain *graph.Node) { | ||
| eca = graph.NewNode(10, graph.NewProperties(), ad.EnterpriseCA) | ||
| inForestDomain = graph.NewNode(1, graph.NewProperties(), ad.Domain) | ||
| foreignDomain = graph.NewNode(2, graph.NewProperties(), ad.Domain) | ||
| return eca, inForestDomain, foreignDomain | ||
| } |
There was a problem hiding this comment.
Remove named return values from adcsForestFilterNodes.
This helper currently uses named returns, which violates the Go guideline for this repository.
Suggested fix
-func adcsForestFilterNodes() (eca, inForestDomain, foreignDomain *graph.Node) {
- eca = graph.NewNode(10, graph.NewProperties(), ad.EnterpriseCA)
- inForestDomain = graph.NewNode(1, graph.NewProperties(), ad.Domain)
- foreignDomain = graph.NewNode(2, graph.NewProperties(), ad.Domain)
- return eca, inForestDomain, foreignDomain
+func adcsForestFilterNodes() (*graph.Node, *graph.Node, *graph.Node) {
+ eca := graph.NewNode(10, graph.NewProperties(), ad.EnterpriseCA)
+ inForestDomain := graph.NewNode(1, graph.NewProperties(), ad.Domain)
+ foreignDomain := graph.NewNode(2, graph.NewProperties(), ad.Domain)
+ return eca, inForestDomain, foreignDomain
}As per coding guidelines: "**/*.go: Named returns are not allowed; all return variables must be defined in the function."
📝 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.
| func adcsForestFilterNodes() (eca, inForestDomain, foreignDomain *graph.Node) { | |
| eca = graph.NewNode(10, graph.NewProperties(), ad.EnterpriseCA) | |
| inForestDomain = graph.NewNode(1, graph.NewProperties(), ad.Domain) | |
| foreignDomain = graph.NewNode(2, graph.NewProperties(), ad.Domain) | |
| return eca, inForestDomain, foreignDomain | |
| } | |
| func adcsForestFilterNodes() (*graph.Node, *graph.Node, *graph.Node) { | |
| eca := graph.NewNode(10, graph.NewProperties(), ad.EnterpriseCA) | |
| inForestDomain := graph.NewNode(1, graph.NewProperties(), ad.Domain) | |
| foreignDomain := graph.NewNode(2, graph.NewProperties(), ad.Domain) | |
| return eca, inForestDomain, foreignDomain | |
| } |
🤖 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 `@packages/go/analysis/ad/adcscache_test.go` around lines 55 - 60, Remove the
named return values from the function signature of adcsForestFilterNodes.
Replace the named return parameters (eca, inForestDomain, foreignDomain
*graph.Node) with unnamed return types (*graph.Node, *graph.Node, *graph.Node).
Keep the return statement unchanged since it already explicitly returns the
three variables by name.
Source: Coding guidelines
fa69c5f to
54d5fbf
Compare
|
dataset.zip |
There was a problem hiding this comment.
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 `@packages/go/analysis/ad/adcs_forest_integration_test.go`:
- Around line 91-97: The test is setting up chain validity for
hostForestEnterpriseCA through linkEnterpriseCAToDomain calls (lines 91-93), but
copiedEnterpriseCA is not being configured as chain-valid. This means the
exclusion assertion at line 111 might pass for the wrong reason (due to an
invalid chain) rather than proving the actual cross-forest host filtering logic.
Add chain validity setup for copiedEnterpriseCA by calling
linkEnterpriseCAToDomain with copiedEnterpriseCA in a similar manner to how
hostForestEnterpriseCA is configured, ensuring both domains are properly linked
so the subsequent exclusion test validates the correct behavior.
🪄 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: Repository YAML (base), Organization UI (inherited)
Review profile: CHILL
Plan: Pro
Run ID: cbb19cde-a5b4-4b74-8377-f6c34e913ce7
📒 Files selected for processing (3)
packages/go/analysis/ad/adcs_forest_integration_test.gopackages/go/analysis/ad/adcscache.gopackages/go/analysis/ad/adcscache_test.go
| linkEnterpriseCAToDomain(testContext, hostForestEnterpriseCA, rootCA, domainA, domainASID) | ||
| // ...and a cross-forest chain into forest B (shared ADCS). | ||
| linkEnterpriseCAToDomain(testContext, hostForestEnterpriseCA, rootCA, domainB, domainBSID) | ||
|
|
||
| host := addEnabledHostingComputer(testContext, "HostA", domainASID, hostForestEnterpriseCA) | ||
| testContext.NewRelationship(host, copiedEnterpriseCA, ad.HostsCAService) | ||
|
|
There was a problem hiding this comment.
Make copiedEnterpriseCA chain-valid so the exclusion assertion proves forest-host filtering.
Right now, Line 91-Line 93 wire chain validity only for hostForestEnterpriseCA. If copiedEnterpriseCA is not chain-valid, Line 111 can pass for the wrong reason (missing chain instead of cross-forest host rejection).
Suggested fix
// Valid cert chain to forest A (the CA's own forest)...
linkEnterpriseCAToDomain(testContext, hostForestEnterpriseCA, rootCA, domainA, domainASID)
// ...and a cross-forest chain into forest B (shared ADCS).
linkEnterpriseCAToDomain(testContext, hostForestEnterpriseCA, rootCA, domainB, domainBSID)
+
+ // Ensure copied CA is also chain-valid so exclusion is attributable to
+ // forest-host gating, not missing chain relationships.
+ testContext.NewRelationship(copiedEnterpriseCA, rootCA, ad.EnterpriseCAFor)
+ linkEnterpriseCAToDomain(testContext, copiedEnterpriseCA, rootCA, domainA, domainASID)
+ linkEnterpriseCAToDomain(testContext, copiedEnterpriseCA, rootCA, domainB, domainBSID)
host := addEnabledHostingComputer(testContext, "HostA", domainASID, hostForestEnterpriseCA)
testContext.NewRelationship(host, copiedEnterpriseCA, ad.HostsCAService)Also applies to: 110-112
🤖 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 `@packages/go/analysis/ad/adcs_forest_integration_test.go` around lines 91 -
97, The test is setting up chain validity for hostForestEnterpriseCA through
linkEnterpriseCAToDomain calls (lines 91-93), but copiedEnterpriseCA is not
being configured as chain-valid. This means the exclusion assertion at line 111
might pass for the wrong reason (due to an invalid chain) rather than proving
the actual cross-forest host filtering logic. Add chain validity setup for
copiedEnterpriseCA by calling linkEnterpriseCAToDomain with copiedEnterpriseCA
in a similar manner to how hostForestEnterpriseCA is configured, ensuring both
domains are properly linked so the subsequent exclusion test validates the
correct behavior.
|
Left a couple comments but looks good otherwise! I know you had planned to go over this for demo tomorrow but lemme know when you're ready for a quick re-review and I'll ✅ |
Description
Fix multi-forest false positives for ADCSx edges being created.
Motivation and Context
Resolves BED-5572
Why is this change required? What problem does it solve?
Multi forest ADCS were generating false positives as certificates were not tracked cross forest this PR fixes that issue
How Has This Been Tested?
Added both unit and integration tests and ensured no previous tests failed
Please describe in detail how you tested your changes.
Include details of your testing environment, and the tests you ran to
see how your change affects other areas of the code, etc.
Screenshots (optional):
Types of changes
Checklist:
Summary by CodeRabbit
Summary by CodeRabbit
Release Notes