diff --git a/src/passport-saml/saml.ts b/src/passport-saml/saml.ts index 0cb93486..1cbbc521 100644 --- a/src/passport-saml/saml.ts +++ b/src/passport-saml/saml.ts @@ -730,6 +730,8 @@ class SAML { if (totalReferencedNodes.length > 1) { return false; } + // normalize XML to replace XML-encoded carriage returns with actual carriage returns + fullXml = this.normalizeXml(fullXml); fullXml = this.normalizeNewlines(fullXml); return sig.checkSignature(fullXml); } @@ -1418,6 +1420,12 @@ class SAML { // https://github.com/node-saml/passport-saml/issues/431#issuecomment-718132752 return xml.replace(/\r\n?/g, "\n"); } + + normalizeXml(xml: string): string { + // we can use this utility to parse and re-stringify XML + // `DOMParser` will take care of normalization tasks, like replacing XML-encoded carriage returns with actual carriage returns + return new xmldom.DOMParser({}).parseFromString(xml).toString(); + } } export { SAML }; diff --git a/test/test-signatures.js b/test/test-signatures.js index 2972a542..98a795c9 100644 --- a/test/test-signatures.js +++ b/test/test-signatures.js @@ -100,4 +100,27 @@ describe('Signatures', function() { }); + describe("Signature on saml:Response with XML-encoded carriage returns", () => { + const samlResponseXml = fs + .readFileSync( + __dirname + "/static/signatures/valid/response.root-unsigned.assertion-signed.xml" + ) + .toString(); + const makeBody = (str) => ({ SAMLResponse: Buffer.from(str).toString("base64") }); + + const insertChars = (str, where, chars) => + str.replace(new RegExp(`()(.{10})(.{10})`), `$1$2${chars}$3`); + + it("SignatureValue with ", async () => { + const body = makeBody(insertChars(samlResponseXml, "SignatureValue", " ")); + await testOneResponseBody(body, false, 2); + }); + + it("SignatureValue with ", async () => { + const body = makeBody(insertChars(samlResponseXml, "SignatureValue", " ")); + await testOneResponseBody(body, false, 2); + }); + + }); + });