diff --git a/src/node-saml/saml.ts b/src/node-saml/saml.ts index 626edb5f..7ff1e2e2 100644 --- a/src/node-saml/saml.ts +++ b/src/node-saml/saml.ts @@ -738,6 +738,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); } @@ -1465,6 +1467,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/node-saml/test-signatures.spec.ts b/test/node-saml/test-signatures.spec.ts index bf066397..b384c09e 100644 --- a/test/node-saml/test-signatures.spec.ts +++ b/test/node-saml/test-signatures.spec.ts @@ -279,4 +279,26 @@ describe("Signatures", function () { await testOneResponseBody(body, false, 1); }); }); + + 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: string) => ({ SAMLResponse: Buffer.from(str).toString("base64") }); + + const insertChars = (str: string, where: string, chars: string) => + 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); + }); + }); });