CVE-2023-40178: Insufficient Session Expiration (CWE-613)
value: Node-SAML is a SAML library not dependent on any frameworks that runs in Node. The lack of checking of current timestamp allows a LogoutRequest XML to be reused multiple times even when the current time is past the NotOnOrAfter. This could impact the user where they would be logged out from an expired LogoutRequest. In bigger contexts, if LogoutRequests are sent out in mass to different SPs, this could impact many users on a large scale. This issue was patched in version 4.0.5.

CWE description: According to WASC, "Insufficient Session Expiration is when a web site permits an attacker to reuse old session credentials or session IDs for authorization."
CVSS v3 base score: 5.3
Published: 2023-08-23T21:15Z
Repository: https://github.com/node-saml/node-saml
Language: TypeScript
Files changed: src/saml.ts, test/static/logout_request_with_bad_expiration.xml, test/tests.spec.ts
Commit date: 2023-08-18 14:07:34 -0400
Commit message: Merge pull request from GHSA-vx8m-6fhw-pccw

* Added verifyLogoutRequest to validatePostRequestAsync flow

* Added bad expiration test

* Updated IssueInstance to be in the past
Diff: diff --git a/src/saml.ts b/src/saml.ts
index a8cc6f3..446ae1c 100644
--- a/src/saml.ts
+++ b/src/saml.ts
@@ -1263,10 +1263,11 @@ class SAML {
     this: SAML,
     doc: XMLOutput,
     dom: Document,
   ): Promise<{ profile: Profile; loggedOut: boolean }> {
     const request = doc.LogoutRequest;
+    this.verifyLogoutRequest(doc);
     if (request) {
       const profile = {} as Profile;
       if (request.$.ID) {
         profile.ID = request.$.ID;
       } else {
diff --git a/test/static/logout_request_with_bad_expiration.xml b/test/static/logout_request_with_bad_expiration.xml
new file mode 100644
index 0000000..a2d7fde
--- /dev/null
+++ b/test/static/logout_request_with_bad_expiration.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<saml2p:LogoutRequest xmlns:saml2p="urn:oasis:names:tc:SAML:2.0:protocol" Destination="http://idp.example.com/SingleLogoutService.php" ID="pfxd4d369e8-9ea1-780c-aff8-a1d11a9862a1" IssueInstant="2013-08-17T22:17:51.000Z" NotOnOrAfter="2022-08-17T22:17:51.000Z" Version="2.0">
+    <saml2:Issuer xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion">http://sp.example.com/demo1/metadata.php</saml2:Issuer>
+<saml2:NameID xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion">test@email.com</saml2:NameID>
+    <saml2p:SessionIndex>_P230330115104244DUB9FUDV49NSOB5</saml2p:SessionIndex>
+<Signature xmlns="http://www.w3.org/2000/09/xmldsig#"><SignedInfo><CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/><SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/><Reference URI="#pfxd4d369e8-9ea1-780c-aff8-a1d11a9862a1"><Transforms><Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/><Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/></Transforms><DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/><DigestValue>26LYQJFuGaMg9xdZLo+6J118KHU=</DigestValue></Reference></SignedInfo><SignatureValue>OTzlyaL0HDLLC9xLgwctQhHB5hthWjmnVho6g7p8e5m84c4J+/08GLaC8/Iz/cqXcoxU32PZr3MLnPAqEY2RwfCIldhMyXmRdVRi4DkiXmOmhw+tzgbXo6kGe+28rTSnXJ7L97NkX48T83bsfGZ+LAdEHe9ZQUL+vWZYXwn7xVagiTJ1YjfUO44UVdTelAJ9kQKQcIjW7A6WafyYqC7dqahvGsgYJ4o4grBkaP+PxfJ5vwuTW0qkjLH7wcLRfpJDeCiWwdNr9W6TgV8C8qJ0fDtl5xfYvSPmbDViXA89TQhu9uCu7tJQpjCeCv1hAO7igj2TbP87QNTyOhseNKpzKA==</SignatureValue><KeyInfo><X509Data/></KeyInfo></Signature></saml2p:LogoutRequest>
\ No newline at end of file
diff --git a/test/tests.spec.ts b/test/tests.spec.ts
index 23f270c..56cf048 100644
--- a/test/tests.spec.ts
+++ b/test/tests.spec.ts
@@ -2845,10 +2845,22 @@ describe("node-saml /", function () {
       await assert.rejects(samlObj.validatePostRequestAsync(body), {
         message: "Invalid signature on documentElement",
       });
     });
 
+    it("errors if expired", async () => {
+      const body = {
+        SAMLRequest: fs.readFileSync(
+          __dirname + "/static/logout_request_with_bad_expiration.xml",
+          "base64",
+        ),
+      };
+      await assert.rejects(samlObj.validatePostRequestAsync(body), {
+        message: "SAML assertion expired: clocks skewed too much",
+      });
+    });
+
     it("returns profile for valid signature", async () => {
       const body = {
         SAMLRequest: fs.readFileSync(
           __dirname + "/static/logout_request_with_good_signature.xml",
           "base64",

Fixed code:     this.verifyLogoutRequest(doc);
<?xml version="1.0" encoding="UTF-8"?>
<saml2p:LogoutRequest xmlns:saml2p="urn:oasis:names:tc:SAML:2.0:protocol" Destination="http://idp.example.com/SingleLogoutService.php" ID="pfxd4d369e8-9ea1-780c-aff8-a1d11a9862a1" IssueInstant="2013-08-17T22:17:51.000Z" NotOnOrAfter="2022-08-17T22:17:51.000Z" Version="2.0">
    <saml2:Issuer xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion">http://sp.example.com/demo1/metadata.php</saml2:Issuer>
<saml2:NameID xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion">test@email.com</saml2:NameID>
    <saml2p:SessionIndex>_P230330115104244DUB9FUDV49NSOB5</saml2p:SessionIndex>
<Signature xmlns="http://www.w3.org/2000/09/xmldsig#"><SignedInfo><CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/><SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/><Reference URI="#pfxd4d369e8-9ea1-780c-aff8-a1d11a9862a1"><Transforms><Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/><Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/></Transforms><DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/><DigestValue>26LYQJFuGaMg9xdZLo+6J118KHU=</DigestValue></Reference></SignedInfo><SignatureValue>OTzlyaL0HDLLC9xLgwctQhHB5hthWjmnVho6g7p8e5m84c4J+/08GLaC8/Iz/cqXcoxU32PZr3MLnPAqEY2RwfCIldhMyXmRdVRi4DkiXmOmhw+tzgbXo6kGe+28rTSnXJ7L97NkX48T83bsfGZ+LAdEHe9ZQUL+vWZYXwn7xVagiTJ1YjfUO44UVdTelAJ9kQKQcIjW7A6WafyYqC7dqahvGsgYJ4o4grBkaP+PxfJ5vwuTW0qkjLH7wcLRfpJDeCiWwdNr9W6TgV8C8qJ0fDtl5xfYvSPmbDViXA89TQhu9uCu7tJQpjCeCv1hAO7igj2TbP87QNTyOhseNKpzKA==</SignatureValue><KeyInfo><X509Data/></KeyInfo></Signature></saml2p:LogoutRequest>
    it("errors if expired", async () => {
      const body = {
        SAMLRequest: fs.readFileSync(
          __dirname + "/static/logout_request_with_bad_expiration.xml",
          "base64",
        ),
      };
      await assert.rejects(samlObj.validatePostRequestAsync(body), {
        message: "SAML assertion expired: clocks skewed too much",
      });
    });