Repository navigation
fix: canonicalize a node-set left at the end of the transforms - #581
Conversation
When a reference's transforms ended in `enveloped-signature`, signing digested `node.toString()` while verification appended inclusive C14N in `loadReference` and digested that. Any document whose xmldom serialization is not already canonical, such as a self-closing tag or attributes out of order, got a signature this library could not verify itself. Both sides reach the digest through `getCanonXml`, so it now converts a node-set that survives the transform list to octets with inclusive C14N, as the reference processing model requires: https://www.w3.org/TR/xmldsig-core1/#sec-ReferenceProcessingModel Signing and verification canonicalize under the same condition by construction. The explicit append in `loadReference` is left in place, so verification of existing signatures is byte-for-byte unchanged. Two canonicalization tests expected `<y/>` from `getCanonXml([enveloped-signature], y)`. That was xmldom's serialization, not C14N, which never emits empty-element tags; `[enveloped-signature, c14n]`, which verification uses for the same reference, yields `<y></y>`. What they guard, that only the signature inside the current node is removed, still holds: removing the wrong one would leave a `ds:Signature` in the output. A custom transform that returns a Node as the last transform is now C14N'd on both sides too, instead of serialized with `toString()`. The README documents custom transforms as returning a string. Fixes node-saml#549 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The TODO asked for every transform to return the same type. The reference processing model lets a transform yield either a node-set or octets and converts a node-set left at the end with Canonical XML, which getCanonXml now does, so there is nothing left to do. Its notes on why individual transforms return what they do describe those transforms, not this loop. https://www.w3.org/TR/xmldsig-core1/#sec-ReferenceProcessingModel Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Advanced Run ID: 📒 Files selected for processing (2)
🚧 Files skipped from review as they are similar to previous changes (1)
Included review availability: Your plan provides up to 2 included reviews per hour; 1 remains after this review. 📝 WalkthroughWalkthroughThe signing path now canonicalizes DOM nodes returned by the final transform before digesting. Tests update canonical XML expectations, add enveloped-signature verification coverage, and document the changed behavior. ChangesDOM result canonicalization
Priority: ➖ Normal Estimated code review effort: 3 (Moderate) | ~20 minutes Change: Bug fix · Severity of issue fixed: Medium Sequence Diagram(s)sequenceDiagram
participant SignedXml
participant getCanonXml
participant C14N
participant Verifier
SignedXml->>getCanonXml: Apply enveloped-signature transform
getCanonXml->>C14N: Canonicalize DOM node
C14N-->>SignedXml: Return canonical octets
SignedXml->>Verifier: Produce signed XML
Verifier->>C14N: Canonicalize referenced node
C14N-->>Verifier: Return matching octets
Suggested reviewers: Merge Risk: ⚪ Minimal · up to The canonicalization change is covered by updated regression tests for empty elements, attribute order, enveloped signatures, and inherited namespaces. No unresolved merge risk is identified. 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
Full details: Linked Issues checkExplanation The implementation satisfies the main coding requirements in
✨ 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 |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## master #581 +/- ##
==========================================
+ Coverage 75.95% 78.50% +2.55%
==========================================
Files 9 9
Lines 1048 1098 +50
Branches 273 280 +7
==========================================
+ Hits 796 862 +66
+ Misses 144 135 -9
+ Partials 108 101 -7 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…ests The README now clarifies that both `Reference` transforms and `SignedInfo` canonicalization algorithms that yield a DOM `Node` are converted to octets using inclusive canonicalization, aligning with the XMLDsig Core 1.0 reference processing model. Earlier releases incorrectly serialized both with `xmldom`. A new integration test confirms that the `enveloped-signature` transform correctly preserves inherited namespaces during canonicalization, ensuring this crucial aspect of XML canonicalization is handled properly. The existing `enveloped-signature` test is also simplified and made more robust by explicitly setting `isEmptyUri: true` when referencing the document root.
Summary
When a reference's transforms ended in
enveloped-signature, signing digested xmldom'snode.toString()while verification appended inclusive C14N inloadReference. Any document whose serialization is not already canonical, such as one with a self-closing tag or attributes out of order, got a signature no version of this library could verify.Both sides reach the digest through
getCanonXml, so it now converts a node-set left after the last transform to octets with inclusive C14N, as the reference processing model requires. Signing and verification canonicalize under the same condition by construction, and verification of existing signatures using the built-in algorithms is unchanged. This also drops thegetCanonXmlTODO about transforms returning either a Node or a string. Transforms after one that returns a string are still skipped rather than getting the octet-to-node-set conversion; that is tracked in #111.Compatibility
Documented in the README's Upgrading section.
getCanonXml()is public. For a transform list ending in a node-set it now returns canonical XML (<y></y>) instead of xmldom's serialization (<y/>). Two tests pinned the old output and are updated.CanonicalizationOrTransformationAlgorithm.process()is typedNode | string. A custom transform, or a customSignedInfocanonicalization algorithm, that returns a Node is now canonicalized on both sides, so its digest or signature differs from 6.1.x: signatures created with it by 6.1.x no longer verify, and the reverse. Signers and verifiers using one need to upgrade together.Fixes #549
🤖 Generated with Claude Code
Summary by CodeRabbit
Bug Fixes
Documentation