CVE-2022-24375 (JavaScript, published 2022-08-24T05:15Z)
Description: value: The package node-opcua before 2.74.0 are vulnerable to Denial of Service (DoS) when bypassing the limitations for excessive memory consumption by sending multiple CloseSession requests with the deleteSubscription parameter equal to False.
Severity: nan (CVSS v3 base score: 7.5)
Weakness: CWE-400 \u2014 Uncontrolled Resource Consumption
The product does not properly control the allocation and maintenance of a limited resource, thereby enabling an actor to influence the amount of resources consumed, eventually leading to the exhaustion of available resources.
Repository: https://github.com/node-opcua/node-opcua
Files changed:
packages/node-opcua-client/source/private/client_session_impl.ts
packages/node-opcua-client/source/private/client_subscription_impl.ts
packages/node-opcua-end2end-test/test/discovery/u_test_discovery_server.ts
packages/node-opcua-end2end-test/test/end_to_end/u_test_e2e_BrowseRequest.js
packages/node-opcua-end2end-test/test/end_to_end/u_test_e2e_SubscriptionUseCase.js
packages/node-opcua-end2end-test/test/end_to_end/u_test_e2e_Subscription_Transfer.js
packages/node-opcua-end2end-test/test/end_to_end/u_test_e2e_browse_read.js
packages/node-opcua-end2end-test/test/end_to_end/u_test_e2e_registerNodes.js
packages/node-opcua-end2end-test/test/end_to_end/u_test_e2e_set_triggering.ts
packages/node-opcua-end2end-test/test/end_to_end/u_test_e2e_test_accessing_service_before_session_is_activated.js
packages/node-opcua-end2end-test/test/end_to_end/u_test_e2e_transfer_session.js
packages/node-opcua-secure-channel/source/client/client_secure_channel_layer.ts
packages/node-opcua-server-discovery/source/opcua_discovery_server.ts
packages/node-opcua-server/source/opcua_server.ts
packages/node-opcua-server/source/server_publish_engine.ts
packages/node-opcua-server/test/test_server_engine_subscription.js
packages/node-opcua-server/test/test_server_publish_engine.ts
packages/parallel_test.js
Commit message: server: use ServiceFault on request error


Vulnerable code:
    promoteOpaqueStructure,
import { BrowseNextRequest, BrowseNextResponse, HistoryReadValueIdOptions, WriteValueOptions } from "node-opcua-types";
                // use soft error, no exceptions
                return callback(null, response);
    OPCUADiscoveryServer
            function check_response(err: Error | null, response: any): void {
                should.not.exist(err);
                response.responseHeader.serviceResult.should.eql(StatusCodes.BadDiscoveryUrlMissing);
            await send_registered_server_request(discoveryServerEndpointUrl, request, check_response);
            function check_response(err: Error | null, response: any) {
                should.not.exist(err);
                response.responseHeader.serviceResult.should.eql(StatusCodes.BadInvalidArgument);
            await send_registered_server_request(discoveryServerEndpointUrl, request, check_response);
            function check_response(err: Error | null, response: any) {
                should.not.exist(err);
                response.responseHeader.serviceResult.should.eql(StatusCodes.BadServerNameMissing);
            await send_registered_server_request(discoveryServerEndpointUrl, request, check_response);
            OPCUAServer.registry.count().should.eql(0);1162
                    g_session.performMessageTransaction(browseNextRequest, function(err, response) {
                        response.responseHeader.serviceResult.should.equal(StatusCodes.BadNothingToDo);
    installSessionLogging

                                        should.exist(response);
                                        should.exist(response);
                        createMonitoredItemsResponse.responseHeader.serviceResult.should.eql(StatusCodes.BadNothingToDo);
                        modifyMonitoredItemsResponse.responseHeader.serviceResult.should.eql(StatusCodes.BadNothingToDo);
                        deleteMonitoredItemsResponse.responseHeader.serviceResult.should.eql(StatusCodes.BadNothingToDo);

                            tracelog("inner", err ? err.message: "");
                    tracelog("done", err ? err.message: "");
                        response.should.be.instanceof(RepublishResponse);
                        response.responseHeader.serviceResult.should.eql(StatusCodes.BadMessageNotAvailable);
                        response.should.be.instanceof(RepublishResponse);
                        response.responseHeader.serviceResult.should.eql(StatusCodes.BadSubscriptionIdInvalid);

                        response1.responseHeader.serviceResult.should.eql(StatusCodes.BadNoSubscription);
                        response2.responseHeader.serviceResult.should.eql(StatusCodes.BadNoSubscription);
                        response3.responseHeader.serviceResult.should.eql(StatusCodes.BadNoSubscription);
                    const response = dataValues;
const opcua = require("node-opcua");
const StatusCodes = opcua.StatusCodes;
const OPCUAClient = opcua.OPCUAClient;
const UnregisterNodesRequest = opcua.UnregisterNodesRequest;
const RegisterNodesRequest = opcua.RegisterNodesRequest;
const AttributeIds = opcua.AttributeIds;
const DataType = opcua.DataType;




            perform_operation_on_client_session(client, endpointUrl, function (session, inner_done) {

                const request = new RegisterNodesRequest({
                    nodesToRegister: []
                });
                session.performMessageTransaction(request, function (err/*, response*/) {
                    err.message.should.match(/BadNothingToDo/);
                    inner_done();
                });

            }, done);

            perform_operation_on_client_session(client, endpointUrl, function (session, inner_done) {

                const request = new RegisterNodesRequest({
                    nodesToRegister: [
                        "ns=0;i=1"
                    ]
                });
                session.performMessageTransaction(request, function (err, response) {
                    should.not.exist(err);
                    response.registeredNodeIds.length.should.eql(1);
                    inner_done();
                });

            }, done);
            perform_operation_on_client_session(client, endpointUrl, function (session, inner_done) {

                const request = new UnregisterNodesRequest({
                    nodesToUnregister: []
                });
                session.performMessageTransaction(request, function (err, response) {
                    should.exist(response);
                    err.message.should.match(/BadNothingToDo/);
                    inner_done();
                });

            }, done);
            perform_operation_on_client_session(client, endpointUrl, function (session, inner_done) {

                const request = new UnregisterNodesRequest({
                    nodesToUnregister: [
                        "ns=0;i=1"
                    ]
                });
                session.performMessageTransaction(request, function (err, response) {
                    should.exist(response);
                    should.not.exist(err);
                    inner_done();
                });

            }, done);

            perform_operation_on_client_session(client, endpointUrl, function (session, inner_done) {

                const nodesToRegister = ["ns=2;s=Static_Scalar_Double"];
                let registeredNodeIds = [];
                let dataValue1, dataValue2;
                async.series([
                    function register_some_node(callback) {

                        session.registerNodes(nodesToRegister, function (err, _registeredNodeIds) {
                            if (err) {
                                return callback(err);
                            registeredNodeIds = _registeredNodeIds;
                            callback();
                        });
                    },
                    function (callback) {

                        const nodeToWrite = {
                            nodeId: registeredNodeIds[0],
                            attributeId: AttributeIds.Value,
                            value: {value: {dataType: DataType.Double, value: 1000}}
                        };
                        session.write(nodeToWrite, function (err, statusCode) {
                            statusCode.should.eql(StatusCodes.Good);
                            callback(err);
                        });
                    },
                    function (callback) {
                        const nodeToRead = {nodeId: nodesToRegister[0], attributeId: 13};
                        session.read(nodeToRead, function (err, dataValue) {
                            if (err) {
                                return callback(err);
                            }
                            dataValue.statusCode.should.eql(StatusCodes.Good);
                            dataValue1 = dataValue;
                            callback();
                        });
                    },
                    function (callback) {
                        const nodeToRead = {nodeId: registeredNodeIds[0], attributeId: 13};
                        session.read(nodeToRead, function (err, dataValue) {
                            if (err) {
                                return callback(err);
                            }
                            dataValue.statusCode.should.eql(StatusCodes.Good);
                            dataValue2 = dataValue;
                            callback();
                        });
                    },
                    function (callback) {
                        registeredNodeIds[0].toString().should.not.eql(nodesToRegister[0].toString());
                        dataValue1.statusCode.toString().should.eql(dataValue2.statusCode.toString());

                        //xx console.log(dataValue1.toString());
                        //xx console.log(dataValue2.toString());

                        dataValue1.value.toString().should.eql(dataValue2.value.toString());
                        callback();
                    }
                ], inner_done);

            }, done);

                //  console.log(result.toString());

                result.removeResults?.length.should.eql(0);
                result.addResults?.length.should.eql(0);
                result.responseHeader.serviceResult.should.eql(StatusCodes.BadNothingToDo);
            should.not.exist(_err, "not expecting any exception");
                last_response.responseHeader.serviceResult.should.eql(StatusCodes.BadSessionNotActivated);
const path = require("path");
const fs = require("fs");
const { 
  OPCUAClient, 
  StatusCodes, 
  PublishRequest,
  CreateSubscriptionRequest,
  CloseSessionRequest,
  ReadRequest,
  TimestampsToReturn, 
  MessageSecurityMode,
  SecurityPolicy,
  UserNameIdentityToken
} = require("node-opcua");
  readCertificate,
  readCertificateRevocationList
} = require("node-opcua-crypto");
  const publishRequest = new PublishRequest({});
  session.performMessageTransaction(publishRequest, function(err, response) {
    callback(err, response);
  });
  const publishingInterval = 1000;
  const createSubscriptionRequest = new CreateSubscriptionRequest({
    requestedPublishingInterval: publishingInterval,
    requestedLifetimeCount: 60,
    requestedMaxKeepAliveCount: 10,
    maxNotificationsPerPublish: 10,
    publishingEnabled: true,
    priority: 6
  });

  session.performMessageTransaction(createSubscriptionRequest, function(err/*, response*/) {
    callback(err);
  });
}

const describe = require("node-opcua-leak-detector").describeWithLeakDetector;

module.exports = function(test) {

  describe("testing session  transfer to different channel", function() {

    it("RQC1 - It should be possible to close a session that has not be activated yet", function(done) {
      let client1;
      let session1;
      async.series([

        function(callback) {
          client1 = OPCUAClient.create({});
          client1.connect(test.endpointUrl, callback);
        },

        // create a session using client1, without activating it
        function(callback) {
          test.server.engine.currentSessionCount.should.eql(0);
          client1._createSession(function(err, session) {
            if (err) {
              return callback(err);
            }
            session1 = session;
            callback();
          });
        },

        function(callback) {

          // Question: ? Should a unactivated session be accounted for
          //             in the currentSessionCount ?
          test.server.engine.currentSessionCount.should.eql(1);

          // however client shall not record session yet
          client1._sessions.length.should.eql(0);

          // in fact, let make sure that close Session is not harmfull
          client1.closeSession(session1, /* deleteSubscriptions =*/true, function(err) {
            client1._sessions.length.should.eql(0);
            if (err) {
              // if treated as a Failure , close session expected to return BadSessionNotActivated
              err.message.match(/BadSessionNotActivated/);
            }
            callback();
          });

        },
        function(callback) {
          test.server.engine.currentSessionCount.should.eql(0);
          callback();
        },
        function(callback) {
          client1.disconnect(callback);
        }
      ], done);

    it("RQB1 - calling CreateSession and CloseSession &  CloseSession again should return BadSessionIdInvalid", function(done) {
      let client1;
      let session1;
      async.series([

        function(callback) {
          client1 = OPCUAClient.create();
          client1.connect(test.endpointUrl, callback);
        },
        // create a session using client1
        function(callback) {
          client1._createSession(function(err, session) {
            if (err) {
              return callback(err);
            }
            session1 = session;
            callback();
          });
        },

        function(callback) {
          session1.close(function() {
            callback();
          });
        },
        function(callback) {
          session1.close(function(err) {
            // now session close do not return error if session in invalid
            // err.message.should.match(/SessionIdInvalid/);
            should.not.exist(err);
            callback();
          });
        },
        //
        function(callback) {
          test.server.engine.currentSessionCount.should.eql(0);
          client1.disconnect(callback);
        }

      ], done);

    });

    it("RQB2 - calling CloseSession without calling CreateSession first", function(done) {
      let client1;

      async.series([

        function(callback) {
          client1 = OPCUAClient.create({});
          client1.connect(test.endpointUrl, callback);
        },
        function(callback) {
          const request = new CloseSessionRequest({
            deleteSubscriptions: true
          });
          client1.performMessageTransaction(request, function(err, response) {
            should.not.exist(err);
            //err.message.should.match(/BadSessionIdInvalid/);
            response.responseHeader.serviceResult.should.eql(StatusCodes.BadSessionIdInvalid);
            callback();
          });
        },
        function(callback) {
          client1.disconnect(callback);
        },
        function(callback) {
          test.server.engine.currentSessionCount.should.eql(0);
          callback();
        }
      ], done);

    it("RQB3 - calling CreateSession,  CloseSession  and CloseSession again", function(done) {
      let client1;
      let session1;
      async.series([

        function(callback) {
          client1 = OPCUAClient.create();
          client1.connect(test.endpointUrl, callback);
        },
        // create a session using client1
        function(callback) {
          client1.createSession(function(err, session) {
            if (err) {
              return callback(err);
            }
            session1 = session;
            callback();
          });
        },

        // first call to close session should be OK
        function(callback) {
          client1.closeSession(session1, /* deleteSubscriptions =*/true, function(err) {
            callback(err);
          });
        },

        // second call to close session should raise an error
        function(callback) {
          const request = new CloseSessionRequest({
            deleteSubscriptions: true
          });
          client1.performMessageTransaction(request, function(err, response) {
            should.not.exist(err);
            //err.message.should.match(/BadSessionIdInvalid/);
            response.responseHeader.serviceResult.should.eql(StatusCodes.BadSessionIdInvalid);
            callback();
          });
        },

        function(callback) {
          client1.disconnect(callback);
        },
        function(callback) {
          test.server.engine.currentSessionCount.should.eql(0);
          callback();
        }
      ], function final(err) {
        client1.disconnect(function() {
          done(err);
      });
    });

    it("RQ0 - call ActiveSession on a session that has been transferred to a different channel", function(done) {

      // this test verifies that the following requirement can be met
      // OpcUA 1.02 part 3 $5.5 Secure Channel Set page 20
      // Once a  Client  has established a  Session  it may wish to access the  Session  from a different
      // SecureChannel. The Client can do this by validating the new  SecureChannel  with the
      // ActivateSession  Service  described in 5.6.3.
      let client1, client2;
      let session1;
      async.series([

        // create a first channel (client1)
        function(callback) {
          client1 = OPCUAClient.create();
          client1.connect(test.endpointUrl, callback);
        },
        // create a session using client1
        function(callback) {
          client1._createSession(function(err, session) {
            if (err) {
              return callback(err);
            }
            session1 = session;
            callback();
          });
        },
        // activate the session as expected on same channel used to create it
        function(callback) {
          client1._activateSession(session1, function(err) {
            callback(err);
          });
        },
        // let verify that it is now possible to send a request on client1's session
        function(callback) {
          // coerce nodeIds
          const request = new ReadRequest({
            nodesToRead: [{ nodeId: "i=2255", attributeId: 13 }],
            maxAge: 0,
            timestampsToReturn: TimestampsToReturn.Both
          });
          request.requestHeader.authenticationToken = session1.authenticationToken;
          client1.performMessageTransaction(request, function(err, response) {
            should.not.exist(err);
            response.responseHeader.serviceResult.should.eql(StatusCodes.Good);
            callback();
          });
        },

        // create a second channel (client2)
        function(callback) {
          client2 = OPCUAClient.create();
          client2.connect(test.endpointUrl, callback);
        },

        // reactivate session on second channel
        function(callback) {
          client2.reactivateSession(session1, function(err) {
            callback(err);
          });
        },

        // now that session has been assigned to client 1,
        // server shall refuse any requests on channel1
        function(callback) {
          // coerce nodeIds
          const request = new ReadRequest({
            nodesToRead: [{ nodeId: "i=2255", attributeId: 13 }],
            maxAge: 0,
            timestampsToReturn: TimestampsToReturn.Both
          });
          request.requestHeader.authenticationToken = session1.authenticationToken;
          client1.performMessageTransaction(request, function(err, response) {
            if (!err) {
              response.responseHeader.serviceResult.should.eql(StatusCodes.BadSecureChannelIdInvalid);
            }
            callback(err);
          });
        },
        // but server shall access request on new channel
        function(callback) {
          // coerce nodeIds
          const request = new ReadRequest({
            nodesToRead: [{ nodeId: "i=2255", attributeId: 13 }],
            maxAge: 0,
            timestampsToReturn: TimestampsToReturn.Both
          });
          request.requestHeader.authenticationToken = session1.authenticationToken;
          client2.performMessageTransaction(request, function(err, response) {
            if (!err) {
              response.responseHeader.serviceResult.should.eql(StatusCodes.Good);
            }
            callback(err);
          });
        },


        // terminate
        function(callback) {
          client2.disconnect(callback);
        },
        function(callback) {
          client1.disconnect(callback);
        },
        function(callback) {
          test.server.engine.currentSessionCount.should.eql(0);
          callback();
        }

      ], done);
    });

    // OpcUA 1.02 part 3 $5.6.3.1 ActiveSession Set page 29
    // When the ActivateSession  Service  is called f or the first time then the Server shall reject the request
    // if the  SecureChannel  is not same as the one associated with the CreateSession  request.
    it("RQ1 - should reject if the channel used to activate the session for the first time is not the same as the channel used to create the session", function(done) {

      let client1, client2;
      let session1;

      let initialChannelCount = 0;
      async.series([

        // create a first channel (client1)
        function(callback) {
          initialChannelCount = test.server.getChannels().length;
          test.server.getChannels().length.should.equal(initialChannelCount);
          client1 = OPCUAClient.create();
          client1.connect(test.endpointUrl, callback);
        },

        // create a session using client1
        //   ( without activating it)
        function(callback) {
          client1._createSession(function(err, session) {
            if (err) {
              return callback(err);
            }
            session1 = session;
            test.server.getChannels().length.should.equal(initialChannelCount + 1);
            callback();
          });
        },
        // create a second channel (client2)
        function(callback) {
          client2 = OPCUAClient.create();
          client2.connect(test.endpointUrl, callback);
        },

        // activate the session created with client1 using client2 !!
        // this should be detected by server and server shall return an error
        function(callback) {
          test.server.getChannels().length.should.equal(initialChannelCount + 2);
          //xx console.log(" ID1 =", client1._secureChannel.channelId);
          //xx console.log(" ID2 =", client2._secureChannel.channelId);

          client2.reactivateSession(session1, function(err) {

            if (!err) {
              callback(new Error("_activateSession shall return an error "));
            }
            err.message.should.match(/BadSessionNotActivated/);
            callback();
          });
        },

        // terminate
        function(callback) {
          client2.disconnect(callback);
        },
        // activate the session as expected on same channel used to create it
        // so we can close it properly
        function(callback) {
          client1._activateSession(session1, function(err) {
            should.not.exist(err);
            session1.close(callback);
          });
        },
        function(callback) {
          client1.disconnect(callback);
        },
        function(callback) {
          test.server.engine.currentSessionCount.should.eql(0);
          callback();
        }

      ], done);
    });
    function m(file) {
      const p = path.join(certificateFolder, file);
      if (!fs.existsSync(p)) {
        console.error(" cannot find ", p);
      }
      return p;
    }

    // OpcUA 1.02 part 3 $5.6.3.1 ActiveSession Set page 29
    // Subsequent calls to  ActivateSession  may be associated with different  SecureChannels.  If this is the
    // case then  the  Server  shall verify that the  Certificate  the  Client  used to create the new
    // SecureChannel  is the same as the  Certificate  used to create the original  SecureChannel.
    it("RQ2 -server should raise an error if a existing session is reactivated from a channel that have different certificate than the original channel", function(done) {

      const serverCertificate = test.server.getCertificateChain();

      let client1, client2;
      let session1;
      async.series([

        // create a first channel (client1) with
        function(callback) {
          //xx console.log(" creating initial channel with some certificate");
          const certificateFile1 = m("client_cert_2048.pem");
          const privateKeyFile1 = m("client_key_2048.pem");
          console.log(certificateFile1);

          client1 = OPCUAClient.create({
            certificateFile: certificateFile1,
            privateKeyFile: privateKeyFile1,
            securityMode: MessageSecurityMode.Sign,
            securityPolicy: SecurityPolicy.Basic128Rsa15,
            serverCertificate: serverCertificate
          });

   
          const certificate = readCertificate(certificateFile1);

          async function doIt() {
            await test.server.serverCertificateManager.trustCertificate(certificate);
            const issuerCertificateFile = m("CA/public/cacert.pem");
            const issuerCertificateRevocationListFile = m("CA/crl/revocation_list.der");
            const issuerCertificate = readCertificate(issuerCertificateFile);
            const issuerCrl = await readCertificateRevocationList(issuerCertificateRevocationListFile);
            await test.server.serverCertificateManager.addIssuer(issuerCertificate);
            await test.server.serverCertificateManager.addRevocationList(issuerCrl);
            callback();
          }
          doIt();
        },
        function(callback) {
          client1.connect(test.endpointUrl, callback);
        },
        // create a session using client1
        function(callback) {
          //xx console.log(" create session");
          client1._createSession(function(err, session) {
            if (err) {
              return callback(err);
            }
            session1 = session;
            callback();
          });
        },
        // activate the session as expected on same channel used to create it
        function(callback) {
          //xx console.log(" activate session");
          client1._activateSession(session1, function(err) {
            callback(err);
          });
        },

        // create a second channel (client2)
        // with a different certificate ....
        function(callback) {

          // creating second channel with different credential
          console.log(" creating second channel with different certificate");
          const certificateFile2 = m("client_cert_3072.pem");
          const privateKeyFile2 = m("client_key_3072.pem");
          client2 = OPCUAClient.create({
            certificateFile: certificateFile2,
            privateKeyFile: privateKeyFile2,
            securityMode: MessageSecurityMode.Sign,
            securityPolicy: SecurityPolicy.Basic256,
            serverCertificate: serverCertificate
          });
          const certificate = readCertificate(certificateFile2);
          test.server.serverCertificateManager.trustCertificate(certificate, callback);

        },
        function(callback) {
          client2.connect(test.endpointUrl, callback);
        },
        function(callback) {
          // reactivate session on second channel
          // Reactivate should fail because certificate is not the same as the original one
          client2.reactivateSession(session1, function(err) {
            if (err) {
              err.message.should.match(/BadNoValidCertificates/);
              callback();
            } else {
              callback(new Error("expecting reactivateSession to fail"));
            }
          });
        },
        // terminate
        function(callback) {
          client2.disconnect(callback);
        },
        function(callback) {
          session1.close(callback);
        },
        function(callback) {
          client1.disconnect(callback);
        },

        function(callback) {
          test.server.engine.currentSessionCount.should.eql(0);
          callback();
        }
      ], done);
    });
    // In addition,the Server shall verify that the  Client  supplied a  UserIdentityToken  that is   identical to the token
    // currently associated with the  Session.
    it("RQ3 - server should raise an error if a session is reactivated with different user identity tokens", function(done) {
      let client1, client2;
      let session1;

      const user1 = {
        userName: "user1", password: "password1"
      };
      const user2 = new UserNameIdentityToken({
        userName: "user1", password: "password1"
      });
      //xx console.log(" user1 ", user1.toString());
      async.series([

        // given a established session with a subscription and some publish request

        function(callback) {
          client1 = OPCUAClient.create();
          client1.connect(test.endpointUrl, callback);
        },
        // create a session using client1
        function(callback) {

          client1.createSession(user1, function(err, session) {
            if (err) {
              return callback(err);
            session1 = session;
            callback();
          });
        },
        // when the session is transferred to a different channel
        // create a second channel (client2)
        function(callback) {
          client2 = OPCUAClient.create();
          client2.connect(test.endpointUrl, callback);
        },
        function(callback) {
          // reactivate session on second channel
          // alter session1.userIdentityInfo
          session1.userIdentityInfo = user2;
          session1.userIdentityInfo.userName.should.eql("user1");

          client2.reactivateSession(session1, function(err) {
            err.message.should.match(/BadIdentityChangeNotSupported/);
            _.contains(client1._sessions, session1).should.eql(true);// should have failed
            callback();
          });
        },
        // terminate
        function(callback) {
          client2.disconnect(callback);
        },
        function(callback) {
          client1.disconnect(callback);
        },
        function(callback) {
          test.server.engine.currentSessionCount.should.eql(0);
          callback();
      ], done);

    });
    // Once the Server accepts the new  SecureChannel  it shall reject requests sent via the old  SecureChannel.
    xit("RQ4 - server should reject request send via old channel when session has been transferred to new channel", function(done) {
      async.series([], done);
    });

    // unprocessed pending Requests such as PublishRequest shall be be denied by the server
    // Once the Server accepts the new  SecureChannel  it shall reject requests sent via the old  SecureChannel
    it("RQ5 - server should reject pending requests send to old channel when session has been transferred to new channel", function(done) {

      const sinon = require("sinon");

      const collectPublishResponse = sinon.spy();

      let client1, client2;
      let session1;
      async.series([

        // given a established session with a subscription and some publish request

        function(callback) {
          client1 = OPCUAClient.create();
          client1.connect(test.endpointUrl, callback);
        },
        // create a session using client1
        function(callback) {
          client1._createSession(function(err, session) {
            if (err) {
              return callback(err);
            }
            session1 = session;
            callback();
          });
        },
        // activate the session as expected on same channel used to create it
        function(callback) {
          client1._activateSession(session1, function(err) {
            callback(err);
          });
        },

        // creaet a subscription,
        function(callback) {
          createSubscription(session1, callback);
        },


        // when the session is transferred to a different channel
        // create a second channel (client2)
        function(callback) {
          client2 = OPCUAClient.create();
          client2.connect(test.endpointUrl, callback);
          collectPublishResponse.callCount.should.eql(0);
        },

        // provision 3 publish requests and wait for the first keep alive
        function(callback) {

          sendPublishRequest(session1, function(err) {
            should.not.exist(err);
            collectPublishResponse.callCount.should.eql(0);
            callback();
          });
          sendPublishRequest(session1, collectPublishResponse);
          sendPublishRequest(session1, collectPublishResponse);
        },


        function(callback) {
          // reactivate session on second channel
          client2.reactivateSession(session1, function(err) {
            callback(err);
          });
        },
        function(callback) {
          setTimeout(callback, 100);
        },

        function(callback) {

          collectPublishResponse.callCount.should.eql(2);
          collectPublishResponse.getCall(0).args[0].message.should.match(/BadSecureChannelClosed/);
          collectPublishResponse.getCall(1).args[0].message.should.match(/BadSecureChannelClosed/);
          callback();
        },


        // terminate
        function(callback) {
          client2.disconnect(callback);
        },
        function(callback) {
          client1.disconnect(callback);
        },
        function(callback) {
          test.server.engine.currentSessionCount.should.eql(0);
          callback();
        }
      ], done);

  });

        ((err as any).request = request), (response = undefined);
            return null; // 
import { Message, Response, ServerSecureChannelLayer } from "node-opcua-secure-channel";
            const response1 = new RegisterServerXResponse({
    BrowseResultOptions
    const response = new ResponseClass({
                assert(response1 instanceof ResponseClass);
                session.publishEngine._on_PublishRequest(request, (request1: PublishRequest, response: PublishResponse) => {
                const response = new SetTriggeringResponse({
                    responseHeader: { serviceResult: statusCode },

                    addResults,
                    removeResults,
                    addDiagnosticInfos: null,
                    removeDiagnosticInfos: null
                });
                sendResponse(response);
                let response;

                    response = new RegisterNodesResponse({ responseHeader: { serviceResult: StatusCodes.BadNothingToDo } });
                    return sendResponse(response);
                response = new RegisterNodesResponse({
                let response;

                    response = new UnregisterNodesResponse({ responseHeader: { serviceResult: StatusCodes.BadNothingToDo } });
                    return sendResponse(response);
                response = new UnregisterNodesResponse({});
import { PublishRequest, PublishResponse, SubscriptionAcknowledgement } from "node-opcua-types";
    callback: (request: PublishRequest, response: PublishResponse) => void;
        
            doDebug && debugLog(
                "ServerSidePublishEngine#on_close_subscription storing subscription",
                subscription.id,
                " to _closed_subscriptions because it has pending notification"
            );
        callback?: (request1: PublishRequest, response: PublishResponse) => void
                                " " + 
                                " " + s.messageSent + 
                doDebug && debugLog(chalk.bgWhite.red("feeding most late subscription subscriptionId  = "), starving_subscription.id);
        const publishResponse = new PublishResponse({
        this._send_response_for_request(publishData, publishResponse);
        this._send_response_for_request(publishData, response);

    public _send_response_for_request(publishData: PublishData, response: PublishResponse): void {
        response.responseHeader.requestHandle = publishData.request.requestHeader.requestHandle;
        publishData.callback(publishData.request, response);
            publishSpy.getCall(1).args[1].subscriptionId.should.eql(0xffffffff);
            publishSpy.getCall(1).args[1].notificationMessage.sequenceNumber.should.eql(0);
            publishSpy.getCall(1).args[1].notificationMessage.notificationData.length.should.eql(0);
            publishSpy.getCall(2).args[1].subscriptionId.should.eql(0xffffffff);
            publishSpy.getCall(2).args[1].notificationMessage.sequenceNumber.should.eql(0);
            publishSpy.getCall(2).args[1].notificationMessage.notificationData.length.should.eql(0);
            publishSpy.getCall(3).args[1].subscriptionId.should.eql(0xffffffff);
            publishSpy.getCall(3).args[1].notificationMessage.sequenceNumber.should.eql(0);
            publishSpy.getCall(3).args[1].notificationMessage.notificationData.length.should.eql(0);
    it("ZDZ-5 a server should return BadNoSubscription as a response for a publish Request if there is no subscription available for this session. ", () => {
        send_response_for_request_spy.getCall(0).args[1].schema.name.should.equal("PublishResponse");
        send_response_for_request_spy.getCall(1).args[1].schema.name.should.equal("PublishResponse");
        send_response_for_request_spy.getCall(1).args[1].results!.should.eql([]);

        send_response_for_request_spy.getCall(2).args[1].schema.name.should.equal("PublishResponse");
        send_response_for_request_spy.getCall(2).args[1].results!.should.eql([]);

            const runningTests = [...runningPages].map((i) => testFiles[i]);
            for (let file of runningTests) {
                const outputs = outputFor[file];
                if (outputs && outputs.length) {
                    console.log(chalk.green("log for", file));
                    console.log(outputs.join("\n"));
                }
            }

Fixed code:
    promoteOpaqueStructure
import {
    BrowseNextRequest,
    BrowseNextResponse,
    HistoryReadValueIdOptions,
    ServiceFault,
    WriteValueOptions
} from "node-opcua-types";
                warningLog("client is now attempting to recreate a session");

                if (response) {
                    // use soft error, no exceptions
                    return callback(null, response);
                } else {
                    return callback(err);
                }
    OPCUADiscoveryServer,
    ServiceFault
            function check_error_response(err: Error | null, response: any): void {
                should.exist(err);
                should.not.exist(response);
                (err as any).response.should.be.instanceOf(ServiceFault);
                (err as any).response.responseHeader.serviceResult.should.eql(StatusCodes.BadDiscoveryUrlMissing);
            await send_registered_server_request(discoveryServerEndpointUrl, request, check_error_response);
            function check_error_response(err: Error | null, response: any) {
                should.exist(err);
                should.not.exist(response);
                (err as any).response.should.be.instanceOf(ServiceFault);
                (err as any).response.responseHeader.serviceResult.should.eql(StatusCodes.BadInvalidArgument);
            await send_registered_server_request(discoveryServerEndpointUrl, request, check_error_response);
            function check_error_response(err: Error | null, response: any) {
                should.exist(err);
                should.not.exist(response);
                (err as any).response.should.be.instanceOf(ServiceFault);
                (err as any).response.responseHeader.serviceResult.should.eql(StatusCodes.BadServerNameMissing);
            await send_registered_server_request(discoveryServerEndpointUrl, request, check_error_response);
            OPCUAServer.registry.count().should.eql(0);
            1162;
                    g_session.performMessageTransaction(browseNextRequest, function(err, _response) {
                        err.response.responseHeader.serviceResult.should.equal(StatusCodes.BadNothingToDo);
    installSessionLogging,
    ServiceFault
                                        should.not.exist(response);
                                        err.response.should.be.instanceOf(ServiceFault);
                                        should.not.exist(response);
                                        should.exist(err);
                        should.not.exist(createMonitoredItemsResponse);
                        err.response.responseHeader.serviceResult.should.eql(StatusCodes.BadNothingToDo);
                        err.response.responseHeader.serviceResult.should.eql(StatusCodes.BadNothingToDo);
                        should.not.exist(modifyMonitoredItemsResponse);
                        err.response.responseHeader.serviceResult.should.eql(StatusCodes.BadNothingToDo);
                        should.not.exist(deleteMonitoredItemsResponse);
                            should.not.exist(err);
                            tracelog("inner", err ? err.message : "");
                    tracelog("done", err ? err.message : "");
                        should.not.exist(response);
                        err.response.should.be.instanceof(ServiceFault);
                        err.response.responseHeader.serviceResult.should.eql(StatusCodes.BadMessageNotAvailable);
                        should.not.exist(response);
                        err.response.should.be.instanceof(ServiceFault);
                        err.response.responseHeader.serviceResult.should.eql(StatusCodes.BadSubscriptionIdInvalid);
                        should.not.exist(response1);
                        should.not.exist(response2);
                        should.not.exist(response3);

                        const err1 = spy_publish_session2.getCall(1).args[0];
                        const err2 = spy_publish_session2.getCall(2).args[0];
                        const err3 = spy_publish_session2.getCall(3).args[0];
                        
                        err1.response.responseHeader.serviceResult.should.eql(StatusCodes.BadNoSubscription);
                        err2.response.responseHeader.serviceResult.should.eql(StatusCodes.BadNoSubscription);
                        err3.response.responseHeader.serviceResult.should.eql(StatusCodes.BadNoSubscription);

        /** @type {ClientSession} */
                    const response = err.response;
const { OPCUAClient, StatusCodes, UnregisterNodesRequest, RegisterNodesRequest, DataType, AttributeIds, ServiceFault } = require("node-opcua");
            perform_operation_on_client_session(
                client,
                endpointUrl,
                function (session, inner_done) {
                    const request = new RegisterNodesRequest({
                        nodesToRegister: []
                    });
                    session.performMessageTransaction(request, function (err /*, response*/) {
                        err.message.should.match(/BadNothingToDo/);
                        inner_done();
                    });
                },
                done
            );
            perform_operation_on_client_session(
                client,
                endpointUrl,
                function (session, inner_done) {
                    const request = new RegisterNodesRequest({
                        nodesToRegister: ["ns=0;i=1"]
                    });
                    session.performMessageTransaction(request, function (err, response) {
                        should.not.exist(err);
                        response.registeredNodeIds.length.should.eql(1);
                        inner_done();
                    });
                },
                done
            );
            perform_operation_on_client_session(
                client,
                endpointUrl,
                function (session, inner_done) {
                    const request = new UnregisterNodesRequest({
                        nodesToUnregister: []
                    });
                    session.performMessageTransaction(request, function (err, response) {
                        should.not.exist(response);
                        should.exist(err);
                        err.response.should.be.instanceOf(ServiceFault);
                        err.message.should.match(/BadNothingToDo/);
                        err.response.responseHeader.serviceResult.should.eql(StatusCodes.BadNothingToDo);
                        inner_done();
                    });
                },
                done
            );
            perform_operation_on_client_session(
                client,
                endpointUrl,
                function (session, inner_done) {
                    const request = new UnregisterNodesRequest({
                        nodesToUnregister: ["ns=0;i=1"]
                    });
                    session.performMessageTransaction(request, function (err, response) {
                        should.exist(response);
                        should.not.exist(err);
                        inner_done();
                    });
                },
                done
            );
            perform_operation_on_client_session(
                client,
                endpointUrl,
                function (session, inner_done) {
                    const nodesToRegister = ["ns=2;s=Static_Scalar_Double"];
                    let registeredNodeIds = [];
                    let dataValue1, dataValue2;
                    async.series(
                        [
                            function register_some_node(callback) {
                                session.registerNodes(nodesToRegister, function (err, _registeredNodeIds) {
                                    if (err) {
                                        return callback(err);
                                    }
                                    registeredNodeIds = _registeredNodeIds;
                                    callback();
                                });
                            },
                            function (callback) {
                                const nodeToWrite = {
                                    nodeId: registeredNodeIds[0],
                                    attributeId: AttributeIds.Value,
                                    value: { value: { dataType: DataType.Double, value: 1000 } }
                                };
                                session.write(nodeToWrite, function (err, statusCode) {
                                    statusCode.should.eql(StatusCodes.Good);
                                    callback(err);
                                });
                            },
                            function (callback) {
                                const nodeToRead = { nodeId: nodesToRegister[0], attributeId: 13 };
                                session.read(nodeToRead, function (err, dataValue) {
                                    if (err) {
                                        return callback(err);
                                    }
                                    dataValue.statusCode.should.eql(StatusCodes.Good);
                                    dataValue1 = dataValue;
                                    callback();
                                });
                            },
                            function (callback) {
                                const nodeToRead = { nodeId: registeredNodeIds[0], attributeId: 13 };
                                session.read(nodeToRead, function (err, dataValue) {
                                    if (err) {
                                        return callback(err);
                                    }
                                    dataValue.statusCode.should.eql(StatusCodes.Good);
                                    dataValue2 = dataValue;
                                    callback();
                                });
                            },
                            function (callback) {
                                registeredNodeIds[0].toString().should.not.eql(nodesToRegister[0].toString());
                                dataValue1.statusCode.toString().should.eql(dataValue2.statusCode.toString());

                                //xx console.log(dataValue1.toString());
                                //xx console.log(dataValue2.toString());

                                dataValue1.value.toString().should.eql(dataValue2.value.toString());
                                callback();
                        ],
                        inner_done
                    );
                },
                done
            );
/* eslint-disable max-statements */
    ServiceFault,
                console.log(result.toString());
            should.exist(_err, "expecting a ServiceFault exception");
            const response = (_err as any).response as ServiceFault;
            response.should.be.instanceOf(ServiceFault);
            response.responseHeader.serviceResult.should.eql(StatusCodes.BadNothingToDo);
            let last_response_err;
                        last_response_err = err;
                last_response_err.response.responseHeader.serviceResult.should.eql(StatusCodes.BadSessionNotActivated);
const path = require("path");
const fs = require("fs");
    OPCUAClient,
    StatusCodes,
    PublishRequest,
    CreateSubscriptionRequest,
    CloseSessionRequest,
    ReadRequest,
    TimestampsToReturn,
    MessageSecurityMode,
    SecurityPolicy,
    UserNameIdentityToken,
    ServiceFault,
    ReadResponse
} = require("node-opcua");
const { readCertificate, readCertificateRevocationList } = require("node-opcua-crypto");
    const publishRequest = new PublishRequest({});
    session.performMessageTransaction(publishRequest, function (err, response) {
        callback(err, response);
    });
    const publishingInterval = 1000;
    const createSubscriptionRequest = new CreateSubscriptionRequest({
        requestedPublishingInterval: publishingInterval,
        requestedLifetimeCount: 60,
        requestedMaxKeepAliveCount: 10,
        maxNotificationsPerPublish: 10,
        publishingEnabled: true,
        priority: 6
    session.performMessageTransaction(createSubscriptionRequest, function (err /*, response*/) {
        callback(err);
}
const describe = require("node-opcua-leak-detector").describeWithLeakDetector;
module.exports = function (test) {
    describe("testing session  transfer to different channel", function () {
        it("RQC1 - It should be possible to close a session that has not be activated yet", function (done) {
            let client1;
            let session1;
            async.series(
                [
                    function (callback) {
                        client1 = OPCUAClient.create({});
                        client1.connect(test.endpointUrl, callback);
                    },

                    // create a session using client1, without activating it
                    function (callback) {
                        test.server.engine.currentSessionCount.should.eql(0);
                        client1._createSession(function (err, session) {
                            if (err) {
                                return callback(err);
                            }
                            session1 = session;
                            callback();
                        });
                    },

                    function (callback) {
                        // Question: ? Should a unactivated session be accounted for
                        //             in the currentSessionCount ?
                        test.server.engine.currentSessionCount.should.eql(1);

                        // however client shall not record session yet
                        client1._sessions.length.should.eql(0);

                        // in fact, let make sure that close Session is not harmfull
                        client1.closeSession(session1, /* deleteSubscriptions =*/ true, function (err) {
                            client1._sessions.length.should.eql(0);
                            if (err) {
                                // if treated as a Failure , close session expected to return BadSessionNotActivated
                                err.message.match(/BadSessionNotActivated/);
                            }
                            callback();
                        });
                    },
                    function (callback) {
                        test.server.engine.currentSessionCount.should.eql(0);
                        callback();
                    },
                    function (callback) {
                        client1.disconnect(callback);
                    }
                ],
                done
            );
        it("RQB1 - calling CreateSession and CloseSession &  CloseSession again should return BadSessionIdInvalid", function (done) {
            let client1;
            let session1;
            async.series(
                [
                    function (callback) {
                        client1 = OPCUAClient.create();
                        client1.connect(test.endpointUrl, callback);
                    },
                    // create a session using client1
                    function (callback) {
                        client1._createSession(function (err, session) {
                            if (err) {
                                return callback(err);
                            }
                            session1 = session;
                            callback();
                        });
                    },

                    function (callback) {
                        session1.close(function () {
                            callback();
                        });
                    },
                    function (callback) {
                        session1.close(function (err) {
                            // now session close do not return error if session in invalid
                            // err.message.should.match(/SessionIdInvalid/);
                            should.not.exist(err);
                            callback();
                        });
                    },
                    //
                    function (callback) {
                        test.server.engine.currentSessionCount.should.eql(0);
                        client1.disconnect(callback);
                    }
                ],
                done
            );
        });
        it("RQB2 - calling CloseSession without calling CreateSession first", function (done) {
            let client1;

            async.series(
                [
                    function (callback) {
                        client1 = OPCUAClient.create({});
                        client1.connect(test.endpointUrl, callback);
                    },
                    function (callback) {
                        const request = new CloseSessionRequest({
                            deleteSubscriptions: true
                        });
                        client1.performMessageTransaction(request, function (err, response) {
                            should.exist(err);
                            should.not.exist(response);
                            err.response.should.be.instanceOf(ServiceFault);
                            err.message.should.match(/BadSessionIdInvalid/);
                            err.response.responseHeader.serviceResult.should.eql(StatusCodes.BadSessionIdInvalid);
                            callback();
                        });
                    },
                    function (callback) {
                        client1.disconnect(callback);
                    },
                    function (callback) {
                        test.server.engine.currentSessionCount.should.eql(0);
                        callback();
                    }
                ],
                done
            );
        });
        it("RQB3 - calling CreateSession,  CloseSession  and CloseSession again", function (done) {
            let client1;
            let session1;
            async.series(
                [
                    function (callback) {
                        client1 = OPCUAClient.create();
                        client1.connect(test.endpointUrl, callback);
                    },
                    // create a session using client1
                    function (callback) {
                        client1.createSession(function (err, session) {
                            if (err) {
                                return callback(err);
                            }
                            session1 = session;
                            callback();
                        });
                    },

                    // first call to close session should be OK
                    function (callback) {
                        client1.closeSession(session1, /* deleteSubscriptions =*/ true, function (err) {
                            callback(err);
                        });
                    },

                    // second call to close session should raise an error
                    function (callback) {
                        const request = new CloseSessionRequest({
                            deleteSubscriptions: true
                        });
                        client1.performMessageTransaction(request, function (err, response) {
                            should.exist(err);
                            should.not.exist(response);
                            err.message.should.match(/BadSessionIdInvalid/);
                            err.response.should.be.instanceOf(ServiceFault);
                            err.response.responseHeader.serviceResult.should.eql(StatusCodes.BadSessionIdInvalid);
                            callback();
                        });
                    },

                    function (callback) {
                        client1.disconnect(callback);
                    },
                    function (callback) {
                        test.server.engine.currentSessionCount.should.eql(0);
                        callback();
                    }
                ],
                function final(err) {
                    client1.disconnect(function () {
                        done(err);
                    });
                }
            );
        });
        it("RQ0 - call ActiveSession on a session that has been transferred to a different channel", function (done) {
            // this test verifies that the following requirement can be met
            // OpcUA 1.02 part 3 $5.5 Secure Channel Set page 20
            // Once a  Client  has established a  Session  it may wish to access the  Session  from a different
            // SecureChannel. The Client can do this by validating the new  SecureChannel  with the
            // ActivateSession  Service  described in 5.6.3.
            let client1, client2;
            let session1;
            async.series(
                [
                    // create a first channel (client1)
                    function (callback) {
                        client1 = OPCUAClient.create();
                        client1.connect(test.endpointUrl, callback);
                    },
                    // create a session using client1
                    function (callback) {
                        client1._createSession(function (err, session) {
                            if (err) {
                                return callback(err);
                            }
                            session1 = session;
                            callback();
                        });
                    },
                    // activate the session as expected on same channel used to create it
                    function (callback) {
                        client1._activateSession(session1, function (err) {
                            callback(err);
                        });
                    },
                    // let verify that it is now possible to send a request on client1's session
                    function (callback) {
                        // coerce nodeIds
                        const request = new ReadRequest({
                            nodesToRead: [{ nodeId: "i=2255", attributeId: 13 }],
                            maxAge: 0,
                            timestampsToReturn: TimestampsToReturn.Both
                        });
                        request.requestHeader.authenticationToken = session1.authenticationToken;
                        client1.performMessageTransaction(request, function (err, response) {
                            should.not.exist(err);
                            response.responseHeader.serviceResult.should.eql(StatusCodes.Good);
                            callback();
                        });
                    },

                    // create a second channel (client2)
                    function (callback) {
                        client2 = OPCUAClient.create();
                        client2.connect(test.endpointUrl, callback);
                    },

                    // reactivate session on second channel
                    function (callback) {
                        client2.reactivateSession(session1, function (err) {
                            callback(err);
                        });
                    },

                    // now that session has been assigned to client 1,
                    // server shall refuse any requests on channel1
                    function (callback) {
                        // coerce nodeIds
                        const request = new ReadRequest({
                            nodesToRead: [{ nodeId: "i=2255", attributeId: 13 }],
                            maxAge: 0,
                            timestampsToReturn: TimestampsToReturn.Both
                        });
                        request.requestHeader.authenticationToken = session1.authenticationToken;
                        client1.performMessageTransaction(request, function (err, response) {
                            should.not.exist(err);
                            should.exist(response);
                            response.should.be.instanceOf(ReadResponse);
                            response.responseHeader.serviceResult.should.eql(StatusCodes.BadSecureChannelIdInvalid);
                            callback();
                        });
                    },
                    // but server shall access request on new channel
                    function (callback) {
                        // coerce nodeIds
                        const request = new ReadRequest({
                            nodesToRead: [{ nodeId: "i=2255", attributeId: 13 }],
                            maxAge: 0,
                            timestampsToReturn: TimestampsToReturn.Both
                        });
                        request.requestHeader.authenticationToken = session1.authenticationToken;
                        client2.performMessageTransaction(request, function (err, response) {
                            if (!err) {
                                response.responseHeader.serviceResult.should.eql(StatusCodes.Good);
                            }
                            callback(err);
                        });
                    },

                    // terminate
                    function (callback) {
                        client2.disconnect(callback);
                    },
                    function (callback) {
                        client1.disconnect(callback);
                    },
                    function (callback) {
                        test.server.engine.currentSessionCount.should.eql(0);
                        callback();
                    }
                ],
                done
            );
        });
        // OpcUA 1.02 part 3 $5.6.3.1 ActiveSession Set page 29
        // When the ActivateSession  Service  is called f or the first time then the Server shall reject the request
        // if the  SecureChannel  is not same as the one associated with the CreateSession  request.
        it("RQ1 - should reject if the channel used to activate the session for the first time is not the same as the channel used to create the session", function (done) {
            let client1, client2;
            let session1;

            let initialChannelCount = 0;
            async.series(
                [
                    // create a first channel (client1)
                    function (callback) {
                        initialChannelCount = test.server.getChannels().length;
                        test.server.getChannels().length.should.equal(initialChannelCount);
                        client1 = OPCUAClient.create();
                        client1.connect(test.endpointUrl, callback);
                    },

                    // create a session using client1
                    //   ( without activating it)
                    function (callback) {
                        client1._createSession(function (err, session) {
                            if (err) {
                                return callback(err);
                            }
                            session1 = session;
                            test.server.getChannels().length.should.equal(initialChannelCount + 1);
                            callback();
                        });
                    },
                    // create a second channel (client2)
                    function (callback) {
                        client2 = OPCUAClient.create();
                        client2.connect(test.endpointUrl, callback);
                    },

                    // activate the session created with client1 using client2 !!
                    // this should be detected by server and server shall return an error
                    function (callback) {
                        test.server.getChannels().length.should.equal(initialChannelCount + 2);
                        //xx console.log(" ID1 =", client1._secureChannel.channelId);
                        //xx console.log(" ID2 =", client2._secureChannel.channelId);

                        client2.reactivateSession(session1, function (err) {
                            if (!err) {
                                callback(new Error("_activateSession shall return an error "));
                            }
                            err.message.should.match(/BadSessionNotActivated/);
                            callback();
                        });
                    },

                    // terminate
                    function (callback) {
                        client2.disconnect(callback);
                    },
                    // activate the session as expected on same channel used to create it
                    // so we can close it properly
                    function (callback) {
                        client1._activateSession(session1, function (err) {
                            should.not.exist(err);
                            session1.close(callback);
                        });
                    },
                    function (callback) {
                        client1.disconnect(callback);
                    },
                    function (callback) {
                        test.server.engine.currentSessionCount.should.eql(0);
                        callback();
                    }
                ],
                done
            );
        });
        function m(file) {
            const p = path.join(certificateFolder, file);
            if (!fs.existsSync(p)) {
                console.error(" cannot find ", p);
            return p;
        // OpcUA 1.02 part 3 $5.6.3.1 ActiveSession Set page 29
        // Subsequent calls to  ActivateSession  may be associated with different  SecureChannels.  If this is the
        // case then  the  Server  shall verify that the  Certificate  the  Client  used to create the new
        // SecureChannel  is the same as the  Certificate  used to create the original  SecureChannel.
        it("RQ2 -server should raise an error if a existing session is reactivated from a channel that have different certificate than the original channel", function (done) {
            const serverCertificate = test.server.getCertificateChain();

            let client1, client2;
            let session1;
            async.series(
                [
                    // create a first channel (client1) with
                    function (callback) {
                        //xx console.log(" creating initial channel with some certificate");
                        const certificateFile1 = m("client_cert_2048.pem");
                        const privateKeyFile1 = m("client_key_2048.pem");
                        console.log(certificateFile1);

                        client1 = OPCUAClient.create({
                            certificateFile: certificateFile1,
                            privateKeyFile: privateKeyFile1,
                            securityMode: MessageSecurityMode.Sign,
                            securityPolicy: SecurityPolicy.Basic128Rsa15,
                            serverCertificate: serverCertificate
                        });

                        const certificate = readCertificate(certificateFile1);

                        async function doIt() {
                            await test.server.serverCertificateManager.trustCertificate(certificate);
                            const issuerCertificateFile = m("CA/public/cacert.pem");
                            const issuerCertificateRevocationListFile = m("CA/crl/revocation_list.der");
                            const issuerCertificate = readCertificate(issuerCertificateFile);
                            const issuerCrl = await readCertificateRevocationList(issuerCertificateRevocationListFile);
                            await test.server.serverCertificateManager.addIssuer(issuerCertificate);
                            await test.server.serverCertificateManager.addRevocationList(issuerCrl);
                            callback();
                        }
                        doIt();
                    },
                    function (callback) {
                        client1.connect(test.endpointUrl, callback);
                    },
                    // create a session using client1
                    function (callback) {
                        //xx console.log(" create session");
                        client1._createSession(function (err, session) {
                            if (err) {
                                return callback(err);
                            }
                            session1 = session;
                            callback();
                        });
                    },
                    // activate the session as expected on same channel used to create it
                    function (callback) {
                        //xx console.log(" activate session");
                        client1._activateSession(session1, function (err) {
                            callback(err);
                        });
                    },

                    // create a second channel (client2)
                    // with a different certificate ....
                    function (callback) {
                        // creating second channel with different credential
                        console.log(" creating second channel with different certificate");
                        const certificateFile2 = m("client_cert_3072.pem");
                        const privateKeyFile2 = m("client_key_3072.pem");
                        client2 = OPCUAClient.create({
                            certificateFile: certificateFile2,
                            privateKeyFile: privateKeyFile2,
                            securityMode: MessageSecurityMode.Sign,
                            securityPolicy: SecurityPolicy.Basic256,
                            serverCertificate: serverCertificate
                        });
                        const certificate = readCertificate(certificateFile2);
                        test.server.serverCertificateManager.trustCertificate(certificate, callback);
                    },
                    function (callback) {
                        client2.connect(test.endpointUrl, callback);
                    },
                    function (callback) {
                        // reactivate session on second channel
                        // Reactivate should fail because certificate is not the same as the original one
                        client2.reactivateSession(session1, function (err) {
                            if (err) {
                                err.message.should.match(/BadNoValidCertificates/);
                                callback();
                            } else {
                                callback(new Error("expecting reactivateSession to fail"));
                            }
                        });
                    },
                    // terminate
                    function (callback) {
                        client2.disconnect(callback);
                    },
                    function (callback) {
                        session1.close(callback);
                    },
                    function (callback) {
                        client1.disconnect(callback);
                    },

                    function (callback) {
                        test.server.engine.currentSessionCount.should.eql(0);
                        callback();
                    }
                ],
                done
            );
        });
        // In addition,the Server shall verify that the  Client  supplied a  UserIdentityToken  that is   identical to the token
        // currently associated with the  Session.
        it("RQ3 - server should raise an error if a session is reactivated with different user identity tokens", function (done) {
            let client1, client2;
            let session1;

            const user1 = {
                userName: "user1",
                password: "password1"
            };
            const user2 = new UserNameIdentityToken({
                userName: "user1",
                password: "password1"
            });
            //xx console.log(" user1 ", user1.toString());
            async.series(
                [
                    // given a established session with a subscription and some publish request

                    function (callback) {
                        client1 = OPCUAClient.create();
                        client1.connect(test.endpointUrl, callback);
                    },
                    // create a session using client1
                    function (callback) {
                        client1.createSession(user1, function (err, session) {
                            if (err) {
                                return callback(err);
                            }
                            session1 = session;
                            callback();
                        });
                    },
                    // when the session is transferred to a different channel
                    // create a second channel (client2)
                    function (callback) {
                        client2 = OPCUAClient.create();
                        client2.connect(test.endpointUrl, callback);
                    },
                    function (callback) {
                        // reactivate session on second channel
                        // alter session1.userIdentityInfo
                        session1.userIdentityInfo = user2;
                        session1.userIdentityInfo.userName.should.eql("user1");

                        client2.reactivateSession(session1, function (err) {
                            err.message.should.match(/BadIdentityChangeNotSupported/);
                            _.contains(client1._sessions, session1).should.eql(true); // should have failed
                            callback();
                        });
                    },
                    // terminate
                    function (callback) {
                        client2.disconnect(callback);
                    },
                    function (callback) {
                        client1.disconnect(callback);
                    },
                    function (callback) {
                        test.server.engine.currentSessionCount.should.eql(0);
                        callback();
                    }
                ],
                done
            );
        });
        // Once the Server accepts the new  SecureChannel  it shall reject requests sent via the old  SecureChannel.
        xit("RQ4 - server should reject request send via old channel when session has been transferred to new channel", function (done) {
            async.series([], done);
        });
        // unprocessed pending Requests such as PublishRequest shall be be denied by the server
        // Once the Server accepts the new  SecureChannel  it shall reject requests sent via the old  SecureChannel
        it("RQ5 - server should reject pending requests send to old channel when session has been transferred to new channel", function (done) {
            const sinon = require("sinon");

            const collectPublishResponse = sinon.spy();

            let client1, client2;
            let session1;
            async.series(
                [
                    // given a established session with a subscription and some publish request

                    function (callback) {
                        client1 = OPCUAClient.create();
                        client1.connect(test.endpointUrl, callback);
                    },
                    // create a session using client1
                    function (callback) {
                        client1._createSession(function (err, session) {
                            if (err) {
                                return callback(err);
                            }
                            session1 = session;
                            callback();
                        });
                    },
                    // activate the session as expected on same channel used to create it
                    function (callback) {
                        client1._activateSession(session1, function (err) {
                            callback(err);
                        });
                    },

                    // creaet a subscription,
                    function (callback) {
                        createSubscription(session1, callback);
                    },

                    // when the session is transferred to a different channel
                    // create a second channel (client2)
                    function (callback) {
                        client2 = OPCUAClient.create();
                        client2.connect(test.endpointUrl, callback);
                        collectPublishResponse.callCount.should.eql(0);
                    },

                    // provision 3 publish requests and wait for the first keep alive
                    function (callback) {
                        sendPublishRequest(session1, function (err) {
                            should.not.exist(err);
                            collectPublishResponse.callCount.should.eql(0);
                            callback();
                        });
                        sendPublishRequest(session1, collectPublishResponse);
                        sendPublishRequest(session1, collectPublishResponse);
                    },

                    function (callback) {
                        // reactivate session on second channel
                        client2.reactivateSession(session1, function (err) {
                            callback(err);
                        });
                    },
                    function (callback) {
                        setTimeout(callback, 100);
                    },

                    function (callback) {
                        collectPublishResponse.callCount.should.eql(2);
                        collectPublishResponse.getCall(0).args[0].message.should.match(/BadSecureChannelClosed/);
                        collectPublishResponse.getCall(1).args[0].message.should.match(/BadSecureChannelClosed/);
                        callback();
                    },

                    // terminate
                    function (callback) {
                        client2.disconnect(callback);
                    },
                    function (callback) {
                        client1.disconnect(callback);
                    },
                    function (callback) {
                        test.server.engine.currentSessionCount.should.eql(0);
                        callback();
                    }
                ],
                done
            );
        });
        (err as any).request = request;
        response = undefined;
            return null; //
import { Message, Response, ServerSecureChannelLayer, ServiceFault } from "node-opcua-secure-channel";
            const response1 = new ServiceFault({
    BrowseResultOptions,
    ServiceFault
    const response = new ServiceFault({
                assert(response1 instanceof ResponseClass || response1 instanceof ServiceFault);
                session.publishEngine._on_PublishRequest(request, (_request1, response) => {
                if (statusCode !== StatusCodes.Good) {
                    const response = new ServiceFault({ responseHeader: { serviceResult: statusCode } });
                    sendResponse(response);
                } else {
                    const response = new SetTriggeringResponse({
                        responseHeader: { serviceResult: statusCode },
                        addResults,
                        removeResults,
                        addDiagnosticInfos: null,
                        removeDiagnosticInfos: null
                    });
                    sendResponse(response);
                }
                    return sendError(StatusCodes.BadNothingToDo);
                const response = new RegisterNodesResponse({
                    return sendError(StatusCodes.BadNothingToDo);
                const response = new UnregisterNodesResponse({});
import { PublishRequest, PublishResponse, ServiceFault, SubscriptionAcknowledgement } from "node-opcua-types";
    callback: (request: PublishRequest, response: PublishResponse | ServiceFault) => void;

            doDebug &&
                debugLog(
                    "ServerSidePublishEngine#on_close_subscription storing subscription",
                    subscription.id,
                    " to _closed_subscriptions because it has pending notification"
                );
        callback?: (request1: PublishRequest, response: PublishResponse| ServiceFault) => void
                                " " +
                                " " +
                                s.messageSent +
                doDebug &&
                    debugLog(chalk.bgWhite.red("feeding most late subscription subscriptionId  = "), starving_subscription.id);
        const response = new ServiceFault({
        this._send_response_for_request(publishData, response);
        this._send_valid_response_for_request(publishData, response);
    public _send_response_for_request(publishData: PublishData, response: PublishResponse | ServiceFault): void {
        response.responseHeader.requestHandle = publishData.request.requestHeader.requestHandle;
        publishData.callback(publishData.request, response);
    }
    public _send_valid_response_for_request(publishData: PublishData, response: PublishResponse): void {
        this._send_response_for_request(publishData, response);
const { ServiceFault } = require("node-opcua-types");
            publishSpy.getCall(1).args[1].should.be.instanceOf(ServiceFault);
            // publishSpy.getCall(1).args[1].subscriptionId.should.eql(0xffffffff);
            // publishSpy.getCall(1).args[1].notificationMessage.sequenceNumber.should.eql(0);
            // publishSpy.getCall(1).args[1].notificationMessage.notificationData.length.should.eql(0);
            publishSpy.getCall(2).args[1].should.be.instanceOf(ServiceFault);
            // publishSpy.getCall(2).args[1].subscriptionId.should.eql(0xffffffff);
            // publishSpy.getCall(2).args[1].notificationMessage.sequenceNumber.should.eql(0);
            // publishSpy.getCall(2).args[1].notificationMessage.notificationData.length.should.eql(0);
            publishSpy.getCall(3).args[1].should.be.instanceOf(ServiceFault);
            // publishSpy.getCall(3).args[1].subscriptionId.should.eql(0xffffffff);
            // publishSpy.getCall(3).args[1].notificationMessage.sequenceNumber.should.eql(0);
            // publishSpy.getCall(3).args[1].notificationMessage.notificationData.length.should.eql(0);
    it("ZDZ-5 a server should return ServiceFault(BadNoSubscription) as a response for a publish Request if there is no subscription available for this session. ", () => {
        send_response_for_request_spy.getCall(0).args[1].schema.name.should.equal("ServiceFault");
        send_response_for_request_spy.getCall(1).args[1].schema.name.should.equal("ServiceFault");
       
        send_response_for_request_spy.getCall(2).args[1].schema.name.should.equal("ServiceFault");
    
                if (key.name === "i") {
                    dumpRunningTests();
                    dumpRunningTestsLogs();
                }
            dumpRunningTestsLogs();
function dumpRunningTestsLogs() {
    const runningTests = [...runningPages].map((i) => testFiles[i]);
    for (let file of runningTests) {
        const outputs = outputFor[file];
        if (outputs && outputs.length) {
            console.log(chalk.green("log for", file));
            console.log(outputs.join("\n"));
        }
    }
}