Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixed the removal of superfluous namespace declarations #94

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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