Skip to content

Commit

Permalink
Merge pull request #94 from rowland66/fix-superfluous-namespace-decla…
Browse files Browse the repository at this point in the history
…ration-cleanup

Fixed the removal of superfluous namespace declarations
  • Loading branch information
russellhaering authored Apr 20, 2023
2 parents 1bb67cd + 1245f63 commit 5a3be1c
Show file tree
Hide file tree
Showing 4 changed files with 69 additions and 17 deletions.
40 changes: 25 additions & 15 deletions canonicalize.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,7 @@ func (c *NullCanonicalizer) Algorithm() AlgorithmID {
}

func (c *NullCanonicalizer) Canonicalize(el *etree.Element) ([]byte, error) {
scope := make(map[string]struct{})
return canonicalSerialize(canonicalPrep(el, scope, false, true))
return canonicalSerialize(canonicalPrep(el, false, true))
}

type c14N10ExclusiveCanonicalizer struct {
Expand Down Expand Up @@ -89,8 +88,7 @@ func MakeC14N11WithCommentsCanonicalizer() Canonicalizer {

// Canonicalize transforms the input Element into a serialized XML document in canonical form.
func (c *c14N11Canonicalizer) Canonicalize(el *etree.Element) ([]byte, error) {
scope := make(map[string]struct{})
return canonicalSerialize(canonicalPrep(el, scope, true, c.comments))
return canonicalSerialize(canonicalPrep(el, true, c.comments))
}

func (c *c14N11Canonicalizer) Algorithm() AlgorithmID {
Expand Down Expand Up @@ -123,8 +121,7 @@ func (c *c14N10RecCanonicalizer) Canonicalize(inputXML *etree.Element) ([]byte,
parentNamespaceAttributes, parentXmlAttributes := getParentNamespaceAndXmlAttributes(inputXML)
inputXMLCopy := inputXML.Copy()
enhanceNamespaceAttributes(inputXMLCopy, parentNamespaceAttributes, parentXmlAttributes)
scope := make(map[string]struct{})
return canonicalSerialize(canonicalPrep(inputXMLCopy, scope, true, c.comments))
return canonicalSerialize(canonicalPrep(inputXMLCopy, true, c.comments))
}

func (c *c14N10RecCanonicalizer) Algorithm() AlgorithmID {
Expand Down Expand Up @@ -161,8 +158,12 @@ const nsSpace = "xmlns"
//
// TODO(russell_h): This is very similar to excCanonicalPrep - perhaps they should
// be unified into one parameterized function?
func canonicalPrep(el *etree.Element, seenSoFar map[string]struct{}, strip bool, comments bool) *etree.Element {
_seenSoFar := make(map[string]struct{})
func canonicalPrep(el *etree.Element, strip bool, comments bool) *etree.Element {
return canonicalPrepInner(el, make(map[string]string), strip, comments)
}

func canonicalPrepInner(el *etree.Element, seenSoFar map[string]string, strip bool, comments bool) *etree.Element {
_seenSoFar := make(map[string]string)
for k, v := range seenSoFar {
_seenSoFar[k] = v
}
Expand All @@ -171,16 +172,25 @@ func canonicalPrep(el *etree.Element, seenSoFar map[string]struct{}, strip bool,
sort.Sort(etreeutils.SortedAttrs(ne.Attr))
n := 0
for _, attr := range ne.Attr {
if attr.Space != nsSpace {
if attr.Space != nsSpace && !(attr.Space == "" && attr.Key == nsSpace) {
ne.Attr[n] = attr
n++
continue
}
key := attr.Space + ":" + attr.Key
if _, seen := _seenSoFar[key]; !seen {
ne.Attr[n] = attr
n++
_seenSoFar[key] = struct{}{}

if attr.Space == nsSpace {
key := attr.Space + ":" + attr.Key
if uri, seen := _seenSoFar[key]; !seen || attr.Value != uri {
ne.Attr[n] = attr
n++
_seenSoFar[key] = attr.Value
}
} else {
if uri, seen := _seenSoFar[nsSpace]; (!seen && attr.Value != "") || attr.Value != uri {
ne.Attr[n] = attr
n++
_seenSoFar[nsSpace] = attr.Value
}
}
}
ne.Attr = ne.Attr[:n]
Expand All @@ -199,7 +209,7 @@ func canonicalPrep(el *etree.Element, seenSoFar map[string]struct{}, strip bool,
for i, token := range ne.Child {
childElement, ok := token.(*etree.Element)
if ok {
ne.Child[i] = canonicalPrep(childElement, _seenSoFar, strip, comments)
ne.Child[i] = canonicalPrepInner(childElement, _seenSoFar, strip, comments)
}
}

Expand Down
38 changes: 38 additions & 0 deletions canonicalize_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,44 @@ func TestExcC14nRedeclareDefaultNamespace(t *testing.T) {
runCanonicalizationTest(t, canonicalizer, input, expected)
}

func TestC14N10RecCanonicalizer(t *testing.T) {
// From https://www.w3.org/TR/2001/REC-xml-c14n-20010315#Example-SETags
input := `<doc>
<e1 />
<e2 ></e2>
<e3 name = "elem3" id="elem3" />
<e4 name="elem4" id="elem4" ></e4>
<e5 a:attr="out" b:attr="sorted" attr2="all" attr="I'm"
xmlns:b="http://www.ietf.org"
xmlns:a="http://www.w3.org"
xmlns="http://example.org"/>
<e6 xmlns="" xmlns:a="http://www.w3.org">
<e7 xmlns="http://www.ietf.org">
<e8 xmlns="" xmlns:a="http://www.w3.org">
<e9 xmlns="" xmlns:a="http://www.ietf.org"/>
</e8>
</e7>
</e6>
</doc>`
expected := `<doc>
<e1></e1>
<e2></e2>
<e3 id="elem3" name="elem3"></e3>
<e4 id="elem4" name="elem4"></e4>
<e5 xmlns="http://example.org" xmlns:a="http://www.w3.org" xmlns:b="http://www.ietf.org" attr="I'm" attr2="all" b:attr="sorted" a:attr="out"></e5>
<e6 xmlns:a="http://www.w3.org">
<e7 xmlns="http://www.ietf.org">
<e8 xmlns="">
<e9 xmlns:a="http://www.ietf.org"></e9>
</e8>
</e7>
</e6>
</doc>`

canonicalizer := MakeC14N10RecCanonicalizer()
runCanonicalizationTest(t, canonicalizer, input, expected)
}

func TestC14N10RecCanonicalizerWithNamespaceInheritance(t *testing.T) {
input := `<RootElement xmlns="http://www.example.com/ns1" xmlns:ns2="http://www.example.com/ns2">
<ns2:ChildElement>
Expand Down
4 changes: 4 additions & 0 deletions etreeutils/sort.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,10 @@ func (a SortedAttrs) Less(i, j int) bool {
}

// Attributes in the same namespace are sorted by their Namespace URI, not the prefix.
// NOTE: This implementation is not complete because it does not consider namespace
// prefixes declared in ancestor elements. A complete solution would ideally use the
// Attribute.NamespaceURI() method obtain a namespace URI for sorting, but the
// beevik/etree library needs to be fixed to provide the correct value first.
if a[i].Key == a[j].Key {
var leftNS, rightNS etree.Attr
for n := range a {
Expand Down
4 changes: 2 additions & 2 deletions validate.go
Original file line number Diff line number Diff line change
Expand Up @@ -363,10 +363,10 @@ func (ctx *ValidationContext) findSignature(root *etree.Element) (*types.Signatu
canonicalSignedInfo = detachedSignedInfo

case CanonicalXML11AlgorithmId, CanonicalXML10RecAlgorithmId:
canonicalSignedInfo = canonicalPrep(detachedSignedInfo, map[string]struct{}{}, true, false)
canonicalSignedInfo = canonicalPrep(detachedSignedInfo, true, false)

case CanonicalXML11WithCommentsAlgorithmId, CanonicalXML10WithCommentsAlgorithmId:
canonicalSignedInfo = canonicalPrep(detachedSignedInfo, map[string]struct{}{}, true, true)
canonicalSignedInfo = canonicalPrep(detachedSignedInfo, true, true)

default:
return fmt.Errorf("invalid CanonicalizationMethod on Signature: %s", c14NAlgorithm)
Expand Down

0 comments on commit 5a3be1c

Please sign in to comment.