Skip to content

Fix incremental sync discarding every certificate it retrieves - #65

Open
Pichatorn-A4K wants to merge 1 commit into
Keyfactor:mainfrom
Pichatorn-A4K:fix/incremental-sync-emits-nothing
Open

Fix incremental sync discarding every certificate it retrieves#65
Pichatorn-A4K wants to merge 1 commit into
Keyfactor:mainfrom
Pichatorn-A4K:fix/incremental-sync-emits-nothing

Conversation

@Pichatorn-A4K

Copy link
Copy Markdown

The defect

In the incremental (non-full) branch of Synchronize():

orderCerts = GetAllConnectorCertsForOrder(caReqId, caList, divFilters, productFilters);
if (orderCerts == null || orderCerts.Count > 0)
{
    continue;
}

The predicate continues for every order whose certificates were retrieved, so the
blockingBuffer.Add loop below is reachable only when orderCerts is empty — in which case it
iterates nothing. An incremental synchronization adds zero certificates to the buffer,
unconditionally.

It also does not look like a failure: the run logs Sync complete with 0 certificates and returns
normally, which is indistinguishable from a healthy incremental sync with nothing to do. The
per-order API fan-out still happens, so the rate-limit cost is paid and the result is discarded.

The full-sync branch a few lines above has the correct comparison:

if (orderCerts == null || orderCerts.Count == 0)

Provenance

Both guards were added in the same commit — ac791a7 "Port sync CA filter from DCOM gateway" (#7) —
one correct and one inverted, which reads as a copy-paste with one edit missed rather than intent.
The predicate has not been touched since.

Worth noting: the incremental-window changes in #38, #40 and #41 all operate inside this code
path
, which cannot emit anything until this comparison is corrected.

How it was found

Reading the shipped 2.4.1 net8.0 release assembly (and confirming in 2.4.2-rc.0, which is
code-identical here) while auditing the plugin before a customer engagement. In the IL the two
branches differ exactly as the source suggests: the full-sync branch proceeds when the count is
non-zero, the incremental branch reaches the emit block only when the count is <= 0.

Verification

Verified with a fake CertCentralClient driving Synchronize() offline: with one issued order, the
full sync emits 1 certificate and the incremental sync emits 0 on main, and both emit 1 with this
change. Reverting the one character makes that test fail again.

Those tests are not in this PR because the repository has no test project and adding one is a much
larger change than the fix. Happy to contribute the harness separately if it would be useful — it
needs a small seam, since the DigiCert client is constructed inline at eight call sites, which is
what makes Synchronize() untestable today.

We also have fixes for a handful of other findings from the same audit (unbounded 429 retry,
Errors[0] dereferences on response bodies that carry no errors array, an unguarded
Intermediates[0], a duplicated ViewCertificateOrder per order) and can raise them as separate
PRs if you would like them.

🤖 Generated with Claude Code

The guard in the incremental (non-full) branch of Synchronize() reads

    if (orderCerts == null || orderCerts.Count > 0) { continue; }

so it continues for every order whose certificates were retrieved
successfully, and the blockingBuffer.Add loop below it is reachable only
when the list is empty - in which case it iterates nothing. An
incremental synchronization therefore adds zero certificates to the
buffer unconditionally, while still logging 'Sync complete with 0
certificates' and returning normally, so it is indistinguishable from a
healthy run with nothing to do.

The full-sync branch a few lines above has the correct 'Count == 0'.
Both guards were introduced in the same commit (ac791a7, 'Port sync CA
filter from DCOM gateway'), which suggests a copy-paste with one edit
missed rather than intent.

Note that the incremental-window fixes in Keyfactor#38, Keyfactor#40 and Keyfactor#41 all operate
inside this code path, which cannot emit anything until this predicate is
corrected.
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.

1 participant