Repository navigation
fix(sync): V1 body-less REVOKED guard and incremental skip of unchanged orders (1.0.2) - #38
Open
spbsoluble wants to merge 5 commits into
Open
spbsoluble wants to merge 5 commits into
spbsoluble wants to merge 5 commits into
Conversation
…hold (0049, V1)
The V1 client never downloads a revoked certificate's body (CERTInext refuses it,
and IsCertificateDownloadable excludes status 22), so Synchronize and
GetSingleRecord emitted every revoked order with Certificate = null. For an order
the gateway never stored with a body (revoked at CERTInext before the gateway saw
it issued), the gateway persists a row with RevocationDate set and no certificate,
never clears that date, and its revoked-certificate search then fails with
"m_safeCertContext is an invalid handle" on every Command scan of the CA.
Port the V2 0049 guard to V1: before emitting a body-less REVOKED record, ask
ICertificateDataReader.GetExpirationDateByRequestId whether the gateway holds a body.
- date returned (gateway holds a body) -> emit REVOKED unchanged, so out-of-band
CA revokes still propagate.
- null (row exists, no body) -> emit FAILED with no RevocationDate/Reason.
- ArgumentException (no row), any other error, or no reader -> Sync skips the
record; GetSingleRecord returns FAILED (it can't skip).
The sync summary now reports FailedFromBodylessRevoked and BodylessRevokedSkipped.
Rows already written this way are not repaired by the plugin (the gateway never
nulls RevocationDate); they need the documented DB repair. Gateway 26.2.1 has the
same revoked-search query, so the guard is still needed after a gateway upgrade.
…body (0049) Read-only. CERTINEXT_PROBE_REVOKED_DOWNLOAD=1 calls DownloadCertificate directly on a revoked order (CERTINEXT_REVOKED_ORDER_NUMBER, or the first revoked order in the report). Sandbox 2026-11-05: refused with "Invalid request status.", confirming there is no body to backfill.
spbsoluble
force-pushed
the
fix/v1.0.2-v1-bodyless-revoked
branch
from
November 5, 2026 21:18
5e816f2 to
2e7f69a
Compare
The V1 order report only filters by calendar day (orderDateFrom = yyyy-MM-dd), so every incremental pass re-listed every order placed that day and refetched each issued or revoked one (TrackOrder + DownloadCertificate), even when the gateway already held it. A customer gateway with ~225 orders in the day made 451 serial calls per 10-minute pass; when CERTInext latency rose, passes overran the interval and ran back to back. Follow the Entrust ECS / HashiCorp Vault / Nexus pattern: on incremental passes, skip an order listed as GENERATED or REVOKED when ICertificateDataReader.GetStatusByRequestID returns the same status and GetExpirationDateByRequestId shows a stored body. New orders, status changes (pending -> issued, issued -> revoked), rows without a body, no reader, and reader errors are processed as before. Pending orders and full sync are unchanged. The sync summary now reports UnchangedInGateway. Not a regression: the date-only filter and per-order refetch date from the initial implementation.
eMudhra is withdrawing from public SSL/TLS and the emSign roots are being phased out (Chrome/Firefox: G1 certificates logged after 2026-11-16 not trusted; Apple: G1 root removed 2026-11-30). CERTInext public TLS certificates move to another CA (SSL.com for InCommon members; eMudhra offers Sectigo). Document that the plugin needs no change (it stores only the end-entity certificate and never reads the issuer), and the operator steps: import the new chain on Command, the gateway and orchestrators, update the Gateway Registration CA certificate, replace G1 certificates before Apple's removal, and confirm product codes with CERTInext. Links the InCommon and eMudhra notices.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
The V1 client never downloads a revoked certificate's body: CERTInext refuses it ("Invalid request status."), and
IsCertificateDownloadableexcludes status 22. SoSynchronizeandGetSingleRecordemit every revoked order with no certificate.That's safe when the gateway already stores the certificate. When it doesn't (the order was revoked at CERTInext before the gateway recorded it as issued), the gateway saves a row with
RevocationDateset and no certificate, and never clears that date. Its revoked-certificate search (POST .../v2/certificate/search) then fails withm_safeCertContext is an invalid handleatCertificateV2Controller.CertificateSearch. Every Command scan of the CA fails from then on, not just the affected record.This ports the V2 fix for issue 0049 to the V1 code path.
Fix
Before emitting a revoked record with no certificate, the plugin asks the gateway whether it already stores the certificate (
ICertificateDataReader.GetExpirationDateByRequestId):null)ArgumentException), other error, or no readerThe sync summary log now includes
FailedFromBodylessRevokedandBodylessRevokedSkipped.Not covered
RevocationDate, so the plugin can't repair rows that are already bad. They need a one-time DB repair: setStatus = 11andRevocationDate = NULLon rows with aRevocationDateand no certificate. The repair was tested end to end on a 25.5.0 lab gateway.Testing
BodylessRevokedGuardTests) cover each row of the table above, plus a failed refetch and a revoked record that has a body. 8 of them fail against the 1.0.1 code. Full suite: 524/524 pass.RevokedDownloadProbeTests,CERTINEXT_PROBE_REVOKED_DOWNLOAD=1): sandbox V1 download of a revoked order was refused, confirming there's no certificate to backfill.Incremental sync refetches every order placed that day (0093)
Problem
The V1 order report filters only by calendar day (
orderDateFromis sent asyyyy-MM-dd), so every incremental pass re-lists every order placed that day. The listing has no certificate, so each issued or revoked order then costs aTrackOrderplus aDownloadCertificate, one after another, even when the gateway already holds it unchanged.Seen in a customer log on 2026-11-10: about 380 orders were placed in CERTInext outside the gateway, and from then on every 10-minute pass processed about 225 orders and made 451 calls (about 8.5 minutes per pass). When CERTInext latency rose that afternoon (1.2 s to 47.6 s average), passes overran the interval and ran back to back. This is not a 1.0.1 regression: the date filter and per-order refetch date from the initial implementation.
Fix
Same rule as the Entrust ECS, HashiCorp Vault and Nexus plugins. On incremental passes only, an order listed as GENERATED or REVOKED is skipped when
ICertificateDataReader.GetStatusByRequestIDreturns the same status andGetExpirationDateByRequestIdshows a stored certificate.Processed as before: no gateway row (new order), a status change (pending to issued, issued to revoked), a row with no certificate, no reader, or a reader error. Pending orders and full sync are unchanged. The sync summary log now includes
UnchangedInGateway.Not changed: a pass still lists the whole day's orders, because the order report has no time or modified-since filter. The cost per issued order drops from two CERTInext calls to two gateway DB lookups.
Testing
IncrementalSyncUnchangedTests): unchanged issued and revoked orders skipped, new order, both status changes, row without a certificate, reader error, no reader, pending order, and full sync.TracePayloadGuardTests,ValidityResolutionTests); both pass alone and on a second full run.NU1801, the GitHub package feed was unreachable from the build machine).Docs: change of issuing CA after eMudhra's exit from public TLS
eMudhra is withdrawing from public SSL/TLS, and the emSign G1 root is being phased out: Chrome and Firefox won't trust G1 certificates logged after 2026-11-16, and Apple removes the root on 2026-11-30. CERTInext public TLS certificates move to another CA (SSL.com for InCommon members; eMudhra offers Sectigo).
docsource/configuration.mdanddocsource/overview.mdnow say the plugin needs no change, because it stores only the end-entity certificate and never reads the issuer. They list the operator steps: import the new chain on Command, the gateway and orchestrators; update the Gateway Registration CA certificate; replace G1 certificates before Apple's removal date; and confirm product codes with CERTInext. The README is regenerated fromdocsource.