fix(namecheap): extract leaf + chain by scanning PEM armor#41
Merged
Conversation
rota's `get_info` was looking for `<CertificateReturned>` element
text and `<CACertificate>` element text. Neither matches Namecheap's
actual `ssl.getInfo&Returncertificate=true` response, which carries
`CertificateReturned` as an ATTRIBUTE on `<Certificates>` and packs
PEMs in nested `<Certificate>` elements:
<Certificates CertificateReturned="true" ReturnType="INDIVIDUAL">
<Certificate><![CDATA[LEAF_PEM]]></Certificate>
<CaCertificates>
<Certificate Type="INTERMEDIATE">
<Certificate><![CDATA[INTERMEDIATE_1_PEM]]></Certificate>
</Certificate>
...
</CaCertificates>
</Certificates>
Result: cert_pem and chain_pem both empty, `is_issued()` false,
polling never terminates even when status==active. So PR #40's
chain-follow lands on the right SSL ID but `await_issuance` still
hangs at the extraction step. Found by extracting the cert manually
out of band when `getInfo` returned status=active for oneiric.dev's
in-flight order: rota's parser yielded empty strings even though
the PEMs were sitting right there in the response.
Fix: new `ApiResponse::pem_blocks(label)` method scans the raw
response for `-----BEGIN <label>-----`...`-----END <label>-----`
armor and returns each block in document order. `get_info` calls
`pem_blocks("CERTIFICATE")`; first block is the leaf, rest are the
chain (concatenated with newlines). The CSR present in the same
response is safely skipped because its label is "CERTIFICATE
REQUEST" and `BEGIN CERTIFICATE-----` doesn't substring-match
`BEGIN CERTIFICATE REQUEST-----`.
This is the 6th and (hopefully) final layer in the rota+Namecheap
end-to-end renewal pipeline, after PRs #36 (reverted), #37 (CDATA
unwrap), #38 (DnsCname variant), #39 (lowercase HostName), #40
(ReplacedBy chain + Status XML path). Tests: 3 new in xml::tests
covering the leaf+chain extraction, the CSR-skip rule, and the
empty-input edge case. Total daemon test count 111 (was 108).
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.
What
get_info's cert_pem and chain_pem extractors were looking at the wrong XML locations. Namecheap returns:rota was calling
first_text("CertificateReturned")andfirst_text("CACertificate")—CertificateReturnedis an ATTRIBUTE on<Certificates>(not an element with text) andCACertificatedoesn't exist (the element is<CaCertificates>with nested<Certificate>s). Result: cert_pem + chain_pem both empty,is_issued()always false, polling hangs atawait_issuanceeven when Sectigo has issued the cert and status isactive.Found this by manually extracting the oneiric.dev cert via curl + Python while debugging PR #40's polling behavior. The cert was right there in the response that rota was discarding as 'empty'.
Fix
New
ApiResponse::pem_blocks(label)scans the raw response for-----BEGIN <label>-----...-----END <label>-----armor and returns each block in document order. Cuts through the nested-element wrapping by ignoring the XML structure entirely for cert payloads.get_infonow callspem_blocks("CERTIFICATE"); first block is the leaf, rest are the chain (joined with newlines). The CSR present in the same response is skipped automatically because its label is "CERTIFICATE REQUEST" —BEGIN CERTIFICATE-----(with trailing dashes) doesn't substring-matchBEGIN CERTIFICATE REQUEST-----.Cumulative
This is bug #6 in the rota+Namecheap end-to-end renewal pipeline. Prior PRs in this series:
Verified
cargo fmt --all --checkcleancargo clippy --workspace --all-targets -- -D warningscleancargo test --workspace --locked111 daemon tests pass (+3 new)