From 7d3b0e0f7e398c80aa82998abe2ab15197b7f62b Mon Sep 17 00:00:00 2001 From: mhassan1 <marc.j.hassan@gmail.com> Date: Thu, 25 Mar 2021 13:56:33 -0400 Subject: [PATCH] resolve XML-encoded carriage returns during signature validation --- src/passport-saml/saml.ts | 8 ++++++++ test/test-signatures.js | 23 +++++++++++++++++++++++ 2 files changed, 31 insertions(+) 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(`(<ds:${where}>)(.{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); + }); + + }); + });