1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118
|
package validator
import (
"bytes"
"encoding/xml"
"fmt"
"io"
"strings"
"testing"
"github.com/stretchr/testify/require"
)
func TestValidXML(t *testing.T) {
docs := []string{
`<Root></Root>`,
`<x:Root></x:Root>`,
`<x:Root xmlns:x="http://example.com/"></x:Root>`,
`<x:Root xmlns="http://example.com/"></x:Root>`,
`<Root xmlns="http://example.com/1" xmlns="http://example.com/2"></Root>`,
`<?xml version="1.0" encoding="EUC-JP"?><Root></Root>`,
`<Root>text "hello"</Root>`,
`<Root><![CDATA[text "hello"]]></Root>`,
`<!-- comment --><Root/>`,
`<Root xmlns="http://example.com/1" x:attr="y"/>`,
`<x:Root xmlns="http://example.com/1" x:attr="y" x:attr2="z"/>`,
`<x:Root xmlns:x="http://example.com/"><Element xmlns:x="http://example.com/"/></x:Root>`,
`&reference;`,
}
for _, doc := range docs {
require.NoError(t, Validate(bytes.NewBufferString(doc)), "Should pass on valid XML documents")
}
}
func TestUnparseableXML(t *testing.T) {
var err error
err = Validate(bytes.NewBufferString(
`<Root><!--`))
require.Error(t, err, "Should error on unclosed comment")
require.IsType(t, &xml.SyntaxError{}, err, "Error should be an &xml.SyntaxError")
err = Validate(bytes.NewBufferString(
`<Root>]]></Root>`))
require.Error(t, err, "Should error on unexpected ']]>' sequence")
require.IsType(t, &xml.SyntaxError{}, err, "Error should be an &xml.SyntaxError")
errs := ValidateAll(bytes.NewBufferString(
`<Root ::attr="x">]]><x::Element/></Root>`))
if el := tokenize(t, `<Root :="value"/>`).(xml.StartElement); el.Attr[0].Name.Local == `:` {
// go1.17+
require.Len(t, errs, 1, "Should return exactly one error")
require.Error(t, errs[0], "Should error on unexpected ']]>' sequence")
require.IsType(t, &xml.SyntaxError{}, errs[0], "Error should be an &xml.SyntaxError")
} else {
// go1.16 and older
require.Len(t, errs, 2, "Should return exactly two errors")
require.Error(t, errs[0], "Should error on bad attribute")
require.Error(t, errs[1], "Should error on unexpected ']]>' sequence")
require.IsType(t, &xml.SyntaxError{}, errs[1], "Error should be an &xml.SyntaxError")
}
}
func TestTokenEquals(t *testing.T) {
tokens := []xml.Token{
tokenize(t, `token`),
tokenize(t, `<!--token-->`),
tokenize(t, `<!token>`),
tokenize(t, `</token>`),
tokenize(t, `<?token?>`),
tokenize(t, `<token>`),
}
for i, token1 := range tokens {
for j, token2 := range tokens {
if i != j {
require.False(t, tokenEquals(token1, token2), fmt.Sprintf("A token of type %T shouldn't equal a token of type %T", token1, token2))
} else {
require.True(t, tokenEquals(token1, xml.CopyToken(token2)), "A token should equal a copy of itself")
}
}
}
nonToken := struct{}{}
require.False(t, tokenEquals(nonToken, nonToken), "Non-token types should never equal")
}
func TestErrorMessages(t *testing.T) {
require.Equal(t, "validator: in token starting at 2:16: unexpected EOF",
XMLValidationError{34, 54, 2, 16, io.ErrUnexpectedEOF}.Error(),
"Validation error message should match expectation")
require.Equal(t, "roundtrip error: expected {{ Foo} []}, observed {{ Bar} []}",
XMLRoundtripError{tokenize(t, `<Foo>`), tokenize(t, `<Bar>`), nil}.Error(),
"Roundtrip error message with mismatching tokens should match expectation")
require.Equal(t, "roundtrip error: unexpected overflow after token: bar",
XMLRoundtripError{tokenize(t, `<Foo>`), tokenize(t, `<Foo>`), []byte(`bar`)}.Error(),
"Roundtrip error message with overflow should match expectation")
}
var errSink []error
func BenchmarkSAMLResponse(b *testing.B) {
responseXML := `<samlp:Response xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" xmlns:dsig="http://www.w3.org/2000/09/xmldsig#" xmlns:enc="http://www.w3.org/2001/04/xmlenc#" xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" xmlns:x500="urn:oasis:names:tc:SAML:2.0:profiles:attribute:X500" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" Destination="http://127.0.0.1:5556/callback" ID="id-IWlPTptSB-PlR80dwt8ZhVeG70mrz7nPvTVrhduK" InResponseTo="_e66b3a98-831c-4c96-5706-b63fe0549624" IssueInstant="2016-12-12T16:54:35Z" Version="2.0"><saml:Issuer Format="urn:oasis:names:tc:SAML:2.0:nameid-format:entity">https://deaoam-dev02.jpl.nasa.gov:14101/oam/fed</saml:Issuer><samlp:Status><samlp:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Success"/></samlp:Status><saml:Assertion ID="id-rT9rTqxdQC9j34YhVeNayUWC9EbIBgym6gp-MZt-" IssueInstant="2016-12-12T16:54:35Z" Version="2.0"><saml:Issuer Format="urn:oasis:names:tc:SAML:2.0:nameid-format:entity">https://deaoam-dev02.jpl.nasa.gov:14101/oam/fed</saml:Issuer><dsig:Signature><dsig:SignedInfo><dsig:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/><dsig:SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/><dsig:Reference URI="#id-rT9rTqxdQC9j34YhVeNayUWC9EbIBgym6gp-MZt-"><dsig:Transforms><dsig:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/><dsig:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/></dsig:Transforms><dsig:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/><dsig:DigestValue>z1HD/59hv6UOd5+jeG+ihaFWLgI=</dsig:DigestValue></dsig:Reference></dsig:SignedInfo><dsig:SignatureValue>I99oG5kiOfIgbXYa21z/TOmzftTkFnXe9ObhBNSKit9kAhT93apYROqqXv4Ax96P144Ld7ERX1hgJsytK8LC2874Pk7QrSNm4zvW3x0D4GR4lM06CvJK/EhIur3TrCUJDPigvyP7TJitheCyBejwt0x0lqNP/OzR3tMbAIMRoho=</dsig:SignatureValue></dsig:Signature><saml:Subject><saml:NameID Format="urn:oasis:names:tc:SAML:2.0:nameid-format:persistent" NameQualifier="https://deaoam-dev02.jpl.nasa.gov:14101/oam/fed" SPNameQualifier="JSAuth">pkieu</saml:NameID><saml:SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer"><saml:SubjectConfirmationData InResponseTo="_e66b3a98-831c-4c96-5706-b63fe0549624" NotOnOrAfter="2016-12-12T16:59:35Z" Recipient="http://127.0.0.1:5556/callback"/></saml:SubjectConfirmation></saml:Subject><saml:Conditions NotBefore="2016-12-12T16:54:35Z" NotOnOrAfter="2016-12-12T16:59:35Z"><saml:AudienceRestriction><saml:Audience>JSAuth</saml:Audience></saml:AudienceRestriction></saml:Conditions><saml:AuthnStatement AuthnInstant="2016-12-12T16:54:10Z" SessionIndex="id-l3NCbxKoBfUZcuKhlotMuIF3ydgYJgGGG6BGTTU6" SessionNotOnOrAfter="2016-12-12T17:54:35Z"><saml:AuthnContext><saml:AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport</saml:AuthnContextClassRef></saml:AuthnContext></saml:AuthnStatement></saml:Assertion></samlp:Response>`
for i := 0; i < b.N; i++ {
errSink = ValidateAll(bytes.NewBufferString(responseXML))
}
}
func tokenize(t *testing.T, s string) xml.Token {
decoder := xml.NewDecoder(strings.NewReader(s))
token, err := decoder.RawToken()
require.NoError(t, err, "Tokenization should succeed")
return token
}
|