fix: collect all subset namespace prefixes when filtering ancestor namespaces - #541
fix: collect all subset namespace prefixes when filtering ancestor namespaces#541msheby wants to merge 3 commits into
Conversation
…mespaces findNSPrefix returned only the first xmlns:* attribute on a subset element, so findAncestorNs filtered only that one prefix when deciding which ancestor namespace declarations to hoist. When a subset element in the default namespace also declared a prefixed namespace (e.g. <AuthenticatedPrivate xmlns:enc="…">), findNSPrefix returned "enc" and left the inherited default namespace in the ancestor list. The C14N serializer then rendered the default namespace twice — once from the element itself and once from the hoisted ancestor entry — producing a digest that no other implementation would ever match. Replace findNSPrefix with findSubsetNSPrefixes, which collects every xmlns:* attribute on the subset element into a Set and always includes the element's own namespace prefix (empty string for the default namespace). findAncestorNs now uses Set.has() to filter, so all already-declared prefixes are suppressed regardless of how many xmlns:* attributes appear on the subset. The change is backward-compatible: it filters more ancestor entries than before, so no previously-hoisted namespace starts being suppressed. The concrete trigger is SMPTE ST 430-3 ETMs, where the XML signature covers <AuthenticatedPrivate xmlns:enc="http://www.w3.org/2001/04/xmlenc#"> as a subset reference. Fixes node-saml#538
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (1)
🚧 Files skipped from review as they are similar to previous changes (1)
📝 WalkthroughWalkthroughThe change updates non-exclusive C14N namespace filtering. It collects all namespace prefixes declared on the subset element, filters matching ancestor namespaces, and adds regression tests for inherited default namespaces and duplicate canonical declarations. ChangesNon-exclusive C14N namespace filtering
Estimated code review effort: 2 (Simple) | ~10 minutes Mergeability Score: ⚪ Minimal · up to The change corrects namespace filtering during canonicalization and adds focused regression coverage; no actionable merge-blocking risk remains beyond normal checks and review. 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ 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 |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/utils.ts`:
- Around line 242-245: Restrict the namespace detection in the loop over
subsetAttributes to XML namespace declaration names only: match the default
xmlns attribute or names beginning with xmlns: followed by a prefix, not
ordinary names such as xmlnsfoo. Update the condition and preserve the existing
prefix extraction and subsetNsPrefixes behavior for valid declarations.
🪄 Autofix
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: 3afa25d4-29d9-4b56-849b-5f3227ba8c2c
📒 Files selected for processing (2)
src/utils.tstest/c14n-non-exclusive-unit-tests.spec.ts
…Prefixes
The /^xmlns:?/ regex also matched ordinary attributes whose names start
with "xmlns" but have no colon (e.g. xmlnsfoo). Such attributes are not
namespace declarations, but the old code added "foo" to the suppression
set, causing findAncestorNs to incorrectly drop an inherited xmlns:foo
declaration that must be hoisted to the subset root.
Replace the regex test with an exact equality check:
nodeName === "xmlns" || nodeName.startsWith("xmlns:")
Adds a regression test to cover the xmlnsfoo case.
Fixes #538
Problem
findNSPrefixreturned only the first xmlns:* attribute found on a subsetelement.
findAncestorNsthen used that single prefix to decide which ancestornamespace declarations to suppress — so any subset element that declared more
than one namespace would silently let the others through.
The concrete trigger is SMPTE ST 430-3 ETMs, where the XML signature covers
<AuthenticatedPrivate xmlns:enc="http://www.w3.org/2001/04/xmlenc#">as asubset reference. That element is in the default namespace inherited from the
document root, and also declares xmlns:enc. findNSPrefix returned "enc",
leaving
{prefix: "", namespaceURI: "…ETM…"}in the ancestor list. The C14Nserializer then rendered the default namespace twice — once from the element
itself via the defaultNs != currNs branch, and once from the hoisted ancestor
entry — producing a digest that no conformant implementation would ever match.
Fix
Replace
findNSPrefix(returns a single string) withfindSubsetNSPrefixes(returns a Set) that collects every xmlns:* attribute on the subset
element and always includes the element's own namespace prefix ("" for the
default namespace). findAncestorNs now uses Set.has() for the filter.
The change is backward-compatible: it can only suppress more ancestor entries
than before — no previously-hoisted namespace starts being retained.
Tests added
declares a prefixed namespace
in the same scenario
xmlns="…" declaration
Summary by CodeRabbit
Bug Fixes
xmlns.Tests