Skip to content

Collect AIACAs with empty CACert BED-7513#274

Merged
lrfalslev merged 5 commits intov4from
lfalslev/bed-7513
Mar 5, 2026
Merged

Collect AIACAs with empty CACert BED-7513#274
lrfalslev merged 5 commits intov4from
lfalslev/bed-7513

Conversation

@lrfalslev
Copy link
Contributor

@lrfalslev lrfalslev commented Feb 26, 2026

Description

There was an edge case where an AD AIACA with a {0} value for CACertificate attribute would cause cert parsing to fail and AIACA wasn't collected. To fix we check if byte array is not empty before attempting to parse.

Motivation and Context

BED-7513

How Has This Been Tested?

As a domain admin, make a new CA with empty cACertificate

$zeroByte = [byte[]](0x00)

New-ADObject `
    -Name "SEVENKINGDOMS-CA-EMPTY-TEST" `
    -Type certificationAuthority `
    -Path "CN=AIA,CN=Public Key Services,CN=Services,CN=Configuration,DC=sevenkingdoms,DC=local" `
    -OtherAttributes @{
        cACertificate=$zeroByte
        certificateRevocationList=$zeroByte
        authorityRevocationList=$zeroByte
    }

Confirm that new CA object with cACertificate of {0} exists in AIA
image

Run new collection and confirm that AIACA node was created
image

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

  • Bug Fixes

    • Improved robustness of LDAP CA handling by validating certificate byte content before parsing; empty or non-meaningful certificate data is now skipped to prevent parsing errors and ensure consistent property population.
  • Tests

    • Added and expanded tests covering null/empty/single-zero-byte certificate scenarios and CA-related property assertions across Root, AIA, and Enterprise CA cases.
  • Documentation

    • Added clarifying documentation for Enterprise CA property reader.

@lrfalslev lrfalslev self-assigned this Feb 26, 2026
@lrfalslev lrfalslev added the bug Something isn't working label Feb 26, 2026
@coderabbitai
Copy link

coderabbitai bot commented Feb 26, 2026

Walkthrough

Adds a byte-validation guard for CACertificate data and applies it in Root, AIA, and Enterprise CA readers to skip certificate parsing when bytes are null, empty, or a single 0x00. Expands unit tests to cover valid and empty CACertificate scenarios across CA readers.

Changes

Cohort / File(s) Summary
Certificate validation & readers
src/CommonLib/Processors/LdapPropertyProcessor.cs
Adds private HasBytes(byte[] data) helper and uses it in ReadRootCAProperties, ReadAIACAProperties, and ReadEnterpriseCAProperties to skip parsing when CACertificate is null/empty/single 0x00; adds XML doc for ReadEnterpriseCAProperties.
Unit tests — CA scenarios
test/unit/LdapPropertyTests.cs
Introduces EmptyCertBytes data provider and expands tests for Root CA, AIA, and Enterprise CA to assert presence/absence of cert-derived properties for valid vs. empty CACertificate bytes; removes an outdated ACA test and updates assertions/test data.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰 I nibble bytes both small and few,
If empty or zero, I hop anew.
No parsing panic, I skip the chore,
Tests thump their feet and check once more.
A quiet patch — I hop off, encore. 🥕

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 18.75% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title directly references the bug being fixed (empty CACert handling for AIACAs) and includes the ticket identifier, clearly summarizing the main change.
Description check ✅ Passed The PR description includes all major sections: Description, Motivation/Context (with issue link), How Has This Been Tested (with commands and screenshots), Types of changes (Bug fix selected), and Checklist items (Tests added/updated, Tests passed).

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

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch lfalslev/bed-7513

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.

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.

🧹 Nitpick comments (1)
src/CommonLib/Processors/LdapPropertyProcessor.cs (1)

513-516: Consider applying the same guard to ReadRootCAProperties and ReadEnterpriseCAProperties.

The HasBytes helper addresses an edge case for AIACA, but ReadRootCAProperties (lines 475-482) and ReadEnterpriseCAProperties (lines 526-533) have identical certificate parsing patterns without this guard:

// ReadRootCAProperties (line 475)
if (entry.TryGetByteProperty(LDAPProperties.CACertificate, out var rawCertificate)) {
    var cert = new ParsedCertificate(rawCertificate);
    ...
}

// ReadEnterpriseCAProperties (line 526)  
if (entry.TryGetByteProperty(LDAPProperties.CACertificate, out var rawCertificate)) {
    var cert = new ParsedCertificate(rawCertificate);
    ...
}

If a RootCA or EnterpriseCA could have empty/zero-byte certificate data, these would also fail. Consider applying the HasBytes guard consistently to all certificate parsing locations.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/CommonLib/Processors/LdapPropertyProcessor.cs` around lines 513 - 516,
ReadRootCAProperties and ReadEnterpriseCAProperties both construct a
ParsedCertificate from rawCertificate without the HasBytes guard; update each
method so they call HasBytes(rawCertificate) (the existing helper) before
instantiating ParsedCertificate and only parse/assign when it returns true,
otherwise skip parsing or handle as null/absent to avoid throwing on
empty/zero-byte data.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@src/CommonLib/Processors/LdapPropertyProcessor.cs`:
- Around line 513-516: ReadRootCAProperties and ReadEnterpriseCAProperties both
construct a ParsedCertificate from rawCertificate without the HasBytes guard;
update each method so they call HasBytes(rawCertificate) (the existing helper)
before instantiating ParsedCertificate and only parse/assign when it returns
true, otherwise skip parsing or handle as null/absent to avoid throwing on
empty/zero-byte data.

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 52b978a and da2d1b4.

📒 Files selected for processing (2)
  • src/CommonLib/Processors/LdapPropertyProcessor.cs
  • test/unit/LdapPropertyTests.cs

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.

🧹 Nitpick comments (2)
test/unit/LdapPropertyTests.cs (1)

672-720: Consider extracting shared CA test setup into helpers.

There’s repeated cert creation and MockDirectoryObject scaffolding in the new CA tests; a small helper would reduce maintenance churn.

Also applies to: 736-754, 807-826

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test/unit/LdapPropertyTests.cs` around lines 672 - 720, Extract the repeated
CA test setup (creating the self-signed cert bytes and building the
MockDirectoryObject) into a small helper method (e.g., CreateMockRootCA or
BuildMockRootCAMock) and call it from
LDAPPropertyProcessor_ReadRootCAProperties,
LDAPPropertyProcessor_ReadRootCAProperties_NoCACertificate, and the other
affected tests (lines noted around 736-754 and 807-826); the helper should
return the MockDirectoryObject (or the cert bytes + dictionary) so tests can
assert on LdapPropertyProcessor.ReadRootCAProperties consistently and avoid
duplicating certificate creation and dictionary scaffolding (refer to
ECDsa.Create, CertificateRequest.CreateSelfSigned, cert.Export,
MockDirectoryObject, and LDAPProperties.CACertificate to locate code to move).
src/CommonLib/Processors/LdapPropertyProcessor.cs (1)

941-944: Make HasBytes null-safe for safer reuse.

At Line 941, HasBytes dereferences data directly. Current call sites are short-circuited, but a null-safe helper is more robust if reused later.

♻️ Proposed defensive tweak
 private static bool HasBytes(byte[] data) {
-    return data.Length > 0
-           && !(data.Length == 1 && data[0] == 0x00);
+    return data is { Length: > 0 }
+           && !(data.Length == 1 && data[0] == 0x00);
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/CommonLib/Processors/LdapPropertyProcessor.cs` around lines 941 - 944,
The HasBytes helper dereferences the input array without null-checking; update
the HasBytes(byte[] data) method to first handle null (return false for null or
empty) and then keep the existing zero-length/sole-null-byte logic so callers
can safely pass null. Modify the implementation inside HasBytes to check data ==
null (or use data?.Length) and return false early, preserving the existing
special-case (data.Length == 1 && data[0] == 0x00) to detect the solitary null
byte.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@src/CommonLib/Processors/LdapPropertyProcessor.cs`:
- Around line 941-944: The HasBytes helper dereferences the input array without
null-checking; update the HasBytes(byte[] data) method to first handle null
(return false for null or empty) and then keep the existing
zero-length/sole-null-byte logic so callers can safely pass null. Modify the
implementation inside HasBytes to check data == null (or use data?.Length) and
return false early, preserving the existing special-case (data.Length == 1 &&
data[0] == 0x00) to detect the solitary null byte.

In `@test/unit/LdapPropertyTests.cs`:
- Around line 672-720: Extract the repeated CA test setup (creating the
self-signed cert bytes and building the MockDirectoryObject) into a small helper
method (e.g., CreateMockRootCA or BuildMockRootCAMock) and call it from
LDAPPropertyProcessor_ReadRootCAProperties,
LDAPPropertyProcessor_ReadRootCAProperties_NoCACertificate, and the other
affected tests (lines noted around 736-754 and 807-826); the helper should
return the MockDirectoryObject (or the cert bytes + dictionary) so tests can
assert on LdapPropertyProcessor.ReadRootCAProperties consistently and avoid
duplicating certificate creation and dictionary scaffolding (refer to
ECDsa.Create, CertificateRequest.CreateSelfSigned, cert.Export,
MockDirectoryObject, and LDAPProperties.CACertificate to locate code to move).

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between da2d1b4 and d8e5ed7.

📒 Files selected for processing (2)
  • src/CommonLib/Processors/LdapPropertyProcessor.cs
  • test/unit/LdapPropertyTests.cs

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

🧹 Nitpick comments (2)
test/unit/LdapPropertyTests.cs (2)

721-721: Parameter naming: Consider using camelCase.

The parameter CACertBytes uses PascalCase. C# conventions typically use camelCase for method parameters (e.g., caCertBytes). This applies to all three NoCACertificate test methods.

♻️ Suggested fix
-public void LDAPPropertyProcessor_ReadRootCAProperties_NoCACertificate(byte[] CACertBytes) {
+public void LDAPPropertyProcessor_ReadRootCAProperties_NoCACertificate(byte[] caCertBytes) {

Apply similarly to LDAPPropertyProcessor_ReadAIACAProperties_NoCACertificate and LDAPPropertyProcessor_ReadEnterpriseCAProperties_NoCACertificate.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test/unit/LdapPropertyTests.cs` at line 721, The parameter name CACertBytes
in the test method LDAPPropertyProcessor_ReadRootCAProperties_NoCACertificate
(and the analogous parameters in
LDAPPropertyProcessor_ReadAIACAProperties_NoCACertificate and
LDAPPropertyProcessor_ReadEnterpriseCAProperties_NoCACertificate) should be
renamed to camelCase (caCertBytes); update the method signatures and every usage
inside each method to the new name so the tests compile and follow C# parameter
naming conventions.

893-899: Multi-byte zero arrays would bypass the HasBytes guard and reach X.509 parsing.

The HasBytes implementation (lines 941-944 of LdapPropertyProcessor.cs) only rejects empty arrays and single-byte zero values. A multi-byte all-zero array like { 0x00, 0x00 } would pass this guard and be passed to the ParsedCertificate constructor, which could trigger certificate parsing failures if such data occurs in AD.

The current test data appropriately covers the specific { 0x00 } edge case mentioned in the bug report. Consider adding a test case for multi-byte zero arrays to document whether this scenario is expected or should be guarded against.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test/unit/LdapPropertyTests.cs` around lines 893 - 899, Add a test case for
multi-byte all-zero certificate bytes to LdapPropertyTests.EmptyCertBytes to
cover the scenario where HasBytes in LdapPropertyProcessor allows arrays longer
than 1 that contain only 0x00; update the test data to include e.g. new byte[] {
0x00, 0x00 } so that behavior when passing such arrays into ParsedCertificate
(constructor used by LdapPropertyProcessor) is exercised and documents whether
additional guarding is needed in HasBytes.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@test/unit/LdapPropertyTests.cs`:
- Line 781: Remove the stray double semicolon at the end of the assertion in the
test: locate the Assert.Contains("basicconstraintpathlength", keys);; statement
in LdapPropertyTests (the failing test uses Assert.Contains) and delete the
extra semicolon so the line ends with a single semicolon.

---

Nitpick comments:
In `@test/unit/LdapPropertyTests.cs`:
- Line 721: The parameter name CACertBytes in the test method
LDAPPropertyProcessor_ReadRootCAProperties_NoCACertificate (and the analogous
parameters in LDAPPropertyProcessor_ReadAIACAProperties_NoCACertificate and
LDAPPropertyProcessor_ReadEnterpriseCAProperties_NoCACertificate) should be
renamed to camelCase (caCertBytes); update the method signatures and every usage
inside each method to the new name so the tests compile and follow C# parameter
naming conventions.
- Around line 893-899: Add a test case for multi-byte all-zero certificate bytes
to LdapPropertyTests.EmptyCertBytes to cover the scenario where HasBytes in
LdapPropertyProcessor allows arrays longer than 1 that contain only 0x00; update
the test data to include e.g. new byte[] { 0x00, 0x00 } so that behavior when
passing such arrays into ParsedCertificate (constructor used by
LdapPropertyProcessor) is exercised and documents whether additional guarding is
needed in HasBytes.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: d28c6894-a0ee-47b7-be8d-aa46c1090479

📥 Commits

Reviewing files that changed from the base of the PR and between d8e5ed7 and e1ba10f.

📒 Files selected for processing (1)
  • test/unit/LdapPropertyTests.cs

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.

🧹 Nitpick comments (1)
test/unit/LdapPropertyTests.cs (1)

719-745: Consider using camelCase for the test parameter.

The test logic is correct and covers all edge cases for empty certificates. Minor style note: C# conventions typically use camelCase for method parameters (caCertBytes instead of CACertBytes).

🔧 Suggested fix
 [Theory]
 [MemberData(nameof(EmptyCertBytes))]
-public void LDAPPropertyProcessor_ReadRootCAProperties_NoCACertificate(byte[] CACertBytes) {
+public void LDAPPropertyProcessor_ReadRootCAProperties_NoCACertificate(byte[] caCertBytes) {
     var mock = new MockDirectoryObject(
         "CN\u003dDUMPSTER-DC01-CA,CN\u003dAIA,CN\u003dPUBLIC KEY SERVICES,CN\u003dSERVICES,CN\u003dCONFIGURATION,DC\u003dDUMPSTER,DC\u003dFIRE",
         new Dictionary<string, object>
         {
             {"description", null},
             {"domain", "DUMPSTER.FIRE"},
             {"name", "DUMPSTER-DC01-CA@DUMPSTER.FIRE"},
             {"domainsid", "S-1-5-21-2697957641-2271029196-387917394"},
             {"whencreated", 1683986131},
-            {LDAPProperties.CACertificate, CACertBytes}
+            {LDAPProperties.CACertificate, caCertBytes}
         }, "","2F9F3630-F46A-49BF-B186-6629994EBCF9");
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test/unit/LdapPropertyTests.cs` around lines 719 - 745, The test method
LDAPPropertyProcessor_ReadRootCAProperties_NoCACertificate uses a PascalCase
parameter name CACertBytes which violates C# parameter naming conventions;
rename the parameter to camelCase (e.g., caCertBytes) in the method signature
and update all usages inside the test (the MockDirectoryObject initialization
and the MemberData reference stays the same) so the variable name matches;
ensure the MemberData attribute name (EmptyCertBytes) remains unchanged and run
tests to confirm no references are broken.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@test/unit/LdapPropertyTests.cs`:
- Around line 719-745: The test method
LDAPPropertyProcessor_ReadRootCAProperties_NoCACertificate uses a PascalCase
parameter name CACertBytes which violates C# parameter naming conventions;
rename the parameter to camelCase (e.g., caCertBytes) in the method signature
and update all usages inside the test (the MockDirectoryObject initialization
and the MemberData reference stays the same) so the variable name matches;
ensure the MemberData attribute name (EmptyCertBytes) remains unchanged and run
tests to confirm no references are broken.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: de76bbe9-9c59-44fc-8a33-6e4dd7a7758e

📥 Commits

Reviewing files that changed from the base of the PR and between e1ba10f and 0b8b202.

📒 Files selected for processing (1)
  • test/unit/LdapPropertyTests.cs

@lrfalslev lrfalslev merged commit f6beedd into v4 Mar 5, 2026
4 of 5 checks passed
@lrfalslev lrfalslev deleted the lfalslev/bed-7513 branch March 5, 2026 20:17
@github-actions github-actions bot locked and limited conversation to collaborators Mar 5, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants